//Open PCM
if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0 /* Mode. 0=blocking. Can also use SND_PCM_NONBLOCK, which returns read/write access immediately, or SND_PCM_ASYNC, which sends a SIGIO back to the program whenever a period has been processed. */) < 0)
{
fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
snd_pcm_close(pcm_handle);
return -1;
}
//Before writing PCM data, we specify access type, sample format, # of channels, # of periods and period size.
//Init the hwparams with full config space:
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0)
{
fprintf(stderr, "Cannot configure this PCM device!\n");
snd_pcm_close(pcm_handle);
return -2;
}
/* Can get info on configurations with:
snd_pcm_hw_params_can_<capability>,
snd_pcm_hw_params_is_<property>,
snd_pcm_hw_params_get_<parameter>.
Can test parameters (access type, buffer size, # of channels,
sample format, sample rate & # of periods)
with:
snd_pcm_hw_params_test_<parameter>
All of this is especially important with "hw" interfaces. The
configuration can be restricted with:
snd_pcm_hw_params_set_<parameter>
*/
//Let's assume 16BitLE data sampled at 44.1KHz:
int rate = 44100;
int exact_rate; //Sample rate returned by snd_pcm_hw_params_set_rate_near()
int dir; //if exact_rate==rate, dir=0. if exact_rate<rate, dir=-1, else dir=+1.
int periods = 2; //Number of periods
snd_pcm_uframes_t periodsize = 8192; //Period size (bytes) = 8kb
/* Interleaved data is stored as the words (for 16bLE, bytes for 8b, etc)
for each channel stored consecutively, then the next period, etc.
Noninterleaved data is stored as each period as a chunk of words for
the first channel, then the nexrt channel, etc, before going to the
next period. Basically, interleaved subdivides the period into the
channels, whereas non-interleaved goes a period at a time.
*/
//Set access type to Interleaved:
if (snd_pcm_hw_params_set_access(pcm_handle,hwparams,SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
{
fprintf(stderr, "Error setting access to Interleaved!\n");
snd_pcm_close(pcm_handle);
return -3;
}
//Set sample format to 16bLE (16 bit Little Endian)
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
{
fprintf(stderr,"Error setting format to 16bLE.\n");
snd_pcm_close(pcm_handle);
return -4;
}
//Set the sample rate - use the nearest to the desired rate that is supported by the hardware!
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(pcm_handle,hwparams,&exact_rate, 0) < 0)
{
fprintf(stderr, "Error setting rate to %d Hz.\n",rate);
snd_pcm_close(pcm_handle);
return -5;
}
if (rate != exact_rate)
{
fprintf(stderr, "Warning: The rate %d Hz is not supported by your hardware.\n Using %d Hz instead.\n", rate, exact_rate);
}
//Set number of channels
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2 /* Stereo */) < 0)
{
fprintf(stderr, "Error setting channels to stereo.\n");
snd_pcm_close(pcm_handle);
return -6;
}
//Set number of periods (AKA Fragments)
if (snd_pcm_hw_params_set_periods(pcm_handle,hwparams,periods,0) < 0)
{
fprintf(stderr, "Error setting periods (fragments) to %d.\n", periods);
snd_pcm_close(pcm_handle);
return -6;
}
/* Unit of Buffersize is sometimes bytes and sometimes frames. 1 frame =
num bytes in data * num channels (16bLE*stereo=4 bytes here)
If buffersize of 2^n is not supported, use
snd_pcm_hw_params_set_buffer_size_near(), which has a format similar to
snd_pcm_hw_params_set_rate_near().
*/
snd_pcm_uframes_t bsize;
//Set buffer size in frames.
//Latency = (periodsize * periods) / (rate * bytesperframe) = BufferSize / rate
bsize = (periodsize*periods)>>2;
if (snd_pcm_hw_params_set_buffer_size(pcm_handle,hwparams,bsize) < 0)
{
fprintf(stderr, "Error setting buffer size. Attempting to set near %d.\n", bsize);
if (snd_pcm_hw_params_set_buffer_size_near(pcm_handle,hwparams,&bsize) < 0)
{
fprintf(stderr, "Error setting buffer size.\n");
snd_pcm_close(pcm_handle);
return -7;
}
}
printf("Buffer is %d.\n",bsize);
//Apply the configuration:
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0)
{
fprintf(stderr, "Error setting HW params!\n");
snd_pcm_close(pcm_handle);
return -8;
}
/* Once the device is configured, PCM data can be written. Playback starts
at the first write!
For Interleaved access we use:
snd_pcm_sframes_t snd_pcm_writei(pcm_handle, data, numframes);
which writes numframes frames from buffer data to PCM device at
pcm_handle, (frame by frame?) returning the number of frames actually
written.
For Noninterleaved access we use:
snd_pcm_frames_t snd_pcm_writen(pcm_handle, data, numframes);
which writes numframes frames from buffer data to the PCM device at
pcm_handle (period by period?) returning the number of frames actually
written.
*/