2012-06-01 4 views
7

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

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

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

Ниже я отправлю код, который я использую для создания 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; 

} 
+0

Что происходит с 'samplerate' если' -r' опция не выдаются? – thb

+0

Это не ваша проблема, но вы должны использовать memcpy, а не strcpy, поскольку это копирует конечный '\ 0'. –

+0

Ницца поймать @ Майкл. Я удивлен, что это еще не все испортило. :) – Kokopelli

ответ

7

Я думаю, что это ваш основной проблемой:

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

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

Но также, поскольку «время» увеличивается, умножение «частоты времени * 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]); 

} 
+0

Петля на 10 должна поддерживать до 10 синусов, все вместе. –

+0

О, я вижу. Я обновил свой ответ, включив в него цикл массива. И правильно закрепить его результаты, прежде чем обтекать поплавок обратно до короткого. – selbie

+0

Спасибо @selbie за ваше предложение. Я попробую и посмотрю, исцелит ли он мое недомогание. – Kokopelli