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
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
If you want to send the audio in C, which is totally different than your 'preferred' pipe solution, then PulseAudio (https://www.freedesktop.org/wiki/Software/PulseAudio/) is probably the way to go on modern Linux, it's almost always the default audio server. The simple API is pretty straightforward:
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.
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..
/** @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 (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