APL solution to change pitch of a WAV file by semitones?
Forum rules
This forum is for discussing APL-related issues. If you think that the subject is off-topic, then the Chat forum is probably a better place for your thoughts !
This forum is for discussing APL-related issues. If you think that the subject is off-topic, then the Chat forum is probably a better place for your thoughts !
4 posts
• Page 1 of 1
APL solution to change pitch of a WAV file by semitones?
I'm looking for a Dyalog APL-only solution to change the pitch of a WAV file by one or more semitones. Specifically, one or more semi-tones higher. I don't want to use external libraries or executables, .NET, etc. However, Win32 functions are OK if needed. Does anyone have anything written in Dyalog APL to do that? Thanks. / Rav
- Rav
- Posts: 23
- Joined: Tue Jul 25, 2023 4:41 pm
Re: APL solution to change pitch of a WAV file by semitones?
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
The sub-function (WriteLong, WriteShort, WriteSmall,etc)are defined in the GitHub.
I hope this helps.
Ray
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
Ray Cannon
Please excuse any smelling pisstakes.
Please excuse any smelling pisstakes.
-
ray - Posts: 238
- Joined: Wed Feb 24, 2010 12:24 am
- Location: Blackwater, Camberley. UK
Re: APL solution to change pitch of a WAV file by semitones?
Rav,
I have just tried creating a wave file one semitone higher, by deleting every twelfth sample, and it appears to have worked OK.
Good luck
Ray
I have just tried creating a wave file one semitone higher, by deleting every twelfth sample, and it appears to have worked OK.
Good luck
Ray
Ray Cannon
Please excuse any smelling pisstakes.
Please excuse any smelling pisstakes.
-
ray - Posts: 238
- Joined: Wed Feb 24, 2010 12:24 am
- Location: Blackwater, Camberley. UK
Re: APL solution to change pitch of a WAV file by semitones?
Thank you, Ray! Success! I studied your MakeWave function and then spent a few hours writing a function to generate the next 12 semi-tone WAV files from the my "original tone" WAV file. Previously, I had used Audacity to create those 12 files and was reading them in and storing them in my workspace, which was taking a lot of room (13 times the space needed for the original tone file). Now I only have to store the one original tone file, and generate the other 12 files on the fly, and it's very fast. My workspace size is now about half the size it used to be, and that will make loading and running the workspace faster for my end user.
I had to manually figure out how many pairs of samples to remove for each semi-tone (my original file is in stereo). Most of the semi-tone files were derived from the original tone file, while others were derived from some of the derived tone files. I hope that makes sense.
The quality of the derived tone files isn't quite as good what Audacity produced, but it's good enough for what I'm using it for. I imagine that Audacity is changing the pitch in some other way.
Anyway, again thanks very much. / Rav
I had to manually figure out how many pairs of samples to remove for each semi-tone (my original file is in stereo). Most of the semi-tone files were derived from the original tone file, while others were derived from some of the derived tone files. I hope that makes sense.
The quality of the derived tone files isn't quite as good what Audacity produced, but it's good enough for what I'm using it for. I imagine that Audacity is changing the pitch in some other way.
Anyway, again thanks very much. / Rav
- Rav
- Posts: 23
- Joined: Tue Jul 25, 2023 4:41 pm
4 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group