Tuesday, August 20, 2013

Audio File Reading and Writing

I have implemented audioinfo and audioread and they should work exactly as in MATLAB. Here is an example session.

octave:1> audioinfo('technical_difficulties.wav')
ans =

  scalar structure containing the fields:

    Filename = technical_difficulties.wav
    CompressionMethod =
    NumChannels =  2
    SampleRate =  44100
    TotalSamples =  305576
    Duration =  6.9292
    BitsPerSample =  16
    BitRate = -1
    Title =
    Artist =
    Comment =

octave:2> [y, Fs] = audioread('technical_difficulties.wav');
octave:3> player = audioplayer(y, Fs);
octave:4> play(player);
For further information about this functionality refer to MATLAB manual pages on these functions: here and here.

Sunday, August 18, 2013

Further Development

Since we already have a working version of the audioplayer and audiorecorder functionality that allows one to write callbacks in Octave here is my plan for further development. There are some issues to be ironed out but I think the interface and functionality will stay more or less the same. We have the firm "pencils down" date set at September 23 by Google.

  1. October 19-25 implement audioread and audioinfo using libsndfile.
  2. October 26-September 1 implement audiowrite.
  3. Use the remainder of the time to integrate everything into Octave proper and fix any issues, write documentation, tests, etc.

Saturday, August 17, 2013

Changes to Octave Callback Interface

The interface to PortAudio that allows you to write callback functions in Octave underwent slight changes. From now on sound is returned in a single Matrix with one or two columns (depending on number of channels) with as many rows as there are audio frames. An example playing back sine waves of different frequencies on the two channels is given below.

function [ sound, status ] = callback_sine (frames)
  global lphase = 0.0;
  global rphase = 0.0;
  incl = 440.0 / 44100.0;
  incr = 443.0 / 44100.0;
  nl = incl * frames;
  nr = incr * frames;
  left = sin (2.0 * pi * [lphase:incl:lphase+nl]);
  right = sin (2.0 * pi * [rphase:incr:rphase+nr]);
  sound = [left', right'];
  status = 0;
  lphase = lphase + nl;
  rphase = rphase + nr;
endfunction
An example of it's use is given below.
player = audioplayer(@callback_sine);
play(player);
# play as long as you want
stop(player);
It is also possible to use Octave functions to process recorded data. An example that writes recorded data in to a text file is given below.

function status = callback_record (sound)
  fid = fopen('record.txt', 'at');
  for index = 1:rows(sound)
    fprintf(fid, "%.4f, %.4f\n", sound(index, 1), sound(index, 2));
  endfor
  fclose(fid);
  status = 0;
endfunction
Here is a plot of the resulting file with me clapping three times. Only one channel is plotted.

Monday, August 5, 2013

Writing Portaudio Callbacks in Octave

It is now possible to have Octave functions supply audio data to be played by audioplayer objects. Stereo and mono playback is supported, the function must supply a specified number of audio frames and make sure to return a valid PortAudio status value (0 for continue, 1 for completed and 2 for abort). For example, put the following in a file called callback_noise.m:

function [ left, right, status ] = callback_noise (frames)
left = randn (1, frames) - 0.5;
right = randn (1, frames) - 0.5;
status = 0;
endfunction

You can now use this as a callback as shown below.

player = audioplayer ('callback_noise', 44100);
play (player);
# play for as much you like
stop (player);

This will play white noise on both channels, which is not really interesting. To make matters more interesting we can use global variables to store state between callback calls. The following will play sine waves of different frequencies in both channels, place it in a file called callback_sine.m:

function [ left, right, status ] = callback_sine (frames)
global lphase = 0.0;
global rphase = 0.0;
incl = 440.0 / 44100.0;
incr = 443.0 / 44100.0;
nl = incl * frames;
nr = incr * frames;
left = sin (2.0 * pi * [lphase:incl:lphase+nl]);
right = sin (2.0 * pi * [rphase:incr:rphase+nr]);
status = 0;
lphase = lphase + nl;
rphase = rphase + nr;
endfunction

Both of these examples will play for as long as you don't use the stop method on the player object you created. This shows an advantage of this mode of operation over having to store all data in memory at one time. This way only small portion of audio has to be stored at any given time.

During audioplayer object construction all the parameters work the same way as when passing audio data. You can supply bit depth and device ID in addition to sampling frequency if you want to.