sound in APL

Learning APL or new to Dyalog? Ask "silly" questions here, without fear...

Re: sound in APL

Postby PGilbert on Sun Jul 03, 2016 3:16 am

Could you use audiowrite to convert your existing mathlab files to .wav format ?
User avatar
Posts: 401
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: sound in APL

Postby Stu on Sun Jul 03, 2016 5:16 pm

I can see that I haven't explained what I want to do clearly. My bad. So let me try again.
Let's say I do this:


z will contain a representation of 64 cycles of a square wave. If I play these (suitably scaled) through my sound card at a sample rate of 8192 Hz., I'll hear a toot of a 1024 Hz. tone for about 63 milliseconds. I'm going to be computing much more complex sounds, but the problem remains the same: how to play the samples. It appears that in Dyalog the best approach is to embed the samples in some standard file format and then play the file. From my point of view, .wav is the best choice (no compression). But it's not clear to me how to make a file that contains both characters and ints of different sizes (the sizes are really important because the .wav interpreter identifies the various parts of a file by byte counts in the three section headers in the file).
Posts: 97
Joined: Thu Dec 31, 2015 1:30 am

Re: sound in APL

Postby Morten|Dyalog on Mon Jul 04, 2016 7:34 am

Stu, I believe the set of "native file functions" should make it possible to write exactly what you want to a file:

Code: Select all
      tn←'c:\temp\sounds.wav' ⎕NCREATE 0  ⍝ OK, not really a WAV file
      1234 ⎕NAPPEND tn 163               ⍝ write 1234 as a 16-bit integer(3)
      'A' ⎕NAPPEND tn 80                 ⍝ write A as an 8-bit unicode char (0)
      ⎕UCS ⎕NREAD tn 80 3 0              ⍝ read 3 unicode chars starting at position 0
210 4 65
      256⊥⌽210 4                         ⍝ litte-endian 1234

Does that help? Also take a look at ⎕DR, which will allow you to perform type conversions explicitly without also writing or reading them.
User avatar
Posts: 394
Joined: Tue Sep 09, 2008 3:52 pm

Re: sound in APL

Postby Stu on Mon Jul 04, 2016 2:32 pm

This looks very promising. Thanks! I think I'll have to make the entire file from individual bytes with the 83 conversion. Just a few of the bytes will have to be hand coded because ⎕AV doesn't exactly match up with ASCII.
Posts: 97
Joined: Thu Dec 31, 2015 1:30 am

Re: sound in APL

Postby ray on Tue Jul 05, 2016 3:28 pm

I wrote some code to create WAV files for PocketAPL under Dyalog (Classic not Unicode) some years back.

The MakeWave function created a WAV file of a single frequency, just one cycle long.

Code: Select all
name MakeWave freq;file;tie;sizpos;datpos;cycle;val;size;freq;rate;bits;chans;chan;max;count;bin;data
 ⍝ Make 1 cycle of wave sound
 +file←('\My Documents\wave'PoW'c:\wave'),name,'.wav'
 tie←MakeFile file
 chans←1                    ⍝  Mono=1/Stero=2
 rate←44100                 ⍝  low=8000/high=44100 sample rate in Bytes per sec
 bits←16                    ⍝  size (in bits) of a sample
 size←rate×(bits÷8)×chans   ⍝  size (in bytes) of 1 sec of sound
⍝ max←{⍵=8:255 ⋄ 65536}bits  ⍝  maximum (integer) value of a sample
 max←{⍵=8:¯1+2*⍵ ⋄ ¯1+2*⍵-1}bits ⍝  maximum (integer) value of a sample
  ⍝ Write the ".WAV" header
 sizpos←tie WriteChar'RIFF' ⍝  RIFF format tag
 tie WriteLong 0            ⍝  Size to end of file from here, fill in later
 tie WriteChar'WAVE'        ⍝  .WAV tag
 tie WriteChar'fmt '        ⍝  Format tag
 tie WriteLong 16           ⍝  Size of Format
 tie WriteShort 1           ⍝  Format tag WAVE_FMT_PCM
 tie WriteShort chans       ⍝  number of channels
 tie WriteLong rate         ⍝  Number of samples a second
 tie WriteLong size         ⍝  Number of Bytes per sec
 tie WriteShort 1           ⍝  Block Align ?
 tie WriteShort bits        ⍝  Number of bits in each sample
 datpos←tie WriteChar'data' ⍝  Data Block tag
 tie WriteLong 0            ⍝  Size of data the following data, fill in later
 ⍝ Calculate the data, use square, rather than sin wave
 data←chans/⌊0.5+(size÷chans){{⍵/¯32768 32767}(⌊⍺÷2×⍵)}freq
 ⍝ Now write the data to file
 :For cycle :In 1           ⍝ Number of complete cycles to write to file
     :If bits=16
         tie WriteShort data
         tie WriteSmall data
 ⍝ update the header with the correct sizes
 tie sizpos ReWriteLong count+28
 tie datpos ReWriteLong count
 ⎕NUNTIE tie
 {}PlaySound file         ⍝ Play untill next sound or PlayNoSound
 {}⎕DL 0.125

