ALSA 'snd_pcm_writei' behavior in blocking mode - c

I slightly modified the demo taken from ALSA Project website in order to test it on my laptop's sound card (Intel PCH ALC3227 Analog, Ubuntu 18.04), which requires 2 channels and 16 bit integers. I also doubled the latency (1 s), switched off resampling and made the demo lasts longer. This is the code (runtime error checking not pasted for sake of synthesis)
#include <alsa/asoundlib.h>
#include <stdlib.h>
static char *device = "hw:1,0"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void) {
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = (unsigned char) (rand() & 0xff);
snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)
snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 48000, 0, 1E6);
// Print actual buffer size
snd_pcm_hw_params_t *hw_params;
snd_pcm_hw_params_malloc(&hw_params);
snd_pcm_hw_params_current(handle, hw_params);
snd_pcm_uframes_t bufferSize;
snd_pcm_hw_params_get_buffer_size(hw_params, &bufferSize);
printf("ALSA buffer size = %li\n", bufferSize);
// playback
for (i = 0; i < 256; ++i) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / 4);
if (frames < 0)
frames = snd_pcm_recover(handle, (int) frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror((int) frames));
break;
}
if (frames > 0 && frames < (long) sizeof(buffer) / 4)
printf("Short write (expected %li, wrote %li)\n",
(long) sizeof(buffer) / 4, frames);
}
snd_pcm_hw_params_free(hw_params);
snd_pcm_close(handle);
return (0);
}
Audio works but could someone explain me why I sometimes get output like the following
ALSA buffer size = 16384
Short write (expected 4096, wrote 9)
Short write (expected 4096, wrote 4080)
indicating that less frames than expected have been written by snd_pcm_writei? According to the ALSA docs, I understand that a signal has to be occurred, but I don't get the reason and which signal is.
I also tried to halve the buffer's size, but the result is pretty the same.

A short read is reported when an error happens, but some frames were already written successfully.
You are supposed to call the same function again, with the remaining buffer; if the error was not transient, it will be reported then.
(This example code is wrong; it just ignores that the remaining part of the buffer was not written.)

Related

Alsa lib 32 bits

I've been trying to use the ALSA lib for a while and I don't understand how I should use it.
I took an example program and I've tried to modify it to use float (32bits) instead of unsigned char (8bits). But now when I'm running it, I have a segmentation fault in the second loop.
Here is my code :
#include <alsa/asoundlib.h>
snd_pcm_t *create_pcm(const char* name, snd_pcm_stream_t mode, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int nbChannel, unsigned int rate, int softSample, unsigned int latency)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, name, mode, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
format,
access,
nbChannel,
rate,
softSample,
latency)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
return handle;
}
int main(void)
{
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
float buffer[16*1024]; /* some random data */
handle = create_pcm("default", // name of the device used by the sound card
SND_PCM_STREAM_PLAYBACK, // to use the device in output
SND_PCM_FORMAT_FLOAT, // use the device with 32bit depth (float)
SND_PCM_ACCESS_RW_INTERLEAVED,
1, // use 1 channel
48000, // use 48000 Hz (dvd quality)
1, // soft resample ON
500000); // 0.5s of latency
// building random data
for(i = 0; i < sizeof(buffer); i++)
buffer[i] = i % 255; // random();
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer)); // segmentation fault
if(frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
break;
}
if (frames > 0 && frames < (long)sizeof(buffer))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
How to use this lib with 32bits?
I've tried this format and others like little endian or big endian.. The only one that doesn't crash is SND_PCM_FORMAT_FLOAT but it's making the error :
ALSA lib pcm.c:8507:(snd_pcm_set_params) Sample format not available for PLAYBACK: Invalid argument
Playback open error: Invalid argument
Thanks in advance.
P.S.: Linux, Ubuntu 19.10 64bits
The segmentation fault may already occur when you write into buffer:
for(i = 0; i < sizeof(buffer); i++)
buffer[i] = i % 255; // random();
sizeof(buffer) will give you the size in bytes not the number of elements. They are only equal for char (and unsigned char) since sizeof(char) is 1. You most likely want to iterate over the elements:
for(i = 0; i < sizeof buffer/sizeof *buffer; i++)
buffer[i] = i % 255; // random();
It was indeed a problem of condition in my loop and my snd_pcm_writei()
Here is the code without errors thanks to #Osiris :
#include <alsa/asoundlib.h>
snd_pcm_t *create_pcm(const char* name, snd_pcm_stream_t mode, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int nbChannel, unsigned int rate, int softSample, unsigned int latency)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, name, mode, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
format,
access,
nbChannel,
rate,
softSample,
latency)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
return handle;
}
int main(void)
{
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
float buffer[16*1024]; /* some random data */
handle = create_pcm("default", // name of the device used by the sound card
SND_PCM_STREAM_PLAYBACK, // to use the device in output
SND_PCM_FORMAT_FLOAT, // use the device with 32bit depth (float)
SND_PCM_ACCESS_RW_INTERLEAVED,
1, // use 1 channel
48000, // use 48000 Hz (dvd quality)
1, // soft resample ON
500000); // 0.5s of latency
// building random data
for(i = 0; i < sizeof(buffer) / sizeof(*buffer); i++)
buffer[i] = i % 0xffffffff; // random();
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / sizeof(*buffer)); // segmentation fault
if(frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
break;
}
if (frames > 0 && frames < (long)(sizeof(buffer) / sizeof(*buffer)))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}

