Why does the code print values separated by zeroes, i.e. "65 0 64 0 65 0 66 0 64 0 ..."?
Normally the default recording audio specification is the one written in your code:
/* Each sample is Signed 16-bit integer recorded in little-endian (the Native
* Endian in intel x86 and x86-64).
*/
ss.format = PA_SAMPLE_S16NE;
/* There are 2 channels. */
ss.channels = 2;
/* Sample rate is 44100 Hz. */
ss.rate = 44100;
/* This is how audio samples are arranged. Given a stream of samples
*
* ADDR 0x0 0x2 0x4 0x6 0x8 0xA
* SAMPLE 12345, 13579, -185, -3, -9876, -9999, ...
* |-----||-----||-----||-----||-----||-----|
* CH1 CH2 CH1 CH2 CH1 CH2
* |------------||------------||------------|
* FRAME FRAME FRAME
*/
Given a sample with value of 65, its hex is 0x0041, with little-endian, it will be stored as 41 00
in memory. Notice that your code print buf
byte by byte, that's why you got 65 0
. So with sample stream 65, 64, 65, ... you will print 65 0 64 0 65 0 ...
.
Tried parec
and pacat
, but got static noise only. How to make it work?
Make sure the audio input (microphone) is not muted and has suitable volume. Run pavucontrol
, go to the "Input Devices" tab, unmute the microphone, and adjust the volume. Make some noise and you should see the signal bar (under the volume bar) jumping.
PulseAudio comes with several recording and playing utilities (package libpulse
in arch, package pulseaudio-utils
in debian): parec
, parecord
, pacat
, paplay
. Try them. They should work.
Play sound recorded by parec
with pacat
.
$ parec sample1.wav
^C
$ pacat sample1.wav
Play sound recorded by parecord
with paplay
.
$ parecord sample2.wav
^C
$ paplay sample2.wav
The main difference between them is that parec
and pacat
do not write and read header in the sound file, while parecord
and paplay
do.
After successfully running those commands, you can try to compile pacat-simple.c and parec-simple.c from the official repository, under src/tests/
.
$ gcc pacat-simple.c -o pacat-simple -lpulse-simple -lpulse
$ gcc parec-simple.c -o parec-simple -lpulse-simple -lpulse
After that, try to run them.
$ ./parec-simple > sample3.wav
^C
$ ./pacat-simple < sample3.wav
It should be working. Now you can start to examine and play with parec-simple.c
and pacat-simple.c
. Note that parec-simple
and pacat-simple
do not write and read header in sound file too, just like parec
and pacat
.
With some modifications on the while-loop, your code will work like parec-simple
. Modified version (file my-parec.c
):
while(1) {
pa_simple_read(s, buf, sizeof(buf), NULL);
//for(int i = 0; i < sizeof(buf); i++)
// printf("%d,", buf[i]);
//printf("\n-----------\n"); // just here for me separating the data, im not playing this back
fwrite(buf, 1, sizeof(buf), stdout);
}
Compile and run it:
$ gcc my-parec.c -o my-parec -lpulse-simple -lpulse
$ my-parec > sample4.wav
^C
Are there any examples of using PulseAudio C API?
Besides parec-simple.c
and pacat-simple.c
, if you've read through the PulseAudio asynchronous API documentation, you can checkout pacat.c under src/utils/
as an example. Actually parec
, parecord
, and paplay
are just symbolic links of pacat
, which source code is pacat.c
.
What is the header in a sound file? It was mentioned above.
If a sound file contains only data, then a player cannot know the audio specification of that file, like number of channels, number of bits (bit depth) per sample, endianness, etc. In that case it can only assume the default specification is used, which may not be the case. So, in order to save those properties into the sound file, we've created a header section in that file to store them. Refer to WAV PCM soundfile format and audio file format specifications for the header format.
To manage the header section of a sound file, the C library libsndfile can be used. pacat.c
did use the library to read and write sound files with a header. libsndfile
also provides some utilities such as sndfile-info
that help us to inspect sound files. Those utilities are included in package libsndfile
in arch, and package sndfile-programs
in debian.
$ sndfile-info sample2.wav
========================================
File : sample2.wav
Length : 698668
RIFF : 698660
WAVE
fmt : 16
Format : 0x1 => WAVE_FORMAT_PCM
Channels : 2
Sample Rate : 44100
Block Align : 4
Bit Width : 16
Bytes/sec : 176400
data : 698624
End
----------------------------------------
Sample Rate : 44100
Frames : 174656
Channels : 2
Format : 0x00010002
Sections : 1
Seekable : TRUE
Duration : 00:00:03.960
Signal Max : 17226 (-5.59 dB)
To add a header into a sound file without header, the easiest way that I can think of is, copy the header from another sound file that has a header, prepend it to the sound file without header, and change some values using a hex editor (I use ghex). Beware of the endianness while editing numeric fields in the header.
# In my system, sample2.wav has the simplest header which is 44 bytes long.
head -c 44 sample2.wav > header.wav
# Prepend header to sound file without header.
cat header.wav sample3.wav > sample3-with-header.wav
# Edit the chunk size field and data chunk size field.
ghex sample3-with-header.wav
Other references
PulseAudio under the hood explained PulseAudio server quite well. Its topics on Buffering and Latency helps in understanding the API usage like pa_stream_begin_write()
and pa_stream_write()
.