this is a crosspost from stackoverflow as I figured there may be more who can help me here...
I've been writing a basic C program to generate a sine wave and write it out to STDOUT for piping into a filter which I am also going to write but I'm having issues.
I am able to generate what sounds like a good clean sine wave, but the frequency is off. When I listen to the output I am generating and compare to a true signal generators output, the frequency of mine is slightly too high.
The C code should generate raw 16 bit signed stereo wav @ 44100Hz for a frequency passed as an argument to the program, or 500Hz as default.
A matlab version of the code is here, (modded very slightly for 1 indexing in Matlab, WHY MATHWORKS WHY!?) minus the passing to STDOUT etc as I know that works fine
CHANNELS = 2; SAMPLING_RATE = 44100; NUM_SAMPLES = 512;
frequency = 1000;
%% BEGIN WHILE(1) LOOP HERE
output_buff = zeros(1,CHANNELS*NUM_SAMPLES);
for i = 1:2:CHANNELS*NUM_SAMPLES
output_buff(i) = 30000 * sin(frequency * pi * (i-1)/NUM_SAMPLES);
output_buff(i+1) = output_buff(i);
end
%% OUTPUT TO STDOUT AND REPEAT
I should add that this code runs (in the C version) inside a while true loop, generating a full output_buff of values, then pushing the buffer to STDOUT.
I have written some further testing code to see what's actually being generated as follows:
plot(1:CHANNELS*NUM_SAMPLES, output_buff)
output_buff = output_buff .* hanning(length(output_buff))';
Y = fft(output_buff);
Mag=abs(Y(1:length(output_buff)/2)).^2;
[a,b]=max(Mag);
% Result
SAMPLING_RATE*b/length(output_buff)
When I run this script, I can see at the end the frequency of the generated signal is actually 1.0767e+03Hz... Close but no cigar...
I've tried tweaking some of the parameters but I have no idea what's wrong or how to make the generated frequency more accurate.
The C code is as follows:
#include <sys/types.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define M_PI 3.1415926535897932384626433832795L
#define NUM_CHANNELS 2
#define NUM_SAMPLES 1024
#define SAMPLE_RATE 44100
double frequency = 500.0;
int main(int argc, char *argv[])
{
if (argc >= 2){
frequency = atof(argv[1]);
}
int16_t samples[NUM_CHANNELS * NUM_SAMPLES];
unsigned cbBuffer=sizeof(samples);
while(1){
for (int i = 0; i < cbBuffer; i+=NUM_CHANNELS){
samples[i] = (int16_t) 30000 * sin(frequency * M_PI * (double)i /(double)(NUM_SAMPLES));
samples[i+1] = samples[i];
}
int done=write(STDOUT_FILENO, samples, cbBuffer);
if(done<0){
fprintf(stderr, "%s : Write to stdout failed, error=%s.", argv[0], strerror(errno));
exit(1);
}else if(done!=cbBuffer){
fprintf(stderr, "%s : Could not read requested number of bytes from stream.\n", argv[0]);
}
}
return 0;
}
UPDATE:
I changed the C code as follows and I now get a nice smooth sine wave but the frequency is way too low
int main(int argc, char *argv[])
{
if (argc >= 2){
frequency = atof(argv[1]);
}
int16_t samples[NUM_CHANNELS * NUM_SAMPLES];
unsigned cbBuffer=sizeof(samples);
int counter = 0;
while(1){
for (int i = 0; i < NUM_CHANNELS * NUM_SAMPLES; i+=NUM_CHANNELS){
samples[i] = (int16_t) 3000 * sin(frequency * M_PI * (double)counter /(double)(SAMPLE_RATE));
samples[i+1] = samples[i];
counter++;
}
int done=write(STDOUT_FILENO, samples, cbBuffer);
if(done<0){
fprintf(stderr, "%s : Write to stdout failed, error=%s.", argv[0], strerror(errno));
exit(1);
}else if(done!=cbBuffer){
fprintf(stderr, "%s : Could not read requested number of bytes from stream.\n", argv[0]);
}
}
return 0;
}
sox --no-show-progress -t raw -e signed -b 16 -c 2 -r 44100
$\endgroup$