Version 7 of Making mathematical waves

Updated 2007-07-03 09:54:01 by theover

Theo Verelst

When making sounds, the most fundamental

This is where Tcl comes in to make a usefull script which executes exactly the right commands to let us do that. Not a very big script (not counting bwise) but one which calls two major applications (maxima and gcc), and which does the essential packing of a fortan function in a suitable file, and finally automatically executes the compiles program, which writes a standard wav file (see snack) to play with some media player.

Maxima can be integrated with tcl in two main ways, see and . Here I use the easier programmable and memory friendly way to start up the executable from the tcl (bwise) script.

This is the main tcl script, which is combined with (sourced) bwise:

 proc domaxima { {m} } {
    set t "display2d:false;\n$m;"
    return [string range [exec maxima << $t | tail -2  ] 6 end-7]
 } 

 proc domaximafor { {m} } {
    set t "display2d:false;\nlinel:3000;\nfortran($m);\n"
    return [string range [exec maxima -q << $t ] 42 end-18]
 } 

 proc formake { {e} } {
   global f
   set t [subst -nocommand {
      subroutine sayhello(x,r)
        real x,r
        r = $e
        return
      end
   }]
   set f [open sub.f w]
   puts $f [string trim $t \n]
   close $f
   exec gcc -ffixed-line-length-none -o fm sub.f mw.c wav.o -lm
   #exec gfortran -ffixed-line-length-none -c sub.f
   #exec gcc -o fm sub.o main.c -lm
   return [exec ./fm]
 } 

Of course maxima must be present on the system and reachable according to the PATH shell variable (like wish and tclsh). I've done this setup on Linux, which when set up right gives excellent maxima and compile times, with complicated formulas (see below) a sceonds long wav file is created in under a second (!). I guess this tcl/maxima/fortran/C setup is therefore usefull for general application, too.

You need to have these files in the current directory which contains the wav file and C loop part of the target program:


/* wav.c */

 #include <stdio.h>

 #include <fcntl.h>

 #include <sys/stat.h>

 #include <string.h>



 #include <sys/types.h>

 #include <stdlib.h>

 /* #define CYGWIN */



 int fd;

 FILE *fp;



 #define MSEC(t) (int)(t*44.1)



 iwrite(fd,n,l)

 int fd,l;

 unsigned int n;

 {

    write(fd,&n,l);

 }



 int initwav(s,l)                 /* wav header, s=filename, l=#samples */

 char *s;

 int l;

 {

 #ifdef CYGWIN

    fd = open(s,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IRUSR|S_IWUSR|S_IRGRP);

 #else

    fd = open(s,O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU);

 #endif

    if (fd < 0) return(-1);



    write(fd,"RIFF",4);

    iwrite(fd,(2*l+36),4);

    write(fd,"WAVE",4);



    write(fd,"fmt ",4);

    iwrite(fd,(0x10),4);

    iwrite(fd,((short) 0x01),2);

    iwrite(fd,((short) 1),2);              /* Mono */

    iwrite(fd,(44100/1),4);                  /* Sample rate */

    iwrite(fd,(2*44100/1),4);

    iwrite(fd,((short) 2),2);

    iwrite(fd,((short) 16),2);



    write(fd,"data",4);

    iwrite(fd,(2*l),4);

    return(0);

 }





 void writewav(p,n)

 short *p;                      /* Sample values */

 int n;                         /* #samples      */

 {

    int i;



    for (i=0; i<n; i++)

       write(fd,&p[i],2);

 }



 void closewav()

 {

    close(fd);

 }


 /* mw.c */

 /* This is file: main.c */
 #include<stdio.h>
 #include<math.h>

 extern void sayhello_(float *, float *);
 extern int initwav(char *,int);
 extern int writewav(short *, int);
 extern void closewav();

 int main(argc, argv)
 int argc;
 char *argv[];
 {
   float in, out;
   float x, start, stop, incr;
   short s;

   if (argc == 1) {
      start = 0.0;
      stop = 3.0;
      incr = 1.0/44100.0;
    if (initwav("math.wav",3*44100) != 0) return((int) -1);
    for (x=start; x<(stop-incr/2); x+=incr) {
      in = x;
      sayhello_(&in,&out);
      /* printf("%f %f\n", x, (float) out); */
      s = (short) (32000*out);
      writewav(&s,1);
    }
    closewav();
   }
   return((int) 0);
 }

Running the script with a complicated formula (using commands, not BWise blocks):

 formake [domaximafor {(sin(6.2831*110*x)*exp(-2*x)+(1/2)*sin(2*6.2831*110*x)*exp(-4*x)+(1/3)*sin(3*6.2831*110*x)*exp(-6*x)+(1/4)*sin(4*6.2831*110*x)*exp(-8*x)+(1/5)*sin(5*6.2831*110*x)*exp(-10*x)+(1/6)*sin(6*6.2831*110*x)*exp(-12*x)+(1/7)*sin(7*6.2831*110*x)*exp(-14*x)+(1/8)*sin(8*6.2831*110*x)*exp(-16*x)+(1/9)*sin(9*6.2831*110*x)*exp(-18*x))/(1+(1/2)+(1/3)+(1/4)+(1/5)+(1/6)+(1/7)+(1/8)+(1/9))}]

THis is the resulting wav file

Finally, it is also possible to read the resulting wav file in a sampler or sample software and 'play' it with different keys on a (usb or midi) keyboard, which is great fun.

Needless to say, because of the block facilities or bwise and general tcl/tk possibilities, a powerfull environment can be made.