用基础知识在C / Matlab中生成正弦波

时间:2014-02-04 22:47:22

标签: c matlab audio wav sine

我一直在编写一个基本的C程序来生成一个正弦波并将其写入STDOUT,以便输入一个过滤器,我也会写,但我遇到了问题。

我能够产生听起来像一个干净的正弦波,但频率是关闭的。当我收听我正在生成的输出并与真正的信号发生器输出进行比较时,我的频率略高。

对于作为程序参数传递的频率,C代码应生成原始16位带符号立体声wav @ 44100Hz,默认为500Hz。

代码的matlab版本在这里,(在Matlab中为1索引修改得非常轻微,为什么MATHWORKS为什么!?)减去传递给STDOUT等因为我知道它工作正常

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

我应该补充说,这段代码在while循环中运行(在C版本中),生成一个完整的output_buff值,然后将缓冲区推送到STDOUT。

我已经编写了一些进一步的测试代码,以了解实际生成的内容如下:

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)

当我运行这个脚本时,我可以看到生成信号的频率实际上是1.0767e + 03Hz ......关闭但没有雪茄......

我尝试调整一些参数,但我不知道错误是什么,或者如何使生成的频率更准确。

C代码本身在我的Linux安装上,如果需要,我可以在明天添加。

3 个答案:

答案 0 :(得分:0)

您正在处理数字信号处理,这是一个复杂的领域,并且有一个专用于它的dsp stackexchange站点。 我的建议是:

  • 如果您想输出完全输入的内容,请选择生成一个频率,该频率是采样率除以2的幂,例如44100/44 = 1002.272727 ...在这种情况下,一个长度为2的幂的样本将完全适合您的FFT输入。

  • 如果您想要更好的FFT结果,请尝试将样本数增加到4096或8192.因为采样率为44,100Hz的512个样本意味着您有512 / 44,100 = ~11.61信号的ms。这意味着你的正弦波数量不完整。一个完整的1000 Hz正弦波正好是1 ms。这个不完整的周期数可能会导致FFT出现近似误差。

答案 1 :(得分:0)

事实证明我有一些错误。我的for循环使用输出缓冲区中的字节数,而不是缓冲区中的元素数。我忽略了wav文件采样率在生成正弦波时的相关性,并且使用循环变量的不连续性导致了奇怪的伪像。

最终的工作代码如下:

#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 512
#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);
    int counter = 0;

    while(1){
        for (int i = 0; i < NUM_CHANNELS * NUM_SAMPLES; i+=NUM_CHANNELS){
            samples[i] = (int16_t) 3000 * sin(frequency * 2 * 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;
}

答案 2 :(得分:0)

我认为我的版本略有改进 - 使用四分之一正弦查找表,该表在主要的while循环之外预先定义。

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

// PI defined here for use in code
#define PI 3.141592653589793
// SINE_TABLE_SIZE - number of values in the sine lookup table - defined here for use in code
#define SINE_TABLE_SIZE 256
#define NUM_CHANNELS 2
#define NUM_SAMPLES 512

//* Sampling frequency in HZ. Must only be set to 8000, 16000, 24000 32000, 44100 (CD standard), 48000 or 96000 */
int sampling_freq = 44100;
// Array of data used by sinegen to generate sine. These are the initial values.
float y[3] = {0,0,0};
float x[1] = {1}; // impulse to start filter
float a0 = 1.4142; // coefficients for difference equation
float b0 = 0.707;
// Holds the value of the current sample
float sample;
float sine_freq = 500.0;

// An array of floats containing SINE_TABLE_SIZE elements
float table[SINE_TABLE_SIZE];
// Step variable for use in the quarter sinegen function
float step4 = 0;
// Modified step value to within Sine wave table values - for use in quarter sine wave table
float table_value;



/********************************** sine_init() **************************/ 
void sine_init4()
{
    // Fill the lookup table with 256 sine data points across one quarter cycle.
    int j;
    for(j=0; j < SINE_TABLE_SIZE; j++)
    {
        table[j] = sin(((0.5*PI*j)/SINE_TABLE_SIZE));
    }
}



/********************************** sinegen4() **************************/
float sinegen4(void)
{
/* This code produces a variable frequency sine wave, using a
quarter-sine-wave lookup table.
*
* */
    float wave4 = 0; // Initialise a variable to store the sine wave datapoint values in.
// To create a sine wave from the quarter sinewave table data.
//For values in the first sinewave quadrant - no adjustment to the step value needs to be made.
    if (step4 < (SINE_TABLE_SIZE))
    {
        table_value = step4;
        wave4 = table[(int)step4];
    }
//Second quadrant - step value must be adjusted to bring the value back into the range 0-255
    else if (step4 < (2*SINE_TABLE_SIZE) && (step4 >= SINE_TABLE_SIZE))
    {
        table_value = ((SINE_TABLE_SIZE-1)-(step4-SINE_TABLE_SIZE));

        wave4 = table[(int)((SINE_TABLE_SIZE-1)-(step4- SINE_TABLE_SIZE))];
    }
//Third quadrant - step value must be adjusted to bring the value back into the range 0-255 and the wave value negated
    else if (step4 < (3*SINE_TABLE_SIZE) && (step4 >= (2*SINE_TABLE_SIZE)) )
    {   
        table_value = (step4-(2*SINE_TABLE_SIZE));
        wave4 = -table[(int)(step4-(2*SINE_TABLE_SIZE))];
    }
//Fourth quadrant - step value must be adjusted to bring the value back into the range 0-255 and the wave value negated
    else if (step4 < (4*SINE_TABLE_SIZE) && (step4 >=(3*SINE_TABLE_SIZE)) )
    {
        table_value = ((SINE_TABLE_SIZE-1)-(step4-(3*SINE_TABLE_SIZE)));
        wave4 = -table[(int)((SINE_TABLE_SIZE-1)-(step4-(3*SINE_TABLE_SIZE)))];
    }
// assign step a value based on sampling frequency and desired output frequency to calculate next table value required.
    step4 += ((4*SINE_TABLE_SIZE)/(sampling_freq/sine_freq));
//To prevent step containing values greater than 4*SINE_TABLE_SIZE-1 which would cause the operation to overflow.
    if (step4 > ((4*SINE_TABLE_SIZE-1)))
    {
        step4 = step4 - (4*SINE_TABLE_SIZE-1);
    }

    return wave4;
}




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

    if(argc > 1)
    {
        sine_freq = atoi(argv[1]);
//      printf("n = %d \n", n );
    }

    // initialises table of one quarter sinewave data
    sine_init4();
    int16_t samples[NUM_CHANNELS * NUM_SAMPLES];
    unsigned cbBuffer=sizeof(samples);
    // Loop endlessley generating a sine wave
    while(1)
    {

    // Calculate next sample from quarter sinewave data table
        for (int i = 0; i < NUM_CHANNELS * NUM_SAMPLES; i+=NUM_CHANNELS)
        {
            samples[i] = 3000*sinegen4();
            samples[i+1] = samples[i];
            //printf(" samples[i] = %d", samples[i]);
        }
        // Copy one sample to output
        int done=write(STDOUT_FILENO, samples, cbBuffer);
        //int done = 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;
}