The functions WriteChar WriteLong WriteShort WriteSmall ReWriteLong MakeFile

Code: Select all
{r}←tie WriteChar data
 r←data ⎕NAPPEND tie 82

Code: Select all
{r}←tie WriteLong data
 r←data ⎕NAPPEND tie 323

Code: Select all
{r}←tie WriteShort data;max
r←data ⎕NAPPEND tie 163

Code: Select all
 {r}←tie WriteSmall data
 r←data ⎕NAPPEND tie 83

Code: Select all
{r}←tiepos ReWriteLong data;tie;pos
 tie pos←tiepos
 r←data ⎕NREPLACE tie pos 323

Code: Select all
tie←MakeFile file
 :Trap 0
     file ⎕NERASE file ⎕NTIE 0
 tie←file ⎕NCREATE 0

You should not need "PoW" which selected either PocketApl or Windows using APLVersion.

PlaySound used a ⎕NA call. This also used PoW to select the correct DLL library (Winmm for Windows, coredll for PocketAPL0).

Code: Select all

         ⍵=1:'Winmm|PlaySound <0t i U'
         ⍵=2:'coredll|PlaySoundW <0t i U'
     }2 PoW 1

     ps ⍵ 0 9
⍝ 9=asynchronously+loop the sound until next sndPlaySound

⍝LPCSTR pszSound, C:\Program Files\NetMeeting\blip.wav
⍝HMODULE hmod,
⍝DWORD fdwSound );
⍝ *  flag values for fuSound and fdwSound arguments on [snd]PlaySound
⍝ */
⍝#define SND_SYNC            0x0000  /* play synchronously (default) */
⍝#define SND_ASYNC           0x0001  /* play asynchronously */
⍝#define SND_NODEFAULT       0x0002  /* silence (!default) if sound not found */
⍝#define SND_MEMORY          0x0004  /* pszSound points to a memory file */
⍝#define SND_LOOP            0x0008  /* loop the sound until next sndPlaySound */
⍝#define SND_NOSTOP          0x0010  /* don't stop any currently playing sound */

Since I wrote this, I've changed to using sin waves (rather than square) and also created a few cords.

Be aware, when creating a cord WAV file with a single "cycle", the fundamental frequencies of the 3(or more) notes making up the cord, have to be "well tempered", so they all start and finish at exactly the same time. Using an "equal temperament" scale (see below) will sound dreadful.

Code: Select all
scale←{ ⍝ create 4 octaves of C scales
     }scal 261.626
     ⍝261.626 277.183 293.665 311.127 329.628 349.228 369.994 391.995 415.305 440 466.164 493.883

Good luck
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
Posts: 173
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: sound in APL

Postby Stu on Tue Jul 05, 2016 6:42 pm

Thanks Ray! A lot of useful info there.

Posts: 97
Joined: Thu Dec 31, 2015 1:30 am

Re: sound in APL

Postby gil on Wed Jul 06, 2016 8:33 pm

Building on Ray's contribution and looking at the Matlab sound function I've put together a couple of functions that should give you what you need.

It might be worthwhile adding another cover function to combine the PlaySound and Wave functions, but it should be enough to get you started.
Posts: 68
Joined: Mon Feb 15, 2010 12:42 am

Re: sound in APL

Postby Stu on Wed Jul 06, 2016 10:03 pm

Thanks Gil! This looks very nice. I was able to get a "wavwrite" of my own running, but it's an ugly kludge compared to yours. Since mine doesn't use the Windows sound API, it doesn't have all the features yours does. My next step it to look into the Dyalog manual to learn how to use namespaces (APL didn't have them when I learned APL in the late 1970's and used it for a year or two).
Posts: 97
Joined: Thu Dec 31, 2015 1:30 am

Re: sound in APL

Postby Stu on Thu Jul 07, 2016 1:08 am

Since this is the "no question is silly" group I'll risk a few questions about your Sound namespace. I have your namespace on my desktop along with my own audio workspace. How do I access your Wave function in my workspace? I've looked in "Mastering Dyalog APL" about namespaces, but I still can't figure out how to do it.

Something in Dyalog prevents me from copying your functions into my workspace. Whenever I try to copy one, the closing curly brace is red. Cut-and-paste from a namespace to some other place is apparently illegal. What's up with that?
Posts: 97
Joined: Thu Dec 31, 2015 1:30 am

Re: sound in APL

Postby gil on Thu Jul 07, 2016 6:51 am

Hi Stu

Namespaces are containers for code similar to folders in the file system. The scripted version of namesapces (like my file in github) can be loaded into your workspace by executing the user command:
      ]load C:\path\to\file\Sound.dyalog

Red curly braces indicate a syntax error. You could get that if you missed copying the entire function (missing the opening brace).

PS. I made a second commit last night as I noticed one of the functions was missing 2 lines. Try downloading a fresh copy and run the above command.
Posts: 68
Joined: Mon Feb 15, 2010 12:42 am


Return to New to Dyalog?

Who is online

Users browsing this forum: No registered users and 1 guest