Hi Rav
I have code to produce WAV files. Both simple (sine, square , sawtooth etc) single frequencies, and complex music (with virtual instruments Church Organ, Piano and Guitar).
This code "knows" about the format of WAV file.
Note: WAV files (unlike say MIDI files) do not contain frequency data, but thousands of "samples" (frequently 44100 samples taken each second) taken from the sound, irrespective of the pitch.
I assume that given a "tune" in a WAV file, you wish to create a new WAV file with that "tune" transposed into another "key".
You could transpose the music part of a MONO WAV file by one octave by simply extracting the musical "data" part, deleting every other "sample", and writing out a new WAV file with this data, with minor adjustments to the header data relating to the change in size.
If the source was a Stereo file, you would need to remove every other "pair" of samples.
To change it by a Semi-tone, I therefor assume you would need to remove every twelfth sample (or sample pair), rather than every other.
However, the discontinuities introduced could be severe at some frequencies, so I am very unsure if this would work when working with instruments with many harmonics, rather than just a simple sine wav.
For information about creating simple WAV files, and their format see
https://github.com/RayCannon/Wave
Here is my MakeWave function
Code: Select all
name MakeWave freq;file;tie;chans;rate;bits;size;max;sizpos;datpos;count;data;cycle;halfcycle;unit;style;note;notes;dur;durs;part
⍝;file;tie;sizpos;datpos;cycle;val;size;freq;rate;bits;chans;chan;max;count;bin;data
⍝ Make 1 cycle of wave sound
file←'C:\Program Files\APL\notes\',name,'.wav'
tie←MakeFile file
⍝ Write the ".WAV" header
⍝ 1 - 4 “RIFF” Marks the file as a riff file. Characters are each 1 byte long.
sizpos←tie WriteChar'RIFF' ⍝ RIFF format tag
⍝ 5 - 8 File size (integer) Size of the overall file - 8 bytes, in bytes (32-bit integer).
⍝ Typically, you’d fill this in after creation using sizpos.
tie WriteLong 0 ⍝ Size to end of file from here, fill in later
⍝ 9 -12 “WAVE” File Type Header. For our purposes, it always equals “WAVE”.
tie WriteChar'WAVE' ⍝ .WAV tag
⍝ 13-16 “fmt " Format chunk marker. Includes trailing null
tie WriteChar'fmt ' ⍝ Format tag
⍝ 17-20 "16" Length of format data as listed above
tie WriteLong 16 ⍝ Size of Format will be 0x10 (16 bytes)
⍝ 21-22 "1" Type of format (1 is PCM) - 2 byte integer
tie WriteShort 1 ⍝ Format tag WAVE_FMT_PCM
⍝ 23-24 "2" Number of Channels - 2 byte integer
chans←1 ⍝ Mono=1/Stero=2
tie WriteShort chans ⍝ number of channels
⍝ 25-28 "44100" Sample Rate - 32 byte integer.
⍝ Common values are 44100 (CD), 48000 (DAT).
⍝ Sample Rate = Number of Samples per second, or Hertz.
rate←44100 ⍝ low=8000/high=44100 sample rate in Bytes per sec
tie WriteLong rate ⍝ Number of samples a second
⍝ 29-32 "176400" (Sample Rate * BitsPerSample * Channels) / 8.
bits←16 ⍝ size (in bits) of a sample
size←rate×(bits÷8)×chans ⍝ size (in bytes) of 1 sec of sound
tie WriteLong size ⍝ Number of Bytes per sec
⍝ 33-34 "4" (BitsPerSample * Channels) / 8.
⍝ 1 - 8 bit mono
⍝ 2 - 8 bit stereo 16 bit mono
⍝ 4 - 16 bit stereo
unit←(bits×chans)÷8
tie WriteShort unit ⍝ size per unit
⍝ 35-36 "16" Bits per sample
tie WriteShort bits ⍝ Number of bits in each sample
⍝ 37-40 “data” Data chunk header.
⍝ Marks the beginning of the data section.
datpos←tie WriteChar'data' ⍝ Data Block tag
⍝ 41-44 File size (data) Size of the data section.
tie WriteLong 0 ⍝ Size of data the following data, fill in later
⍝ Data section follows
count←0
style←⊃0 0 1 0 0/'sine' 'triangle25' 'square' 'sawtooth' 'isosceles'
data←style MakeData(chans size rate bits freq)
:For cycle :In 1 ⍝ Number of complete cycles to write to file
:If bits=16
tie WriteShort data
count+←2×⍴data
:Else
tie WriteSmall data
count+←⍴data
:End
:End
⍝ update the header with the correct sizes
tie sizpos ReWriteLong count+40 ⍝ 40 is 28 in hex
tie datpos ReWriteLong count
⎕NUNTIE tie
The sub-function (WriteLong, WriteShort, WriteSmall,etc)are defined in the GitHub.
I hope this helps.
Ray