Back to main page
Back to sound contents

Creating waveforms with Maxima

This section shows the principal functions and sound options available in package sound.

Since we want to play the sound and draw the waveform everytime we call the play function, we set some sound options defaults to avoid rewriting them.

set_sound_defaults(
   player    = "vlc",
   draw_wave = true,
   draw_wave_options = [terminal   = png,
                        pic_height = 200,
                        pic_width  = 800] )$

A note for Windows users: there are probably better ways for playing wav files from Maxima, but these steps worked for me:

  1. download Media Player Classic from sourdeforge.net/projects/guliverkli2,
  2. allocate the exe binary in c:\Program Files\Maxima-x.y.z\bin,
  3. Make use of option player = "mplayerc" or player = "mplayerc /close" in Maxima's play command.
I have tried options like player = "start /min", which theoretically should call the default wav player, but they didn't work, since this command doesn't accept long directory names with spaces.


Let's begin with a sinusoidal waveform. Function play is called here with only one argument, namely sound object wave, which in turn is defined as a sinusoidal signal of frequency 440 cycles per second (440 Hz) from time t1 = 0 s to t2 = 0.5 s. This pure wave sound corresponds to A4, key A of the fourth octave; in Latin speaking countries (Spain, France, Italy) and others (Russia) this key is called Do4.

We get three results from the following function call:

play ( wave(1000 * sin(2*%pi*440*t), t, 0, 0.5) );
Download wav file

A side effect of function play is that the resulting wave is stored in global variable sound_sample, which is a lisp 2d array, containing the samples of the channels:

(%i23) /* number of channels */
       sound_sample_channels();
(%o23)                                 1

(%i24) /* sample size */
       sound_sample_size();
(%o24)                               8192

(%i25) /* sample of channel #1 in Maxima list format */
       sound_sample_list (1);
(%o25) [0.0, 5502.834111437016, 10849.36032083387, 15887.71064924192, 
        .................................
        - 10849.36032082994, - 5502.834111438126]

(%i26) /* list length is 8192, of course */
       length(%);
(%o26)                               8192

(%i27) /* 3rd sample of channel #1,
          indexing starts at 0   */
       sound_sample[0,2];
(%o27)                         10849.36032083387

Waveforms are saved in wav files in 16 bits per sample. By default, samples are normalized to range [-32767, 32767], but this behavior can be changed setting sound option normalize to none or any positive integer less or equal than 32767; default is auto.


In this example, we add two waveforms of different frequency (A4 and A3, or Do4 and Do3) and amplitude.

play ( wave(1000 * sin(2*%pi*440*t) + 
            500  * sin(2*%pi*220*t), t, 0, 0.5) );
Download wav file

Now we want to divide our signal in three parts:

  1. A tone of decreasing amplitude during 1/2 seconds,
  2. 1/5 seconds of silence,
  3. A tone of increasing frequency during 1 second.

Note that in this example the sound_sample_rate (samples per second) is set to 20000 (default is 16384). The fifth argument to the second wave object indicates when the sound must start; by default, with only four arguments, the sound starts at t = 0. See also this and this examples.

sample_rate : 20000 $
play (
   wave((1000 - 2000*t) * sin(2*%pi*440*t), t, 0, 1/2),
   wave(1000*sin(2*%pi*(2700*t + 300)*t), t, 0, 1, 1/2 + 1/5));
Download wav file

Sound object wave accepts more than four arguments. In this example, the same wave, of duration 1 second, is repeated two times, beginning at t = 0 and 1/2 seconds. In the overlapping interval, [1/2, 1] the waves are added. The total signal has a duration of 1.5 seconds.

play (
   wave(1500 * sin(2*%pi*440*t), t, 0, 1, 0, 1/2) );
Download wav file

Perhaps one wants the samples to be saved in a plane text file for further processing. In this case, make use of option file_format = txt; by default it is equal to wav.

Note that whenever the play function is called, the generated waveform is stored in the Lisp array sample_array. But we don't want to make use of this array this time.

This example also demonstrates how to save computing time. We want tone A4 for 1 second, but we tell Maxima to only compute 0.25 seconds (from 0 to 0.25); after that, Maxima must repeat the same waveform at t = 0, 0.25, 0.5 and 0.75, that is one second in total.

play (
   file_format = txt,
   wave(1000 * sin(2*%pi*440*t), t, 0, 0.25, 0, 0.25, 0.5, 0.75) ) $
Download plane text file
/* Read file as a Maxima list */
s: read_list("maxout.txt")$

/* Divide amplitude by 2 and draw */
draw2d(
   points_joined = true,
   point_size    = 0,
   points(s / 2))$

Dial tons. Or is this R2-D2?

random_dial_call (num) :=
  block([n, t, dial_tones, amp : 1000],
        dial_tones : [/* 1= */ (sin(4379*t)+sin(7596*t))/2,
                      /* 2= */ (sin(4379*t)+sin(8394*t))/2,
                      /* 3= */ (sin(4379*t)+sin(9280*t))/2,
                      /* A= */ (sin(4379*t)+sin(10260*t))/2,
                      /* 4= */ (sin(4838*t)+sin(7596*t))/2,
                      /* 5= */ (sin(4838*t)+sin(8394*t))/2,
                      /* 6= */ (sin(4838*t)+sin(9280*t))/2,
                      /* B= */ (sin(4838*t)+sin(10260*t))/2,
                      /* 7= */ (sin(5353*t)+sin(7596*t))/2,
                      /* 8= */ (sin(5353*t)+sin(8394*t))/2,
                      /* 9= */ (sin(5353*t)+sin(9280*t))/2,
                      /* C= */ (sin(5353*t)+sin(10260*t))/2,
                      /* *= */ (sin(5912*t)+sin(7596*t))/2,
                      /* 0= */ (sin(5912*t)+sin(8394*t))/2,
                      /* #= */ (sin(5912*t)+sin(9280*t))/2,
                      /* D= */ (sin(5912*t)+sin(10260*t))/2],
        n : length (dial_tones),
        makelist(wave(amp * dial_tones[1 + random(n)], t, 0.0, 0.1, 0.2*(k-1)), k, 1, num)) $

apply(
   play, random_dial_call (9)) $
Download wav file

Back to main page
Back to sound contents


by Mario Rodríguez Riotorto