Something's odd here...
I'm trying to de-interleave the stereo output from sfront
into discrete channel buffers suitable for jack. Everything's
fine when number of channels = 1; but when it's 2, it seems
that I get the left channel in both outputs. I've confirmed
that the left and right outputs are different when running
with oss audio output instead of jack.
I have a buffer called tempbuf, which contains nframes channel-interleaved
sample frames. Call it tempbuf. e.g.:
long nframes;
long nsamples = nframes * ASYS_OCHAN;
/* ASYS_OCHAN is the number of channels */
float* tempbuf = (float*) calloc(nsamples, sizeof(float));
next, I set up an array of buffers into which I want to put
the deinterleaved output:
float* obuf[ASYS_OCHAN];
for(i=0; i < ASYS_OCHAN; i++) {
obuf[i] = (float*) calloc(nframes, sizeof(float));
/* actually I use jack_port_get_buffer() instead of calloc... */
/* and actually there are various typedefs in use, not float */
}
That's all fine, as far as I can tell. Now here's the de-interleaving
code:
for(i=0; i < nsamples; i++) {
obuf[i % ASYS_OCHAN][i / ASYS_OCHAN] = tempbuf[i];
}
The effect this *should* have (when ASYS_OCHAN = 2):
obuf[0][0] = tempbuf[0]
obuf[1][0] = tempbuf[1]
obuf[0][1] = tempbuf[2]
obuf[1][1] = tempbuf[3]
obuf[0][2] = tempbuf[4]
obuf[1][2] = tempbuf[5]
...
... but I'm hearing the left output on both channels.
Have I made some silly mistake in the de-interleaving?
Any other ideas on what I could've done wrong?
Complete actual code attached if anyone wants to see more
context...
--
Paul Winkler
home: http://www.slinkp.com
"Muppet Labs, where the future is made - today!"
/* TO TO:
- do asys_isetup and asys_iosetup.
- FIgure out why 2-channel output is working but sounds
totally fucked up.
- buffer size change:
jack_get_buffer_size() can be called ONLY before client activation
to find out current max buffer size.
While running, if jack changes buffer size, it calls function registered
with jack_set_buffer_size_callback.
On the sfront side:
asys_orun's 2nd argument is a pointer to long whose value represents
the max number of sample periods available.
Hmm, I'm not sure I need to register a function with jack;
looks like sfront can handle any buffer size at any time
(as long as it is a multiple of the number of channels).
*/
/*
# Sfront, a SAOL to C translator
# This file: jackd audio driver for sfront
# copyright (c) 2002 Paul M. Winkler, Brooklyn, NY
# www.slinkp.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (Version 2) as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Maintainer: Paul Winkler www.slinkp.com
*/
/* includes needed for JACK */
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <jack/jack.h>
/* include <glib.h> */
/* defines needed by sfront */
#define ASYSN_JACK_DEBUG 1 /* debugging printouts */
#define ASYSN_JACK_SLEEPMS 5 /* exit interval */
/* declarations needed for jack */
jack_port_t *asysn_jack_input_port[ASYS_ICHAN] ;
jack_port_t *asysn_jack_output_port[ASYS_OCHAN];
jack_client_t *asysn_jack_client;
/* declarations needed for sfront */
volatile int asysn_jack_proc_status; /* used to signal status */
/* my own stuff */
char asysn_jack_client_name[256] = "SAOL Test Client";
int asysn_jack_srate_was_called = 0;
int asysn_jack_oports_connected = 0;
int asysn_jack_iports_connected = 0;
void asysn_jack_print_usage(void);
void asysn_jack_print_usage(void) {
fprintf(stderr, "Usage: ... write useful blahblah here\n");
}
/* callback functions used by jack in various scenarios */
/** OUTPUT ONLY **/
#if defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT)
int asysn_jack_process (jack_nframes_t nframes, void * arg )
/* cast arg to pointer to whatever I need. */
{
/* OUTPUT ONLY */
/* vars. copied from portaudio driver....
but this is my jack process() function, which only gets
a nframes & a pointer-to-void argument.
*/
long nsamples = (long) nframes * ASYS_OCHAN;
long oremaining = nsamples ;
long optr = 0;
long osize;
jack_default_audio_sample_t* obuf[ASYS_OCHAN];
ASYS_OTYPE* tempbuf;
int i, j;
if (asysn_jack_srate_was_called >1) {
/* force jack to remove this client as per PBD message on jackit-devel
on 4/24/02 (thread: "stereo?")
*/
printf("uh-oh... called srate again, and we can't handle it\n");
return 1;
}
/* printf("calling process...\n"); */
/* Get memory buffers associated with the jack output ports. */
for (i=0; i< ASYS_OCHAN; i++) {
obuf[i] = (jack_default_audio_sample_t *)
jack_port_get_buffer(asysn_jack_output_port[i],
nframes);
}
/* sfront wants one interleaved buffer, so we need a separate one for that */
tempbuf = (ASYS_OTYPE *) calloc(nsamples, sizeof(ASYS_OTYPE));
/* Keep running while result is not ASYS_EXIT */
while ((asysn_jack_proc_status == ASYS_DONE) && (oremaining > 0)) {
osize = oremaining;
/* WHITE NOISE TEST */
/* output should be random between -0.5 and 0.5 */
/* for(; optr < osize; optr++) {
tempbuf[optr]= ((float) rand()) / RAND_MAX - 0.5;
}
*/
/* asys_orun is defined by sfront */
/* According to the sfront manual, it "writes at most */
/* the next *osize channel-interleaved sample values */
/* into the buffer". */
/* remember that asys_orun will leave osize set to the
actual number of samples that it wrote! */
asysn_jack_proc_status =asys_orun(tempbuf, &osize);
oremaining -= osize;
//if (oremaining == 0) {
// printf("wrote: %d\n", osize);
//}
//else {
//printf("remaining: %d written: %d\n", oremaining, osize);
// we didn't write into the whole buffer! why not?
// look at what portaudio driver does here.
// I think it fills the rest with zeroes...
//}
/* DE-INTERLEAVE */
for(i=0; i < nsamples; i++) {
// cast is probably a no-op but can't hurt.
obuf[i%ASYS_OCHAN][i/ASYS_OCHAN] =
(jack_default_audio_sample_t) tempbuf[i];
}
free(tempbuf);
}
return 0;
}
#endif
/** INPUT ONLY **/
#if defined(ASYS_HASINPUT) && !defined(ASYS_HASOUTPUT)
int asysn_jack_process (jack_nframes_t nframes, void * arg)
{
/* INPUT ONLY */
printf("I");
return 0;
}
#endif
/** INPUT AND OUTPUT **/
#if defined(ASYS_HASINPUT) && defined(ASYS_HASOUTPUT)
int asysn_jack_process (jack_nframes_t nframes, void *arg)
{
/* INPUT AND OUTPUT*/
printf("IO");
return 0;
}
#endif
/* FUNCTIONS to REGISTER WITH JACK */
int asysn_jack_bufsize(jack_nframes_t nframes, void *arg) {
/* looks like this gets called once when we start up. */
printf("can I set buffer size in an sfront program?\n");
return 0;
}
int asysn_jack_srate (jack_nframes_t nframes, void *arg) {
/* looks like this gets called once when we start up. */
printf("Can't adjust srate at runtime in an sfront program.\n");
// set a flag which will force us to be removed by jackd
asysn_jack_srate_was_called++;
return 1;
}
void asysn_jack_shutdown(void *arg)
{
// called if jack every shuts down or decides to stop me
jack_client_close(asysn_jack_client);
exit (1);
}
/* SFRONT INITIALIZATION FUNCTIONS */
/* These are called ONCE at program startup. */
/** OUTPUT ONLY **/
#if defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT)
int asys_osetup(long srate, long ochannels,
long osample, char * oname, long toption)
{
char outchan_name[256];
int i, j;
/* connect as client to jack server */
if ((asysn_jack_client = jack_client_new (asysn_jack_client_name)) == 0) {
fprintf( stderr, "could not connect... jack not running?\n");
return ASYS_ERROR;
}
/* register asysn_jack_process() with jack.
will be called whenever jack decides there's work to do.
*/
jack_set_process_callback(asysn_jack_client, asysn_jack_process, 0);
/* register bufsize() with jack, to be called when max. nframes
will change
*/
jack_set_buffer_size_callback(asysn_jack_client, asysn_jack_bufsize, 0);
/* register srate() as function for jack to call when sampling
rate changes
*/
jack_set_sample_rate_callback (asysn_jack_client, asysn_jack_srate, 0);
/* register for anytime we get disconnected,
i.e. jack shuts down or stops calling asysn_jack_client
*/
jack_on_shutdown(asysn_jack_client, asysn_jack_shutdown, 0);
/* print sampling rate at startup */
printf("jack sampling rate: %lu\n", jack_get_sample_rate(asysn_jack_client));
printf("OK ");
/* create ports */
printf("creating output port(s)\n");
for(i=0; i < ASYS_OCHAN; i++) {
asysn_jack_output_port[i] = jack_port_register(asysn_jack_client,
"output",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
}
/* tell jack asysn_jack_client is ready */
if (jack_activate (asysn_jack_client)) {
fprintf(stderr, "can't activate client!\n");
return ASYS_ERROR;
}
else
printf("client activated OK\n");
/* now we can connect ports */
/* if (jack_connect (asysn_jack_client, "alsa_pcm:in_1", */
/* jack_port_name(asysn_jack_input_port))) { */
/* fprintf(stderr, "can't get input ports!\n"); */
/* } */
/* PROCESS COMMAND-LINE ARGS */
for(i=1; i < asys_argc; i++) {
/* connect to requested output ports */
if(! strcmp(asys_argv[i], "-asys_jack_out")) {
if (((i + 1) >= asys_argc)) { // || (strncmp(asys_argv[i+1], "-", 1))) {
asysn_jack_print_usage();
return ASYS_ERROR;
}
for(j=i+1; j < asys_argc && strncmp(asys_argv[j], "-", 1); j++) {
if (j-i > ASYS_OCHAN) {
fprintf(stderr,
"Too many out channels requested! %s can only do %d\n",
asys_argv[0], ASYS_OCHAN);
return ASYS_ERROR;
}
if (jack_connect (asysn_jack_client,
jack_port_name(asysn_jack_output_port[j-(i+1)]),
asys_argv[j])) {
fprintf(stderr, "can't get output ports!\n");
fprintf(stderr, "failed trying to connect output %d to %s\n",
j - (i+1),
asys_argv[j]);
return ASYS_ERROR;
}
asysn_jack_oports_connected++;
}
}
/* connect to requested input ports */
else if (! strcmp(asys_argv[i], "-asys_jack_in")) {
}
}
/* connect remaining output ports to alsa client by default */
/* ... whoops, what if user explicitly used one already? Forget it. */
/* for(i=asysn_jack_oports_connected; i < ASYS_OCHAN; i++) { */
/* sprintf(outchan_name, "alsa_pcm:out_%d", i + 1); */
/* if (jack_connect (asysn_jack_client, */
/* jack_port_name(asysn_jack_output_port[i]), */
/* outchan_name)) { */
/* fprintf(stderr, "can't get output port %d!\n", i); */
/* return ASYS_ERROR; */
/* } */
/* } */
if (asysn_jack_oports_connected != ASYS_OCHAN) {
fprintf(stderr, "Oops! You've connected %d out channels and you need %d.\n",
asysn_jack_oports_connected, ASYS_OCHAN);
return ASYS_ERROR;
}
printf("asys_osetup is all done!\n");
/* all is well */
return ASYS_DONE;
}
#endif
/**************** END OF SETUP **************************/
/******* shutdowns called in various scenarios *******/
#if defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT)
void asys_oshutdown(void) {
printf("running asys_oshutdown.\n");
};
#endif
#if defined(ASYS_HASINPUT) && !defined(ASYS_HASOUTPUT)
void asys_ishutdown(void) {
printf("running asys_ishutdown.\n");
}
#endif
#if defined(ASYS_HASINPUT) && defined(ASYS_HASOUTPUT)
void asys_ioshutdown(void) {
printf("running asys_ioshutdown.\n");
}
#endif
/***** active audio main - works for all I/O types, I hope ************/
/* Called once by sfront app. *after* setup; when it terminates, we're done.
and we run asys_[io]shutdown.
*/
void asys_main(void) {
int i=0;
printf("Started asys_main...\n");
asysn_jack_proc_status = ASYS_DONE;
/* Run until ASYS_EXIT... */
while (asysn_jack_proc_status == ASYS_DONE) {
/*printf("now in main loop %d\n", i);
i++;*/
usleep(ASYSN_JACK_SLEEPMS * 1000); // doesn't seem to matter
}
jack_client_close(asysn_jack_client);
/* return(0); */
}