To start learning to use asoundlib.h, I am writing a simple program that plays a tone.
I am filling an array of shorts, samples of length buffersize. with samples of a sine wave.
When I pass samples to snd_pcm_writei() with its length, buffersize, the samples in samples are played for half of the time and noise is played for the other half of the time. When I pass samples to snd_pcm_writei() with a length of buffersize/2, the output is half as long clearly but all the noise is eliminated. That is to say, it seems like snd_pcm_writei() expects two array elements for each sample, but I cannot figure out why.
I am writing sample values to each element of samples. Believe I am configuring the pcm correctly for single channel, 16-bit, 44100 sample rate playback. I have it configured for one period per buffer.
Is there something in my pcm configuration that is causing snd_pcm_writei() to process twice as many array elements as I'm expecting it to? If not, what else could be causing half of whats played back to be noise?
Code below
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <math.h>
#include "waveform.h"
#define pi 3.14159265358979323846
int main() {
int duration=10; //seconds
int samplerate=44100;
int periodsize=44100;
int periodsperbuffer=1; //dont change for nodbay
int buffersize=periodsizeperiodsperbuffer;
int periodtime=(int)1000000(((double)periodsize)/(samplerate));
int iterations=(int)((double)duration*1000000/(periodtime));
short *samples;
samples = (short*)malloc(sizeof(short)*buffersize);
for (int i=0; i<buffersize; i++) {
samples[i]=0;
}
snd_pcm_t *pcm;
if (snd_pcm_open(&pcm, "hw:CARD=PCH,DEV=0", SND_PCM_STREAM_PLAYBACK,0)<0) {
printf("Couldn't open device for playback\n");
}
snd_pcm_hw_params_t *hw_params;
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_hw_params_any(pcm, hw_params);
snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(pcm, hw_params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(pcm, hw_params, 1);
snd_pcm_hw_params_set_rate(pcm, hw_params, samplerate, 0);
snd_pcm_hw_params_set_periods(pcm, hw_params, periodsperbuffer, 0);
snd_pcm_hw_params_set_period_time(pcm, hw_params, periodtime, 0);
snd_pcm_hw_params(pcm, hw_params);
float freq=440;
float phase=0;
float amp=1;
add_harmonic(samples,buffersize,freq,phase,amp,samplerate);
for (int i=0; i<iterations; i++){
snd_pcm_writei(pcm, samples, buffersize/2); // why does buffersize need to
} // be divided by two??
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
free(samples);
return 0;
}
and the function add_harmonic from waveform.h
void add_harmonic(short *buffer, int bufferlen, float freq, float phase, float amp, int samplerate) {
freq=freq*2*pi;
phase=phase*2*pi;
int max=0;
for (int i=0; i<bufferlen; i++) {
buffer[i] = buffer[i]+(short)amp*sin(freq*(float)i/samplerate+phase)*10000;
if (buffer[i]>max) {
max=buffer[i];
}
}
for (int i=0; i<bufferlen; i++) {
buffer[i] = (short)(10000*buffer[i]/max);
}
}
Side note, the program sometimes segfaults even when the size of the buffer is specified as buffersize/2.