Вопрос по c, math, audio – Синтез файлов WAV с нуля - C

8

Недавно в моем классе CS 101 я увидел видеолекцию, которая вдохновила меня начать играть с форматом файлов WAV в C. Сегодня мой проект состоит в создании звуков с использованием простой математической функции синуса. Несмотря на пару препятствий, моя программа теперь может принимать несколько входных данных (частоты волн, амплитуды волн, частоту дискретизации и т. Д.) И создавать файл WAV, содержащий указанные высоты.

Однако при воспроизведении этих тонов на динамиках моего компьютера появляется странный ритмичный звук, который зависит от частоты дискретизации. При более высокой частоте дискретизации частота хлопающего звука увеличивается и превращается в раздражающий скулящий звук.

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

Ниже я опубликую код, который я использую для создания файла WAV. Любое понимание того, что может быть причиной этого явления, будет оценено. Это, наверное, просто глупая ошибка с моей стороны где-то. :)

#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <math.h>

struct WAVHeader {
    char ChunkID[4];
    uint32_t ChunkSize;
    char RIFFType[4];
};

struct FormatHeader {
    char ChunkID[4];
    uint32_t ChunkSize;
    uint16_t CompressionCode;
    uint16_t Channels;
    uint32_t SampleRate;
    uint32_t AvgBytesPerSec;
    uint16_t BlockAlign;
    uint16_t SigBitsPerSamp;
};

struct DataHeader {
    char ChunkID[4];
    uint32_t ChunkSize;

};


void main(int argc, char * argv[]) {

//Check for valid number of arguments or display help
if(argc < 8) {
    printf("Usage:\n./Tone -l [length] -s [frequency] [amplitude] -o [output-file] -r [sample-rate]\n");
    printf("-l length of tone to produce in seconds\n");    
    printf("-s Creates sine wave. Can be used multiple times. Frequency (Hz) and amplitude (0 - 32767) of each tone. \n");  
    printf("-o File to write to\n");
    printf("-r samples per second (kHz). Note: Must be double highest frequency in tone.\n");   
    return;
}

//Organize arguments
int length, sinf[10], sina[10], samplerate;
memset(sinf, 0, sizeof(int) * 10);
memset(sina, 0, sizeof(int) * 10);
char * output = NULL;
int i = 0;
int count;
for(count = 1; count < argc; count++){
    char first = *argv[count];
    int second = *(argv[count] + 1);    
    if (first == '-') {
        switch (second) {
            case 's':
                sinf[i] = atoi(argv[count+1]);
                sina[i] = atoi(argv[count+2]);
                i++;
                break;
            case 'l':
                length = atoi(argv[count+1]);
                break;
            case 'o':
                output = argv[count+1];
                break;
            case 'r':
                samplerate = atoi(argv[count+1]) * 1000;
                break;
        }
    }
}

//Allocate memory for wav file
size_t size = sizeof(struct WAVHeader) + sizeof(struct FormatHeader) + sizeof(struct DataHeader) + (length * samplerate * 2);
void * buffer = malloc(size);

//Fill buffer with headers
struct WAVHeader * WAV = (struct WAVHeader *)buffer;
struct FormatHeader * Format = (struct FormatHeader *)(WAV + 1);
struct DataHeader * Data = (struct DataHeader *)(Format + 1);

strcpy(WAV->ChunkID, "RIFF");
WAV->ChunkSize = (uint32_t)size - 8;
strcpy(WAV->RIFFType, "WAVE");

strcpy(Format->ChunkID, "fmt ");
Format->ChunkSize = 16;
Format->CompressionCode = 1;
Format->Channels = 1;
Format->SampleRate = (uint32_t)samplerate;
Format->SigBitsPerSamp = 16;
Format->BlockAlign = 2;
Format->AvgBytesPerSec = Format->BlockAlign * samplerate;

strcpy(Data->ChunkID, "data");
Data->ChunkSize = length * samplerate * 2;

//Generate Sound
printf("Generating sound...\n");
short * sound = (short *)(Data + 1);
short total;
float time;
float increment = 1.0/(float)samplerate;
for (time = 0; time < length; time += increment){
    total = 0;
    for (i = 0; i < 10; i++) {
        total += sina[i] * sin((float)sinf[i] * time * (2 * 3.1415926));
    }
    *(sound + (int)(time * samplerate)) = total;
    //printf("Time: %f Value: %hd\n", time, total);
}

//Write buffer to file
FILE * out = fopen(output, "w");
fwrite(buffer, size, 1, out);
printf("Wrote to %s\n", output);

return;

}
Что происходит сsamplerate если-r вариант не оформлен? thb
Кстати, мне также потребовались stdint.h и stdlib.h для компиляции (и gcc -lm для связи) Jeremy Leipzig
Хороший улов @Майкл. Я удивлен, что еще не все испортил. :) Kokopelli
Это не ваша проблема, но вы должны использовать memcpy, а не strcpy, так как он копирует конечные символы "\ 0". Michael Anderson
Ой. Причина, по которой он не испортился, заключается в том, что я пишу поверх него сразу после того, как совершил ошибку. Что ж. Если я не могу быть хорошим, я могу быть счастливчиком. : D Kokopelli

Ваш Ответ

1   ответ
7

*(sound + (int)(time * samplerate)) = total;

Я подозреваю, что (time * samplerate) не всегда увеличивается на целочисленных границах из-за ошибок округления с плавающей запятой. Следовательно, некоторые выборочные позиции пропускаются и / или перезаписываются из-за ошибок округления. Это всего лишь предположение.

Но также, как "время" увеличивается, умножение «время * частота * 2PI»; будет переполнен в пределах поплавка. Таким образом, вы должны нормализовать «время» такой, что она не увеличивается вечно.

В любом случае, я проверил, что этот измененный цикл работает (и звучит) просто отлично:

float TWOPI = 6.28318531f;
unsigned int sample_count = length * samplerate;

for (unsigned int i = 0; i < sample_count; i++)
{
    unsigned int j = i % samplerate; // normalize the sample position so that we don't blow up in the subsequent multiplication
    float f = 0.0f;
    int result;

    for (int x = 0; x < 10; x++)
    {
        f += sina[x] * sin((sinf[x] * j * TWOPI) / samplerate);
    }

    result = (long)f;

    //clamp to 16-bit
    if (result > 32767)
    {
        result = 32767;
    }
    else if (result < -32768)
    {
        result = -32768;
    }

    sound[i] = (short)result;

    //printf("%d\n", sound[i]);

}
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Kokopelli
Error: User Rate Limit Exceeded

Похожие вопросы