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);
}
Related
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;
}
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.)
I use FFmpeg to decode my flac file and write it to pcm file, then use GoldenWave to play it with pcm signed 16bit, little endian, mono and the total play time is ok.
I doubt i write the 2 channel file in one place, but i don't know how to get every signal channel and write it to pcm file.
any help? thank you.
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
AVPacket orig_pkt = pkt;
do {
ret = decode_packet(&got_frame, 0);
if (ret < 0)
break;
pkt.data += ret;
pkt.size -= ret;
} while (pkt.size > 0);
av_free_packet(&orig_pkt);
}
pkt.data = NULL;
pkt.size = 0;
do {
decode_packet(&got_frame, 1);
LOG("flush cached frames");
} while (got_frame);
static int decode_packet(int *got_frame, int cached)
{
int ret = 0;
int decoded = pkt.size;
*got_frame = 0;
if (pkt.stream_index == audio_stream_idx) {
ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
LOG("Error decoding audio frame (%s)\n", av_err2str(ret));
return ret;
}
decoded = FFMIN(ret, pkt.size);
if (*got_frame) {
size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(audio_dec_ctx->sample_fmt);
//decode packet nb_samples:4608, xx:2, unpadded_linesize: 9216
LOG("decode packet nb_samples:%d, xx:%d, unpadded_linesize: %d",
frame->nb_samples, av_get_bytes_per_sample(audio_dec_ctx->sample_fmt), unpadded_linesize);
fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);
//int nb_sample = frame->nb_samples;
//fwrite(frame->extended_data[0], 1, nb_sample, audio_dst_file);
//fwrite(frame->extended_data[0] + nb_sample, 1, nb_sample, audio_dst_file);
}
}
if (*got_frame && api_mode == API_MODE_NEW_API_REF_COUNT)
av_frame_unref(frame);
return decoded;
}
You didn't describe the problem you're having, but from what you're writing, I see two problems:
you're not checking the raw audio format of the frame, see frame->format (or audio_dec_ctx->sample_fmt). You're writing it as if it were AV_SAMPLE_FMT_S16, but you're not checking that it is
your unpadded_linesize is not multiplied by the number of channels (see e.g. frame->channels)
I am pretty new to ffmpeg, and now I want to decode the frames and encode it back in reverse order. I have used this tutorial for decoding the video and this example to encode it back. I can decode the frames properly, but when I re-encode it, I get a dummy image rather than clear video. How can I get the actual reversed video?
static void video_encode_example(const char *filename, int codec_id)
{
AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y, got_output;
FILE *f;
struct SwsContext *sws_ctx = NULL;
AVFrame *frame;
AVFrame *frameRGB = NULL;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
printf("Encode video file %s\n", filename);
/* find the mpeg1 video encoder */
codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base = (AVRational){1,25};
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
/* the image can be allocated by any means and av_image_alloc() is
* just the most convenient way if av_malloc() is to be used */
ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height,
c->pix_fmt, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
exit(1);
}
/* encode 1 second of video */
for (i = 0; i < 25; i++) {
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
fflush(stdout);
/* prepare a dummy image */
/* Y */
for (y = 0; y < c->height; y++) {
for (x = 0; x < c->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
/* Cb and Cr */
for (y = 0; y < c->height/2; y++) {
for (x = 0; x < c->width/2; x++) {
frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;
/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
// Convert the image from its native format to RGB
sws_scale
(
sws_ctx,
(uint8_t const * const *)frame->data,
frame->linesize,
0,
c->height,
frameRGB->data,
frameRGB->linesize
);
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* get the delayed frames */
for (got_output = 1; got_output; i++) {
fflush(stdout);
ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* add sequence end code to have a real mpeg file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
av_frame_free(&frame);
printf("\n");
}
Assuming that your code sample have nothing to do with a video input nor a reverse encoding, but is only an "encode sample".
A video file is made of severa parts.
First, you have a container format (for example mkv, or avi). It contains various informations on which streams are in this file (subtiles? how many video? multiple audio stream? stuff like that).
Then you have the data streams.
A video stream inside this file is a list of packets, encoded by a specific codec (h264 or mpeg4 for example) from frames.
In your code sample, you have a list of frames, and your are encoding them with a codec, giving you a list of packets (I suppose it works, since it is a tutorial code).
But these packets are dumped into a file, without container. So if a video player wants to read them, I will have a hard time to guess what is the format.
It is stated as the first line in your link
Instead of dumping your packets in a file directly, you want to use FFmpeg to embed your packets in a container, and save your container.
You have an example of all this process here
Last thing: if you want to have your video in reverse order, you must encode frames in the reverse order, not only mux packets in reverse order.
When I compile ALSA's pcm_min.c example with
gcc -Wall -lasound pcm_min.c -o pcm_min
Everything is fine, but running it, I get the white noise as expected, but I also get this warning/error:
Short write (expected 16384, wrote 7616)
Which comes from the last if-statement.
#include <alsa/asoundlib.h>
static char *device = "default"; /* 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] = random() & 0xff;
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_U8,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
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;
}
Can someone see why this warning/error occur?
Hugs,
Louise
The snd_pcm_writei() function might return less than sizeof(buffer) when there's either a signal received or an underrun. In your case, it seems that you're mixing bytes and frames. The last parameter of the call is the number of frames that you have in your buffer. Since you're passing the number of bytes in your buffer instead, you're seeing an underrun.
I was also having some problems with this example. I modified it a bit and now it works.
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
static char *device = "default"; /* 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;
snd_pcm_uframes_t bufferSize, periodSize;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
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_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1, //channels
44100, //sample rate
1, //allow resampling
500000) //required latency in us
) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_prepare(handle)) < 0) {
printf("Pcm prepare error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_get_params( handle, &bufferSize, &periodSize )) < 0) {
printf("Pcm get params error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
printf("Buffer size:%d, Period size:%d\n", (int)bufferSize, (int)periodSize);
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, periodSize);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
if (frames > 0 && frames < (long)periodSize)
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}