Sending 'signed long int' via sockets

I am trying to send a long int number using sockets (from client to server).
Both the client and the server are x86 (and identical machines). The client writes 21 long int's to the socket and the server reads it out.
Here is a part of the code.
Server:
long int num = 0;
int ret;
for (int i = 0; i < 21; i++) {
if ((ret = read(sfd_client, (char *) &num, sizeof(num))) == -1) {
perror("read");
exit(1);
}
printf("number = %d = %ld ret = %d\n", i, num, ret);
}
Client:
for (int i = 0; i < 21; i++) {
if (write(sockfd_client, &temp[i], sizeof(long int)) == -1) {
exit(1);
}
}
I noticed that after calling the read the return value is exactly 8, which means that 8 bytes were read; yet the data is always 0. I don't understand what I am doing wrong. Also I was looking at various functions, and all cater to unsigned numbers but not signed (ntoh and hton family).
NOTE: Also I noticed that the first read() is 8 bytes, but the following ones are only 1 byte.
Whats the best way I can transmit all these numbers? (Also I noticed that the for loop refuses to quit if I have the read statement in there.)
Solution (The problem was the fact that the read was returning less bytes than required, This function solved it)
void read_long(int sockfd_client, char *num) {
unsigned int size = sizeof(long int);
int rlen = 0;
int ret;
while (rlen < size) {
if ((ret = read(sockfd_client, (num + rlen), size - rlen)) == -1) {
perror("read_long");
exit(1);
}
if (ret == 0) {
perror("socket closed before consumption");
exit(1);
}
rlen += ret;
}
}
[I'm going to repeat my comment as an answer, given that it turned out to be correct]
Note that calling read() with sizeof(num) returns up to sizeof(num) bytes. It might return fewer and it's your responsibility to accumulate them.
Similarly write() does not guarantee to write the requested number of bytes. You need to check the return value from write to see how many bytes were actually written, and then write the remaining bytes.

Getting EINVAL when trying to write to mtd device

I am referring to the code in this answer.
I added error handling though.
The open() erase and the read() all perform without error and the 20 bytes that I read all are 0xff.
However, when trying to write the 20 bytes from the data[] array, I get an EINVAL errorcode from the write() function.
What could be the cause of the problem? I did erase the memory before trying to write...
I have seen your original post.
I have the same problem recently, and I found that the write size is important.
mtd_info_t(struct mtd_info_user) have a variable named writesize
(reference: https://elixir.bootlin.com/linux/v3.2/source/include/mtd/mtd-abi.h#L125)
struct mtd_info_user {
__u8 type;
__u32 flags;
__u32 size; /* Total size of the MTD */
__u32 erasesize;
__u32 writesize;
__u32 oobsize; /* Amount of OOB data per block (e.g. 16) */
__u64 padding; /* Old obsolete field; do not use */
};
when write to the mtd, should notice writesize
#include <fcntl.h>
#include <mtd/mtd-user.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
int main(){
mtd_info_t mtd_info; // the MTD structure
erase_info_t ei; // the erase block structure
int i;
unsigned char read_buf[20] = {0x00}; // empty array for reading
int fd = open("/dev/mtd0", O_RDWR); // open the mtd device for reading and
// writing. Note you want mtd0 not mtdblock0
// also you probably need to open permissions
// to the dev (sudo chmod 777 /dev/mtd0)
ioctl(fd, MEMGETINFO, &mtd_info); // get the device info
// dump it for a sanity check, should match what's in /proc/mtd
printf("MTD Type: %x\nMTD total size: %x(hex) bytes\nMTD erase size: %x(hex) bytes\nMTD write size: %x(hex) bytes\n",
mtd_info.type, mtd_info.size, mtd_info.erasesize, mtd_info.writesize);
ei.length = mtd_info.erasesize; //set the erase block size
for(ei.start = 0; ei.start < mtd_info.size; ei.start += ei.length)
{
ioctl(fd, MEMUNLOCK, &ei);
// printf("Eraseing Block %#x\n", ei.start); // show the blocks erasing
// warning, this prints a lot!
ioctl(fd, MEMERASE, &ei);
}
lseek(fd, 0, SEEK_SET); // go to the first block
read(fd, read_buf, sizeof(read_buf)); // read 20 bytes
// sanity check, should be all 0xFF if erase worked
for(i = 0; i<20; i++)
printf("buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
/**********************************************************
* important part! *
* notice the size of data array is mtd_info.writesize *
**********************************************************/
uint32_t write_size = mtd_info.writesize;
unsigned char data[write_size];//write 0
bzero(data, write_size);
lseek(fd, 0, SEEK_SET); // go back to first block's start
write(fd, data, sizeof(data)); // write our message
lseek(fd, 0, SEEK_SET); // go back to first block's start
read(fd, read_buf, sizeof(read_buf));// read the data
// sanity check, now you see the message we wrote!
for(i = 0; i<20; i++)
printf("buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
close(fd);
return 0;
}
Hope it can help
From ./drivers/mtd/nand/nand_base.c
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
This is the check performed by driver:
/* Reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
pr_notice("%s: attempt to write non page aligned data\n",__func__);
return -EINVAL;
}
Both the address you are starting the write and and length of the buffer you are writing must satisfy the macro condition(to be multiple of the subpage size).

Sending sine wave values from array to audio output

Im trying to get a c program to send a sine wave tone to the audio output of my raspberry pi using ALSA
Im generating the buffer of sine wave values but when i send them it doesnt sound like a sine - but like low-mid frequency noise?
I have a dumped the values from the array into a .csv and plotted in excel to verify the sine wave is good
Just wondering have a got stuff fundamentally incorrect in this program, if someone could spot something id be very grateful, thanks
edit: final working code below!!!
#include <alsa/asoundlib.h>
#include <alsa/pcm.h>
#include <math.h>
#define BUFFER_LEN 48000
static char *device = "default"; //soundcard
snd_output_t *output = NULL;
float buffer [BUFFER_LEN];
int main(void)
{
int err;
int j,k;
int f = 440; //frequency
int fs = 48000; //sampling frequency
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
// ERROR HANDLING
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_FLOAT,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
// SINE WAVE
printf("Sine tone at %dHz ",f);
for (k=0; k<BUFFER_LEN; k++){
buffer[k] = (sin(2*M_PI*f/fs*k)); //sine wave value generation
}
for (j=0; j<5; j++){
frames = snd_pcm_writei(handle, buffer, BUFFER_LEN); //sending values to sound driver
}
snd_pcm_close(handle);
return 0;
}
You have configured the sample format SND_PCM_FORMAT_U8, but the actuall buffer contains 32-bit floating-point samples.
Use SND_PCM_FORMAT_FLOAT, or define the buffer as an array of unsigned char.
Furthermore, you have confused the loop to initialize the buffer and the loop to play the data, and many bytes/frames numbers, and fs is wrong; you need to use something like this:
for (i = 0; i < BUFFER_LEN; i++)
buffer [i] = sin(2*M_PI*f/48000*i); // sine wave value generation
for (i = 0; i < 10 * 48000 / BUFFER_LEN; i++) { // 10 seconds
frames = snd_pcm_writei(handle, buffer, BUFFER_LEN);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
break;
}
if (frames > 0 && frames < BUFFER_LEN)
printf("Short write (expected %li, wrote %li)\n", BUFFER_LEN, frames);
}

socket and portability between x86 and x86-64

Let's take an example of my doubt with this server code:
/* some code */
void *filebuffer = NULL;
/* some other code */
for (size_to_send = fsize; size_to_send > 0; ){
rc = sendfile(f_sockd, fd, &offset, size_to_send);
if (rc <= 0){
perror("sendfile");
onexit(f_sockd, m_sockd, fd, 3);
}
size_to_send -= rc;
}
/* other code */
and this client code:
/* some code */
void *filebuffer;
/*some other code */
for(size_to_receive = fsize; size_to_receive > 0;){
nread = read(f_sockd, filebuffer, size_to_receive);
if(nread < 0){
perror("read error on retr");
onexit(f_sockd, 0, 0, 1);
}
if(write(fd, filebuffer, nread) != nread){
perror("write error on retr");
onexit(f_sockd, 0, 0, 1);
}
size_to_receive -= nread;
}
/* other code */
My question is: if the server is on a x86 machine (little endian) and the client is on a x64 machine (little endian) could the pointer different size (4-8 byte) lead to a problem?
If yes, how can i solve?
No it won't be a problem, as you actually don't send pointers over the socket, just a stream of bytes. The only problem I see is if the file is a binary data and have 64-bit integers in it, and the receiving platform is 32 bit without support for 64 bit integers (e.g. long long), however, that is very unlikely unless you are receiving on an embedded system.
PS.
In your receiving loop you check for read errors, but not for the socket to be closed by the other end (when read returns 0.)

Resources