4

I'm learning to use ALSA on a Raspberry Pi 2. I've written a small test program in C++ to generate a 440 Hz test tone. It makes the tone, but there is a clicking sound about twice per second in the tone.

Does anyone have an idea why this might be happening? The code is below.

#include <cmath>
#include <climits>
#include <iostream>
#include <alsa/asoundlib.h>

#include "definitions.hpp"

using namespace std;

int main() {
    int ret;

    snd_pcm_t* pcm_handle;  // device handle
    snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
    snd_pcm_hw_params_t* hwparams;  // hardware information
    char* pcm_name = strdup("plughw:0,0");  // on-board audio jack
    int rate = 48000;

    const uint16 freq = 440;
    long unsigned int bufferSize = 8192*4;
    const uint32 len = bufferSize*100;
    const float32 arg = 2 * 3.141592 * freq / rate;
    sint16 vals[len];

    for(int i = 0; i < len; i = i + 2) {
        vals[i] = SHRT_MAX * cos(arg * i / 2);
    }

    snd_pcm_hw_params_alloca(&hwparams);

    ret = snd_pcm_open(&pcm_handle, pcm_name, stream, 0);
    cout << "Opening: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params_any(pcm_handle, hwparams);
    cout << "Initializing hwparams structure: " << snd_strerror(ret) << endl;   

    ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams,
            SND_PCM_ACCESS_RW_INTERLEAVED);
    cout << "Setting access: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams,
            SND_PCM_FORMAT_S16_LE);
    cout << "Setting format: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params_set_rate(pcm_handle, hwparams,
            rate, (int)0);
    cout << "Setting rate: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2); 
    cout << "Setting channels: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 2, 0);
    cout << "Setting periods: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams,
            &bufferSize);
    cout << "Setting buffer size: " << snd_strerror(ret) << endl;

    ret = snd_pcm_hw_params(pcm_handle, hwparams);
    cout << "Applying parameters: " << snd_strerror(ret) << endl;

    cout << endl << endl;


    const void* ptr = (const void*)&vals;
    int err;

    do {
        ptr += bufferSize;
        ret = snd_pcm_writei(pcm_handle,
                ptr, len);

        if(ret < 0) {
            err = snd_pcm_prepare(pcm_handle);
            cout << "Preparing: " << snd_strerror(err)
                << endl;
        }
    } while(ret < 0);

    cout << "Writing data: " << ret << ", " << snd_strerror(ret)
        << endl;
}

When you run it, you get this terminal output. Of course, there's no write error, just the number of bits written.

pi@raspberrypi:~/radio $ ./bin/alsatest 
Opening: Success
Initializing hwparams structure: Success
Setting access: Success
Setting format: Success
Setting rate: Success
Setting channels: Success
Setting periods: Success
Setting buffer size: Success
Applying parameters: Success


Writing data: 344110, Unknown error 344110

UPDATE - NEXT DAY OK. I've hooked the output up to my handy-dandy oscilloscope and saw the following waveform. There seems to be a discontinuity in the signal every time there's a click. I added a few lines to count how many nearly-zero values were next to each other in my sinusoid array, and there were none. Oddly enough, the ALSA sample program /test/pcm.c makes a perfect wave. Perhaps I need to write in really small chunks? There doesn't seem to be much difference between my code and the example.

enter image description here

wisner
  • 523
  • 1
  • 5
  • 18
  • A few things to note (there probably are others which could better explain the clicking): 1) Every second sample of the `vals` array are _uninitialized_ because of the `i=i+2` increment in your loop. 2) you skip the first `bufferSize` samples, and yet call `snd_pcm_writei` for the whole `len` samples. 3) The allocated parameters do not have to match exactly the requested values (esp. those with `_near`), so make just to read back the actual before the generation. 4) As you feed chunks of data, make sure those chunks are continuous (esp. if you loop back at the end of your buffer). – SleuthEye Jan 15 '16 at 22:31
  • Thanks, SleuthEye! You really live up to your name :) I had let my blood sugar get too low when I wrote this. 1) yeah, I only cared about one channel. In hindsight, I should have just done both. It's pretty easy. 2) whoops! That was leftover from a loop that fed data in by bufferSize'd chunks. 3) I'll add this. Hopefully it can shed some light. I'll update this question tomorrow when I know the actual values. 4) I'm still trying to work this problem out. It'll be problematic once I incorporate similar code to this into a larger project. – wisner Jan 15 '16 at 23:02
  • When you guys have a chance, can you look at my update? – wisner Jan 16 '16 at 21:07
  • 1
    Did you try this out on a regular linux box? Just run the sample in an ubuntu vm and it generates a perfect sinewave, no clicking at all. – Sjoerd van Kreel May 18 '16 at 21:07
  • You mean you just "ran" the sample in an Ubuntu VM? Or that I should run the sample in the VM? – wisner Feb 06 '17 at 20:18

0 Answers0