Author Topic: Ubuntu C-language sound peripheral programming - simple C example to read/write?  (Read 1555 times)

0 Members and 1 Guest are viewing this topic.

Offline wb0gazTopic starter

  • Regular Contributor
  • *
  • Posts: 190
I have a need to read/write sound streams from the sound port (card, motherboard, USB, etc.) peripheral in Ubuntu linux distribution, using (strong preference) C language I can compile (from source.)

The ideal solution would be something that would accept the sound peripheral identifier (for recording/input or playback/output) and related parameters (sample rate, bits/sample, etc.) on command line, then output a continuous stream of bytes to stdout (from the recording input), or accept a stream of bytes on stdin (to go to the playback output.) I realize the sample rate of audio is quite fixed, so presumably there would be modest buffering in the program, and it would be the responsibility of the (software, etc.) linked to stdin/stdout to maintain the required data rate, or accept overruns/dropouts as appropriate.

The important requirements are that the code is in source form so it can be recompiled, and that it just emit or accept (either, doesn't need to do both at the same time, as two instances would be used for simultaneous recording and playback) audio stream using (ideally) stdin/stdout, or alternatively, a named pipe or something like that.

This isn't intended as a media player or recorder (no files need be read or written), it just needs to work with live audio stream at the physical port of the PC.

Any pointers to whether this has been solved/done or otherwise where I might get started?

Thanks!
 

Offline FlyingDutch

  • Regular Contributor
  • *
  • Posts: 144
  • Country: pl
Hello,

see audio-codecs (and streaming audio) in "FFMPEG" source code; Here is link to it's WWWpage:

https://www.ffmpeg.org/documentation.html

Best Regards
 


Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
Well.. 

I am not sure what level of knowledge you need...

But buntus  are still  bounded to *NIX (Linux) at least..
(and besides all efforts made w/pulseaudio by POTTERIX himself)
(which BTW pulseaudio is a piece of crap.. as well )

and..  you already know  in *NIX everything is a file..

so it is a plain trivial matter to open the file (ANY FILE!) over a FIFO
or even better a named pipe... and redirect elsewhere...

like
Code: [Select]

mkfifo audio.wav && cat audio.wav | <whatever DSP>


Obviously  audio.wav like any file can be the  port  /dev/audio as well
as long as you do not name the pipe as the device ... of course...
all file operators apply trivial like cat/tee .. on your sound devices..

trivial and used daily in any DAW... mostly combined with a REAL TIME
transport stream like JACK  to route multi-channel named pipes...

in doubt man mkfifo
Paul
« Last Edit: January 21, 2022, 05:42:26 pm by PKTKS »
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
If you want to send the audio in C, which is totally different than your 'preferred' pipe solution, then PulseAudio is probably the way to go on modern Linux, it's almost always the default audio server. The simple API is pretty straightforward:

Code: [Select]
pa_simple *s;
pa_sample_spec ss;
 
ss.format = PA_SAMPLE_S16NE;
ss.channels = 2;
ss.rate = 44100;
 
s = pa_simple_new(NULL,               // Use the default server.
                  "Fooapp",           // Our application's name.
                  PA_STREAM_PLAYBACK,
                  NULL,               // Use the default device.
                  "Music",            // Description of our stream.
                  &ss,                // Our sample format.
                  NULL,               // Use default channel map
                  NULL,               // Use default buffering attributes.
                  NULL,               // Ignore error code.
                  );

Then you just call blocking pa_simple_write to send data. There's an async API too if you prefer.

If you just want to use pipelines and stdin/out, you can use the `pacat` command. Trying to do this with realtime audio is probably not going to go super well.
73 de VE7XEN
He/Him
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14301
  • Country: fr
If you want exclusive access to a peripheral - servers like PulseAudio will potentially mix stuff from various sources - you can use ALSA. It's the most commonly available on Linux. It's not that hard to use - take a look at examples. https://www.alsa-project.org/alsa-doc/alsa-lib/examples.html
 

Offline wb0gazTopic starter

  • Regular Contributor
  • *
  • Posts: 190
Thanks for the helpful ideas!

I wandered into pulse audio and "pacat" and associated small programs.

The binary in my Ubuntu 20.04 distribution is somewhat larger (about 3x larger) than the binary I build from source from pacat-simple.c, but it seems like a reasonable start. No particular objection to using Ubuntu.

I hadn't considered at all the idea of piping directly to/from a device node...

Anyway, it seems like pacat_simple.c and parec_simple.c are very close to what I had in mind (I'd not heard of those before), so I'm going to play with them a little and see if I can just do the microphone-to-speaker transfer (chaining pacat_simple into parec_simple.

Thanks again for the helpful ideas and pointers,

Dave
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
Anything needing REAL TIME DSP at the moment needs JACK...

boils down to interfacing the ring buffers

Paul
 

Offline wb0gazTopic starter

  • Regular Contributor
  • *
  • Posts: 190
Thanks Paul -

Does one install JACK (daemon, I remember of it but haven't encountered it in some time) used in lieu of Pulse Audio (that came with Ubuntu linux) or alongside Pulse Audio?

I expect to use ring buffers sufficient to prevent underruns, but I haven't investigated the length/depth of the buffers needed to accomplish that.
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
Thanks Paul -

Does one install JACK (daemon, I remember of it but haven't encountered it in some time) used in lieu of Pulse Audio (that came with Ubuntu linux) or alongside Pulse Audio?

I expect to use ring buffers sufficient to prevent underruns, but I haven't investigated the length/depth of the buffers needed to accomplish that.

2 cents of jambo...

PulseAudio has a solely unique purpose of serving systemd panacea...
It is absolutely useless (as systemd is as well.. ) and against all real time applications ..
reason being is systemd doing ad hocs things (it should not) in the background

Both can/should be removed in full without loosing any type of functionality and with a great (much much great) improvement in security for a  dedicated DIGITAL SIGNAL PROCESSING machine...

As systemd  is deeply buried in the install embracing several dozens tools and compilation options.. it is mostly IMPOSSIBLE to be removed...  but PulseAudio is a tiny useless applet that can be removed safely...

Reason being is that the very same EGOtrip author of systemd is also the author of PulseAudio and both serve nothing for real in a  DSP machine... the PulseAudio just completes systemd...

A single safe SystemV init coupled with vanilla ALSA works 100 times fold better in any way...

JACK is a very optimized REAL TIME daemon which should be run with a considerable criteria setup.. done to integrate JACK alongside all the DSP pipes available via LADSPA...
It can handle several threads safely with a real time schedule almost flawless with proper setup

I have done an exercise some years ago trying to integrate my instrument widget set with SIGROK and JACK  and I was deeply surprised how damn well the JACK signal processor can handle data I/O of any kind for INSTRUMENTATION as well...   :popcorn:  wondering how high can raise frequency these days...

The setup requires in the very first place to ditch systemd because that thing is a REAL TIME PIG... it was not designed for real time applets or even DSP machines at all.. it serves other way different reasons.. the EGOtrip of the author...

Screenshot of my INSTRUMENTATION widgets integrated with JACK and SIGROK using a FULLY CAPABLE LADSPA  Plugin console...  it can be raised to almost 500kHz without changing a bit...

It took me about a week to write that thing... just a toy.. amazingly well how it works.  :popcorn:
The applet was written in plain GTK tripple buffered (all instruments..) each instrument with a separate thread and each DSP with own thread as well...

It can interface ANYTYPE of JACK device available in the system..
SCOPE + DMM + FUNCTION GENERATOR... with LADSPA  support..

Paul
« Last Edit: January 22, 2022, 01:55:31 pm by PKTKS »
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
 |O You get off my lawn folks are insufferable.

Quote
PulseAudio has a solely unique purpose of serving systemd panacea...
PulseAudio and systemd are unrelated.

Quote
It is absolutely useless (as systemd is as well.. ) and against all real time applications ..
It serves a useful purpose, and its analogue exists on every popular OS.

If you really hate it so much because of its connection to FDO or its developer(s), use pipewire, which doesn't change anything for implementing a client.

I agree that for a dedicated signal processing machine, JACK is more appropriate, being designed for that purpose, but is that the case here? It wasn't really described that way, especially since OP wants to use UNIX pipes to pass audio around. Using JACK will make that audio interface basically unusable for normal audio purposes.

Your rant doesn't help OP at all. If they want to run Ubuntu, trying to remove SystemD and PulseAudio is just a wild goose chase that will frustrate them and offer no useful benefit. You're proposing they basically replace their entire operating system in response to a simple question about audio I/O...

Quote
Does one install JACK (daemon, I remember of it but haven't encountered it in some time) used in lieu of Pulse Audio (that came with Ubuntu linux) or alongside Pulse Audio?

If you want to use JACK you will need to install it, and then make sure nothing else is using the audio device you intend to assign to JACK, as it takes exclusive control. Then you run the JACK daemon and bind it to the audio interface, and connect your clients to it. A management client like QJackCtl will help.

It depends on your ultimate goal which approach is more appropriate. If you want something that can integrate with your normal system, and maybe be usable by others in the same scenario, use PA. If you want to write some signal processing for live audio or professional musicians, use JACK... or write a LADSPA plugin, which would be even more flexible.
73 de VE7XEN
He/Him
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
(o my gosh..  no rant )

... In 2011 Poettering, one of the main developers of PulseAudio, ..
https://en.wikipedia.org/wiki/Lennart_Poettering

This folk  made the whoooole thingy...

Tried that a couple times..  for serious use... no way.
My 6y old nephew uses a POTTERIX  machine I made for him...

I like that
he likes that..
it is vanilla POTTERIX  made by me.. for him  to play games...

Nothing will change the fact that systemd + Pulse are crap...

And any JACK patch bay will make your life easier really
although not necessary..


Me using a POTTERIX machine.. unlikely
Paul

 

Offline wb0gazTopic starter

  • Regular Contributor
  • *
  • Posts: 190
Thanks - progress...

I am from the era of /etc/inittab, so I've not dealt with systemd either way and would rather not try to re-engineer that subsystem.

I *have* used jackd (long ago, when it happened to be part of a linux distribution I was experimenting with) and would be fine using it, but learning curve would be from zero as I didn't try to use it as an API/Library back then.

My question is this - should I remove pulse audio (sudo apt remove "something") then install jackd (sudo apt install jackd?)? To narrow down the OS environment - Ubuntu 20.04.3 LTS running on 64-bit X86 type machine; the OS install has nothing beyond whatever constitutes a "minimum install" plus build-essential.

My application doesn't have a latency or jitter requirement or specification (it is not for music or entertainment).

I will use a ring buffer as long (large) as is needed to avoid underrun conditions (think "buffering..." as the buffer fills to some set point before processing ensues).

The output (or processed) byte count will exactly match the input (unprocessed) byte count, so for example if 1000 bytes of audio samples arrive from the microphone/line in connector, 1000 bytes of audio samples will go back out to the speaker/line out connector. I will need to use stereo (left/right) so I'll actually have two concurrent (and equal rate/format) sample streams arriving then departing. Between the input (mic/line in) and output (spk/line out) API interfaces (however my "C" application would talk to jackd's library calls) would be some as-yet undefined processing (at a minimum, it would just echo each sample received back out, so audio arriving at the microphone would, after whatever processing delay, be delivered to the attached speaker.

Hope this helps better define the problem!

Thanks again for suggestions/ideas and/or pointers to reference/reading material or (ideally) sample source code.
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
Thanks - progress...

I am from the era of /etc/inittab, so I've not dealt with systemd either way and would rather not try to re-engineer that subsystem.

I *have* used jackd (long ago, when it happened to be part of a linux distribution I was experimenting with) and would be fine using it, but learning curve would be from zero as I didn't try to use it as an API/Library back then.
(..)

By making a very simple setup you can achieve very HIGH REAL TIME performance of JACK...
Just ditch everything you do not need to have all resources to JACK daemon...

Your machine will do DSP like real dedicated ones..

Been there several times.. have a boot setup myself for that..
I love that...  looking my old widgets performing so well

Paul
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
Thanks - progress...
(..)
Thanks again for suggestions/ideas and/or pointers to reference/reading material or (ideally) sample source code.

WELCOME !!!

Grab yourself the whole thing..  https://www.ladspa.org/

Very good  examples you can easily understand how to interface things..

Paul
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
My question is this - should I remove pulse audio (sudo apt remove "something") then install jackd (sudo apt install jackd?)? To narrow down the OS environment - Ubuntu 20.04.3 LTS running on 64-bit X86 type machine; the OS install has nothing beyond whatever constitutes a "minimum install" plus build-essential.

If you remove Pulse from an Ubuntu system, you will basically break audio entirely, all the system audio depends on it. I don't recommend doing that unless you want to go down that particular unprodcutive rabbit hole, or if this will be a machine dedicated to live audio processing and you don't care about normal system audio.

If you want to use JACK, I suggest you just stop Pulse while you are using it. I don't recall off the top of my head how Ubuntu starts PA, but `systemctl --user stop pulseaudio.service pulseaudio.socket` is probably sufficient to control it manually. You could also look into using `pasuspender` to automatically suspend PA while running jackd and restart it when you are done, I'm not sure if this tool is included in Ubuntu or not.

I'd suggest starting with the simplest case, which would be either PulseAudio or ALSA (or your original pipeline idea), and if you find you need/want better realtime performance, you can add JACK later. The complication will just make development more cumbersome, IMO.
73 de VE7XEN
He/Him
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
yeah.. the POTTERIX  thing is made to put everything under that..
It is a whole crippled thing from ground to top

You can not remove systemd..
You can not just use the plain ALSA like it should..
You can not just put JACK to run with all those things running...

Change your system.. and put proper things in place..

My nephew loves POTTERIX.. I like to see him happy..
But no chance in hell I will use that ...

The JACK code contains the best examples available..

Code: [Select]
/** @file simple_client.c
 *
 * @brief This simple client demonstrates the basic features of JACK
 * as they would be used by many applications.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <jack/jack.h>

jack_port_t *output_port1, *output_port2;
jack_client_t *client;

#ifndef M_PI
#define M_PI  (3.14159265)
#endif

#define TABLE_SIZE   (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
}
paTestData;

static void signal_handler(int sig)
{
jack_client_close(client);
fprintf(stderr, "signal received, exiting ...\n");
exit(0);
}

/**
 * The process callback for this JACK application is called in a
 * special realtime thread once for each audio cycle.
 *
 * This client follows a simple rule: when the JACK transport is
 * running, copy the input port to the output.  When it stops, exit.
 */

int
process (jack_nframes_t nframes, void *arg)
{
jack_default_audio_sample_t *out1, *out2;
paTestData *data = (paTestData*)arg;
int i;

out1 = (jack_default_audio_sample_t*)jack_port_get_buffer (output_port1, nframes);
out2 = (jack_default_audio_sample_t*)jack_port_get_buffer (output_port2, nframes);

for( i=0; i<nframes; i++ )
{
out1[i] = data->sine[data->left_phase];  /* left */
out2[i] = data->sine[data->right_phase];  /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
   
return 0;     
}

/**
 * JACK calls this shutdown_callback if the server ever shuts down or
 * decides to disconnect the client.
 */
void
jack_shutdown (void *arg)
{
exit (1);
}

int
main (int argc, char *argv[])
{
const char **ports;
const char *client_name;
const char *server_name = NULL;
jack_options_t options = JackNullOption;
jack_status_t status;
paTestData data;
int i;

if (argc >= 2) { /* client name specified? */
client_name = argv[1];
if (argc >= 3) { /* server name specified? */
server_name = argv[2];
int my_option = JackNullOption | JackServerName;
options = (jack_options_t)my_option;
}
} else { /* use basename of argv[0] */
client_name = strrchr(argv[0], '/');
if (client_name == 0) {
client_name = argv[0];
} else {
client_name++;
}
}

for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = 0.2 * (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.left_phase = data.right_phase = 0;
 

/* open a client connection to the JACK server */

client = jack_client_open (client_name, options, &status, server_name);
if (client == NULL) {
fprintf (stderr, "jack_client_open() failed, "
"status = 0x%2.0x\n", status);
if (status & JackServerFailed) {
fprintf (stderr, "Unable to connect to JACK server\n");
}
exit (1);
}
if (status & JackServerStarted) {
fprintf (stderr, "JACK server started\n");
}
if (status & JackNameNotUnique) {
client_name = jack_get_client_name(client);
fprintf (stderr, "unique name `%s' assigned\n", client_name);
}

/* tell the JACK server to call `process()' whenever
   there is work to be done.
*/

jack_set_process_callback (client, process, &data);

/* tell the JACK server to call `jack_shutdown()' if
   it ever shuts down, either entirely, or if it
   just decides to stop calling us.
*/

jack_on_shutdown (client, jack_shutdown, 0);

/* create two ports */

output_port1 = jack_port_register (client, "output1",
  JACK_DEFAULT_AUDIO_TYPE,
  JackPortIsOutput, 0);

output_port2 = jack_port_register (client, "output2",
  JACK_DEFAULT_AUDIO_TYPE,
  JackPortIsOutput, 0);

if ((output_port1 == NULL) || (output_port2 == NULL)) {
fprintf(stderr, "no more JACK ports available\n");
exit (1);
}

/* Tell the JACK server that we are ready to roll.  Our
* process() callback will start running now. */

if (jack_activate (client)) {
fprintf (stderr, "cannot activate client");
exit (1);
}

/* Connect the ports.  You can't do this before the client is
* activated, because we can't make connections to clients
* that aren't running.  Note the confusing (but necessary)
* orientation of the driver backend ports: playback ports are
* "input" to the backend, and capture ports are "output" from
* it.
*/
 
ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical|JackPortIsInput);
if (ports == NULL) {
fprintf(stderr, "no physical playback ports\n");
exit (1);
}

if (jack_connect (client, jack_port_name (output_port1), ports[0])) {
fprintf (stderr, "cannot connect output ports\n");
}

if (jack_connect (client, jack_port_name (output_port2), ports[1])) {
fprintf (stderr, "cannot connect output ports\n");
}

jack_free (ports);
   
    /* install a signal handler to properly quits jack client */
#ifdef WIN32
signal(SIGINT, signal_handler);
signal(SIGABRT, signal_handler);
signal(SIGTERM, signal_handler);
#else
signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
#endif

/* keep running until the Ctrl+C */

while (1) {
#ifdef WIN32
Sleep(1000);
#else
sleep (1);
#endif
}

jack_client_close (client);
exit (0);
}



API  at  http://jackaudio.org

It is just another level of things..
BUNTUs POTTERIX and today RedHat thingys are NICHES..

If you want serious things... just move on..

Paul
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
yeah.. the POTTERIX  thing is made to put everything under that..

You mean just like how running jackd will take exclusive control of the audio device? Any audio server will do this, because that's how the kernel's hardware API works. |O

Can you please take your misplaced ideological opposition to Pulse and Systemd elsewhere? It is not helpful to OP to have you suggesting the most convoluted path for their task, just because you don't like the way that modern Linux does things.

PulseAudio is the default audio framework for Linux machines today. Deal with it.

Quote
You can not just use the plain ALSA like it should..

You can, the ALSA API will work just fine on a (properly set up) PulseAudio system. So use that if you want, but since it's a wrapper, it doesn't really offer any advantages.

Quote
You can not just put JACK to run with all those things running...

You can use JACK as a backend for PulseAudio if you want. But this is just adding complication and for 99% of users has no benefit and several drawbacks. And of course, requires configuration and setup that is not helping OP achieve their rather simple goal of audio I/O on their existing system.

Quote
BUNTUs POTTERIX and today RedHat thingys are NICHES..
|O No. Any alternative is a niche. Mainstream Linux is systemd and PulseAudio, period.
73 de VE7XEN
He/Him
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br

No. Any alternative is a niche. Mainstream Linux is systemd and PulseAudio, period.

I have seen this slightly different..
Like

No more good alternatives.
Mainstream POTTERIX is the thing...

And MS is no longer that mainstream thingo

Paul
 

Offline Bassman59

  • Super Contributor
  • ***
  • Posts: 2501
  • Country: us
  • Yes, I do this for a living
No more good alternatives.
Mainstream POTTERIX is the thing...

You keep using that word.

Nobody knows what the fuck you're on about.
 

Offline PKTKS

  • Super Contributor
  • ***
  • Posts: 1766
  • Country: br
No more good alternatives.
Mainstream POTTERIX is the thing...

You keep using that word.

Nobody knows what the fuck you're on about.

Very simple..

The folk cited above (see wiki) is the author of systemd and pulse... 

He obviously created a whole new thing which diverts more and more from *nix..

Each month a new feature which nobody's ever asked for is pushed into that his system.. freakshow

Has gone so far that this new system which can no longer be called *nix receives his name.. kinda...POTTERIX

Has gone way too far... the folk created a new thing which obviously veered off trying to mimic MS mainstream look feel..

Kinda hype of IT  darlings.
I have seen this new system name on other discussions related to the serious problems caused by all those changes.. not new at all

Paul

PS>  another interesting comment i have read about it is that POTTERIX is like Arduino for  IT..  a newbie thinks that is great.. fantastic magic..

Anything above ground zero requires a proper MCU or system...

So POTTERIX is like Arduino..
My 6y old nephew loves it and Arduino
« Last Edit: January 26, 2022, 09:34:29 am by PKTKS »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf