Csound Csound-dev Csound-tekno Search About

Re: how to REALLY pipe -L events in linux/unix

Date1998-06-18 18:44
FromPaul Winkler
SubjectRe: how to REALLY pipe -L events in linux/unix
Thanks for all the suggestions, everyone. The nonexistant prize goes to 
Jens Kilian whose suggestion to use fflush() was exactly what was needed 
to get things working the way I wanted.

Here's some sample code that does a (meaningless?) benchmark of realtime 
csound output. Watch out for sneaky email line wraps...
Sorry for the length of this message, but I think some of the issues 
raised are interesting.

(first the orchestra)
sr          =           44100
kr          =           4410
ksmps       =           10
nchnls      =           1

            instr 1
;p4 = freq of fundamental (Hz)
;p5 = amp (dB)
iamp = ampdb(p5)
a1      oscil  iamp, p4, 1     
out a1
endin

			instr 2
; same but using oscili
;p4 = freq of fundamental (Hz)
;p5 = amp (dB)
iamp = ampdb(p5)
a1      oscili  iamp, p4, 1     
out a1
endin

;now the sco
f1 0 32768 10 1 ; great big sinusoid table.
f0 1000 ; dummy line to keep score active for a long time.
e

And here's the C code...
/*******************************
 Some code to benchmark realtime csound output.
This version sends only 1 note at a time; each note sticks around for a 
long time. 
***************************/
#include 
#include 
#include 
#include 
#include 

#define ORCFILE "/home/pw/Orcs+Scores/osciltest.orc" 
#define SCOFILE "/home/pw/Orcs+Scores/osciltest.sco"
#define DUR 2

int i, pitch, volume, initpitch, initnum, initvol, instr, sndbufsize;
FILE *ofp;
char csoundcmd[256];

int main(void)
{  /**** Got tired of typing these over and over...
  printf("Give starting pitch in integer hz:  ");
  scanf("%d", &initpitch);
  printf("Give starting volume in integer db:  ");
  scanf("%d", &initvol);
  ******/
  printf("Give initial number of oscillators:  ");
  scanf("%d", &initnum);
  printf("Use oscil or oscili? [oscil = 1, oscili = 2] ");
  scanf("%d", &instr);
  printf("Buffer size:  ");
  scanf("%d", &sndbufsize);

  pitch = initpitch = 100;
  volume = initvol = 70;
    
  /* Now let's make a pipe to write notes to... */
  
  sprintf(csoundcmd, "csound -m 2 -d -L stdin -b %d -odevaudio %s %s > 
/dev/null", sndbufsize, ORCFILE, SCOFILE); 
  /***** Note that I'm only playing with -b, not -B. I haven't observed 
any benefits from changing -B yet, but maybe I'm not looking hard 
enough... ******/
  ofp = popen(csoundcmd, "w");
  sleep(3);
  assert(ofp != NULL);
  printf("Opened pipe to Csound.\n");
  for(i= 1; pitch < 22050 && volume > 0 ; ++i, pitch += initpitch, 
volume -=1){
	  fprintf(ofp, "i%d 0 1000 %d %d\n", instr, pitch, volume);
	  fflush(ofp );
	  printf("%d oscillators, oh my...\n", i);
	  	  if (i >= initnum) {
      /* nicely suspend for 2 seconds after each score event */
			  sleep(DUR);
		  }
   } 
	pclose(ofp);
	return 0;
}


I was curious if there was a limit to how much I could dump at csound at 
once, so I made the following version to play with that; it's the same 
up until after the pipe is open, except we also declare 
int tones;
...and then after the pipe's open it reads like this:

/************************* 
This version also tests dumping a whole lot of notes of duration DUR to 
stdin of csound at once. This illustrates that popen() to stdin as 
a method of csound control is not very satisfactory; with just a few 
score events you can clearly hear them entering at different times, and 
eventually (around 23 oscils for me) csound just goes silent!
The problem seems to be with the pipe (I explored this by substituting 
printf for fprintf), but I'm not sure if it can be improved, perhaps by 
tuning the pipe's buffer size; or if it's just in the nature of pipes...
or maybe there's a limit to how many lines csound can receive from stdin 
at once?
******************************/

  /*** 
Start with initnum voices. Each note adds 1 voice. Each voice is the 
next overtone in the series, and each voice is 1 db softer than the 
previous. 
  ***/
  
  for(tones = initnum; ; ++tones) {
    for (i = 0, pitch = initpitch, volume = initvol; i < tones ; ++i) {
      fprintf(ofp, "i%d 0 %d %d %d\n", instr, DUR, pitch, volume);
      pitch += initpitch;
      --volume;
    }
    fflush(ofp); /* dump this batch of tones all at once */
    printf("%d oscillators, oh my...\n", i);
    sleep(2); /* wait 2 seconds after each batch of notes. */
  }
  
  pclose(ofp);	
  return 0;
}

Finally, I was noticing latency issues with various buffer sizes, so I 
made this version which lets you really feel the latency.

Again, this is the same as the first version up until after the pipe's 
open. Oops, up top we need to declare char noteline[reasonable size]....

/********** Some code intended to test the effects of the -b buffersize 
on realtime csound output.

It seems to be quite acceptable at values around 256, or even 512, but
conversely this somewhat limits what csound can do... at 1024 the delay 
is easy to feel and would probably be annoying to try to "play" in 
realtime. At 2048 the delay is probably in the range of 100 ms.
For a realtime interactive csound frontend app to be really
"playable" I would guess the latency should be more in the range of
10 ms. Some testing suggests that csound on my system gives most 
reliable output with the buffer around 8192 which gives latency of about 
half a second. 
Yuck!!!
*************/

  printf("Enter S to hear a sound, ctrl-c to exit.\n");
  
  /* Get the note events ready. */  
  for (i = 0, pitch = initpitch, volume = initvol; i < initnum ; ++i) {
	  sprintf(nextnote, "i%d 0 %d %d %d\n", instr, DUR, pitch, volume);
	  strcat(noteline, nextnote);
	  pitch += (initpitch * 1.1); /*got bored of harmonic stuff */
	  --volume;
  }  
  /*** Try to send note events as soon as ENTER is pressed. **/
  while ((c = getchar())) {
	  if (c == 's' || c == 'S') {
			  fprintf(ofp, "%s", noteline);
			  fflush(ofp); /* dump this batch of tones all
                                          at once */
		  }
	 }
	  
  pclose(ofp);	
  return 0;
}

end of blahblah.

regards,

PW

______________________________________________________
Get Your Private, Free Email at http://www.hotmail.com

Date1998-06-19 16:04
FromNicola Bernardini
SubjectRe: several messages
On Thu, 18 Jun 1998, Paul Winkler wrote:

[snip]
> >And forget sockets, that's
> >another story - pipes are fine and they work really well for the kind
> >of things you want to do.
> 
> I've heard this a couple of times now, so OK, no sockets for me. :)

sorry, I did not mean to be harsh: the point is that sockets mean
setting up services, client-server communication etc. etc. I know how
to do that and I am ready to help if there is a real need, but this
does not seem to be the case. Really, bi-directional (named) pipes or IPC
communication should be more than enough (in fact, we are discussing
uni-directional ones right now...).

Now, speaking of 'csound -L stdin',

On Thu, 18 Jun 1998, Paul Winkler wrote:

[snip]
> csound -L stdin is what the manual says to do, and it works.

did *NOT* work for me at all as of 3.482. I investigated a bit in the sources
and found out:

1) indeed, "stdin" as a name is taken into consideration and opens and
   read _io_[0] (stdin, that is)

2) there is a commented out 'if' statement at musmon.c line 183 that
   blocks out the reading from stdin. There is no comment about why it
   is commented out (but read further).

3) removing the comments 'csound -L stdin' *somewhat works*. The behaviour
   is actually fairly funny. This works:

	echo -e "f1 0 4096 10 1\ni1 0 10" |\
	csound -b8192 -L stdin -do devaudio oscil.orc

   although csound does'nt stop after the 10 seconds, it needs a Ctrl-C;
   but this does not!

	cat oscil.sco |\
	csound -b8192 -L stdin -do devaudio oscil.orc

   (of course, oscil.sco was made out of 'echo -e ... > oscil.sco', so the
   stuff piped to csound is identical) - further enquiry and debugging showed
   that csound is actually blocking at the *output* writing, not in the input;
   The problems are:

   a) I don't see how a unix application (in this case csound) knows what
      is coming on the other side of the pipe (why 'echo' works and 'cat'
      does not; btw, also 'csound -L stdin etc. etc. < oscil.sco' does not
      work)

   b) what has the output to do with the different inputs

   now this completely surpasses my unix chops and my intellectual
   capabilities, so I gave up and went back to study fcntl() and all its
   implications. That's enough for me...

4) on the positive side of things, I tried something else:
   
   mkfifo /tmp/fifo; # so I don't forget to mention
   cat oscil.sco >> /tmp/fifo & csound -do devaudio oscil.orc /tmp/fifo

   this works no matter what you use to fill /tmp/fifo; no '-L stdin' etc.
   involved - ideed named pipes work very well under linux ;-)

On Thu, 18 Jun 1998, Paul Winkler wrote:

[snip]
> Thanks for all the suggestions, everyone. The nonexistant prize goes to 
> Jens Kilian whose suggestion to use fflush() was exactly what was needed 
> to get things working the way I wanted.

yes of course. Btw, somebody else suggested to zero out the kernel buffers.
That means to do an ioctl() call for every character, is'nt it? That'll
cost some time...

Thanks Paul for pushing us all to investigate... As far as I can see
the problem is far from solved.

[snip]
>   /***** Note that I'm only playing with -b, not -B. I haven't observed 
> any benefits from changing -B yet, but maybe I'm not looking hard 
> enough... ******/

...mmm... I saw that too... seems broken, is'nt it?

Nicola
------------------------------------------------------------------------
Nicola Bernardini
E-mail: nicb@axnet.it
 
Re graphics: A picture is worth 10K words -- but only those to describe
the picture.  Hardly any sets of 10K words can be adequately described
with pictures.