Search code examples
ctrigonometryalsa

Strange sine wave on output of an alsa linux c program


I'm studying alsa programming on ubuntu.

I'm trying to output a sine wave to line-out of my laptop soundcard, and then redirecting to line-in (microphone) via an audio cable.

  ___LINE_IN(microphone)\
 /                       \
|                         \ _____________
|                          |soundcard-pc |
cable                     /
|                        /
 \ ___LINE_OUT(speakers)/

I'm using this code

#include<stdio.h>
#include<stdlib.h>
#include<alsa/asoundlib.h>
#include<math.h>

#define SIZEBUF 2048

int main(void)
{
    int i;
    int err;
    double x;
    double cost;
    double frequency=500;
    unsigned int rate=44100;
    short buf[SIZEBUF];
    snd_pcm_t *phandle;
    snd_pcm_hw_params_t *hw_params;

    snd_pcm_open(&phandle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    snd_pcm_hw_params_malloc(&hw_params);
    snd_pcm_hw_params_any(phandle, hw_params);

    if ((err = snd_pcm_hw_params_set_access(phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
        printf("Cannot set access type.\n");
        exit(1);
    }
    snd_pcm_hw_params_set_format(phandle, hw_params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_rate_near(phandle, hw_params, &rate, 0);
    snd_pcm_hw_params_set_channels(phandle, hw_params, 1);
    snd_pcm_hw_params(phandle, hw_params);
    snd_pcm_hw_params_free(hw_params);
    snd_pcm_prepare(phandle);

    cost = 2.0 * M_PI * frequency / (double)rate;

    printf("cost=%f.\n", cost);
    for (i = 1 ; i < SIZEBUF ; i++) {
        x      = sin(i * cost);
        buf[i] = (short)(32767 * x + 32768);
    }

    for (i = 0 ; i < 50 ; i++) {
        snd_pcm_writei(phandle, buf, SIZEBUF);
    }
    snd_pcm_close(phandle);

    exit(0);
}

I'm using audacity to see the wave, but it appears strange, like in this image

enter image description here

It seems not to have the behaviour of a sine wave. Why?


Solution

  • You are using samples of type short, and have correctly declared them as S16_LE. However, your code then computes the values as if they were unsigned:

    buf[i] = (short)(32767 * x + 32768);
    

    You do not need an offset; with signed samples, zero actually is zero. Use this:

    buf[i] = (short)(32767 * x);