I'm playing around with pa_simple
api to get a better understanding of digital audio. I've written some code to feed custom generated audio data to the pulseaudio server:
char buff[2];
while (playing) {
timePassed += periodMicros;
int val = userFunc(timePassed/1000000.0);
buff[0] = (val >> 0) & 0xff;
buff[1] = (val >> 1) & 0xff;
int error;
pa_simple_write(s, buff, 2, &error);
}
func
generates a simple sine wave like this:
uint16_t func(double time)
{
double op = sin(2*3.14*freq * time);
return op * 100;
}
It produces a note as expected, but eats up 50% of cpu on my machine, because of the unrestricted while
loop in the first codeblock. So I tried fixing it like this:
void _mainLoop() {
char buff[2*bufSize];
while (playing) {
for ( int i = 0; i < bufSize; i++ ) {
uint8_t word = userFunc(timePassed/1000000.0);
timePassed += periodMicros;
buff[i*2+0] = (word >> 0) & 0xff;
buff[i*2+1] = (word >> 1) & 0xff;
}
pa_simple_write(s, buff, 2*bufSize, &error);
this_thread::sleep_for(chrono::microseconds(bufSize*periodMicros));
}
}
It fixes the cpu issue but the audio generated by it is no longer a perfect note. It appears to be a bit higher pitched with a retro feel to it.
I wanted to know the correct way to use the pa_simple
api without my code eating up the cpu.
Note that the sound generated by second code isn't fixed even if I change bufSize
to 1
and remove the sleep
line.
Edit: This is how I've initialized the pulseaudio connection:
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.channels = 1,
.rate = 44100
};
s = pa_simple_new(
NULL,
"SYNTH",
PA_STREAM_PLAYBACK,
NULL,
"Music",
&ss,
NULL,
NULL,
NULL
);