PureSoX (audio synthesis, editing and augmentation)

Share your advanced PureBasic knowledge/code with the community.
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

SoX is a command-line utility for augmenting and synthesising audio files. This is a library to simplify using it from PB.

PureSoX.pbi
Envelope.pbi (required)
WavFiles.pbi (required)

It requires the SoX exe and its add-ons. These are completely portable and could be included with your app.


Demo:

Code: Select all

XIncludeFile "PureSoX.pbi"

; first, specify the location of the SoX utilities and a temp files folder
InitSoX("C:\Program Files (x86)\sox-14-4-2\","G:\my_sox_temp_folder\")

SoX_Morse("PB")
SoX_Overdrive(10,15)
bare.s = Sox_LowPassFilter(1000)
SoX_Reverb(100,10,100,100,0.5,-6,#True)
SoX_ChorusSimple(25,0.5,2.25,2,#SoxWave_Triangle)
SoX_Flanger(3,2,-50,Random(100,50),0.5,#SoXWave_Sine,Random(100,5))
Sox_LowPassFilter(2500)
SoX_Normalize()
SoX_MixWith(0.5,bare,0.35)
morsefn.s = folder+"morse_concat.wav"
SoX_Export(morsefn)
SoX_ClearCache()
RunProgram(morsefn)

Every PureSoX procedure call uses the output of the previous call as its basis, thus we constantly move forward in "stages". It is therefore very easy to apply effects in sequence:

Code: Select all

SoX_Import("E:\my_input_sound.wav")
SoX_ChorusSimple(5,0.5,1,3,#SoxWave_Triangle)
SoX_Normalize(-3)
SoX_Export("E:\my_output_sound.wav")
To explain what is happening:

Code: Select all

; conform the input file --> TempFile#1
; apply chorus effect to TempFile#1 --> TempFile#2
; normalize TempFile#2 to -3dB --> TempFile#3
; copy TempFile#3 to the specified location
Another demo - generate a sweeping sine tone:

Code: Select all

SoX_Synthesize(#SoxWave_Sine,4,200,800)
SoX_FadeOut(10)
SoX_Export("E:\sine-sweep.wav")
I am aware that the library does not combine effects into single SoX calls, and that doing so would reduce the need for temp files and even cut some latency time. The goal was not to have the most efficient code, but to make programming with SoX as simple as possible.

But also, working this way enables quite complex operations. Each stage produces a temp file with a unique ID, which is returned by each procedure, so that we can use that version of the audio again later on. For example:

Code: Select all

original.s = SoX_Import("E:\my_input_sound.wav")
SoX_Reverb(100,25,100,100,0,0,#True)
chorus.s = SoX_ChorusSimple(5,0.5,1,3,#SoxWave_Sine)
SoX_RevertTo(original)
SoX_FadeOut(3500)
SoX_MixWith(0.5,chorus,0.5)
SoX_Export("E:\my_output_sound.wav")
Explanation:

Code: Select all

; conform the input audio --> TempFile#1
; apply reverb effect to TempFile#1, outputting ONLY the reverb --> TempFile#2
; apply chorus effect to TempFile#2 --> TempFile#3
; we're now working from #TempFile1 again
; fade out #TempFile1 over 3500ms --> #TempFile4
; mix TempFile4 with TempFile#3 --> TempFile#5
; copy TempFile#5 to the specified location
In this way, complex structured effects can be created.

Functions for handling the structure:
SoX_Copy ; creates an identical copy of the current stage (or a previous one, if specified)
SoX_RevertTo ; works from a previous stage
Functions for truncating audio operate exactly like their PB equivalents for text:
SoX_Left
SoX_Mid
SoX_Right
SoX_Trim ; will trim silence from start and end
SoX_LTrim ; will trim silence from start
SoX_RTrim ; will trim silence from end
Functions for extending audio (with silence):
SoX_PadStart
SoX_PadEnd
SoX_Pad ; both at once
Functions for altering volume:
FadeIn
FadeOut
Normalize
VolumeDecimal
VolumeDecibel
VolumeEnvelope
VolumeEnvelopeStereo ; separate, per-channel control
Mute
MuteLeft
MuteRight
Functions for working with channels:
Pan ; static pan position
PanEnvelope ; animated panning
SwapLeftRight
MirrorLeft
MirrorRight
A surprising omission from SoX is the ability to modulate volume over time, except for fade-in at the start of a file and fade-out at the end. This is a glaring omission which makes many obvious audio tasks impossible. Using code from BasicallyPure, I wrote procedures to operate on the wave data directly. This yielded the procedures VolumeEnvelope and VolumeEnvelopeStereo. Using the latter, a third procedure was developed, PanEnvelope, and then Pan and several others. All of these procedures are integrated into the standard effects system.

Envelope point times should be in (not necessarily whole) seconds. The final point's time is taken as the duration of the envelope. There are three types of envelope scaling: native (unchanged), stretch to duration of audio, and looped.

For the volume procedures, the input values should be in the range 0-1.

For Pan and PanEnvelope, they should range from -1 (100% left) to 1 (100% right).

Code: Select all

SoX_Synthesize(#SoxWave_BrownNoise,8)
;SoX_Pan(-0.5)
SoX_PanEnvelope("0~0|1~-0.5|2~-1|6~1|8~0|")
SoX_VolumeEnvelope("0~0|1~0.5|7~1|8~0|")
SoX_Export("E:\brownnoise-panned.wav")
The rest of the library is ports to SoX's various effects. I will add more of these as and when I need them, but already the following are implemented:
ShiftSpeed
ShiftPitch
ShiftSpeedAndPitch
BendPitch
Stretch
Overdrive
Reverb
Echos
Chorus (simplified version)
Flanger
Phaser
Reverse
Downsample
A note on how to use the pitch-bend effect:

Code: Select all

SoX_BendPitch("0~1~200|2~3~-400|4~5~600|6~7~-400|")
This example performs four bends of the pitch, delimited with the | symbol. Each bend is described with start time, end time, and cents change - these three fields being delimited with the ~ symbol. The times are in continuous form, whereas SoX takes them in relative form (this is taken care of by PureSoX). Pitch changes are specified in cents (100ths of a semitone).

Some usage notes...

All temp files will be deleted by calling SoX_ClearCache.

SoX_Import converts an input file into the standard operating format: stereo, 16-bit, 48khz. Everything is done in that format, but SoX_Export has a flag to export in mono.

Effects procedures work from the output of the previous stage. Synthesis/import procedures do not - these include SoX_Import, SoX_Synthesize, SoX_Silence, SoX_Concatenate, SoX_GenerateSoundtrack and SoX_Morse.

To revert to a previous stage at any time, use SoX_RevertTo.

SoX_Silence is not a port to SoX's own "silence" function. It simply creates a silent wave file of the specified length.


There are also some miscellaneous functions:
SoX_LoopTempo ; loops the current sound at a given tempo
SoX_CurrentDuration ; duration of the current stage
SoX_StageDuration ; duration of a previous stage
SoX_Morse ; generates Morse code -> new current stage
SoX_WobblePitch
SoX_Concatenate
SoX_GenerateSoundtrack

GetWavFileDuration
GetWavFileSampleRate
GetWavFileBitDepth
GetWavFileChannels
SoX_Concatenate does what you would expect. All files are automatically converted to the standard format.

SoX_GenerateSoundtrack essentially answers my own question from 9 years ago. It simplifies the scheduled mixing of multiple audio files. As above, format conformance is taken care of.

Code: Select all

Dim smix.SoxSoundtrackStructure(3)
smix(1)\fn = folder+"bass drum.wav"
smix(1)\start_secs = 10.25
smix(1)\volume_dec = 0.2
smix(2)\fn = folder+"cymbal.wav"
smix(2)\start_secs = 0
smix(2)\volume_dec = 1
smix(3)\fn = folder+"top hat.wav"
smix(3)\start_secs = 7.3
smix(3)\volume_dec = 0.6
SoX_GenerateSoundtrack(smix())
SoX_Export(folder+"mix.wav")
One final note...

I have also included a ring modulator effect. This isn't native to SoX but is made possible by combining several of its functions. I actually discovered it by accident while researching. I hope that people create more such effects for this library, using the building blocks that it provides. Direct access to the wave data makes all sorts of stuff possible (see the ApplyStereoEnvelope procedure for a clear example of how it's done). But that quickly gets into areas of maths etc. that I am not good at, so I won't be attempting it myself. But if other people do this, please share your effects here. Some things I would like to see are distortion, bitcrushing, waveshaping, stereo width adjustment, filter sweeping, and perhaps even vocoding.

This library has been a pleasure to work on and I am confident that it will be useful to other people. If anyone finds bugs, please let me know.
Last edited by Seymour Clufley on Wed Jul 13, 2022 8:28 am, edited 16 times in total.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
User avatar
Caronte3D
Addict
Addict
Posts: 1025
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: PureSoX

Post by Caronte3D »

Your work is very interesthing (like ever), thanks for sharing it :wink:
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

Quite a major update.

I have transferred the WAV helper functions to a dedicated include, WavFiles.pbi, which is required by PureSoX (see OP for link). Before getting into the PureSoX update, I will describe this include. It contains the following procedures from before:
GetWavFileSampleRate
GetWavFileBitDepth
GetWavFileChannels
GetWavFileDuration
as well as GetWavFileDetails, which combines all of the above into a structure.

GetWavFileDuration has been improved (it would fail with certain WAV files).

The underlying code for handling WAV data has been cleaned up and streamlined, yielding two very usable procedures:
LoadWavIntoStructure
CreateWavStructure(duration)
I would also draw attention to another procedure which could be very useful for somebody some day: GetWavFileCuePoints. Cue points are a way for WAV files to mark points of interest within the audio. They are known as "regions" within Sound Forge. The procedure returns a list with each cue point's text label and sample position in the audio.

Now, PureSoX...

Name changes:
SoX_VolumeTrend -> SoX_VolumeEnvelope
SoX_VolumeTrendStereo -> SoX_VolumeEnvelopeStereo
SoX_PanTrend -> SoX_PanEnvelope
New procedures:
SoX_MixFrom
SoX_MixTo
SoX_MixSpline
SoX_VolumeSpline
SoX_PanSpline
SoX_Append
SoX_Prepend
SoX_Gapper
SoX_Snipper
SoX_Jumble
SoX_StereoPingPong
LoadSoXSound
LoadSoXSound is for using stages with PB's native Sound library. If no stage ID is supplied, the current stage will be used.

The spline procedures require a spline, whose X values represent times (in seconds), and Y values modulate the effect. With SoX_MixSpline, the Y values will be clipped between 0 (100% current sound) and 1 (100% other sound). With SoX_VolumeSpline, they will be clipped between 0 and 1. With SoX_PanSpline, they will be clipped between -1 (100% left) and 1 (100% right).

SoX_Prepend and SoX_Append insert the specified stage (or sound file) before or after the current stage.

Code: Select all

XIncludeFile "PureSoX.pbi"
SoX_Synthesize(1,#SoxWave_Sine,1000,100)
SoX_VolumeEnvelope("0.975~1|1~0|") ; fade the last few milliseconds of the sine wave, to eliminate clipping
bare.s = SoX_VolumeDecimal(0.25)
SoX_Reverb(100,0,100,100,0,0,#True)
SoX_Left(4)
SoX_Prepend(bare)
snd.i=LoadSoXSound() : PlaySound(snd) : Delay(SoX_CurrentDuration()*1000)
Gapper and Snipper are emulations of effects found in Sound Forge. Gapper inserts silent gaps throughout the sound, at given frequency and length. Snipper snips out sections of the sound, at given frequency and length. SoX_Jumble is a sort of variant, which rearranges the sound by segments of the given length.

Code: Select all

XIncludeFile "PureSoX.pbi"
SoX_Synthesize(10,#SoxWave_Sine,1000,100)
SoX_VolumeDecimal(0.5)
SoX_Snipper(0.25,0.25,0.01)
;SoX_Gapper(0.25,0.25,0.01)
;SoX_Jumble(0.25,0.01)
snd.i=LoadSoXSound() : PlaySound(snd) : Delay(SoX_CurrentDuration()*1000)
SoX_StereoPingPong is obvious. The parameters are for pingpong duration (in seconds), decimal volume for the "quiet" sections (default is mute), and fade edges duration (in seconds).

Code: Select all

XIncludeFile "PureSoX.pbi"
SoX_Synthesize(1,#SoxWave_Sine,1000,100)
SoX_VolumeEnvelope("0.975~1|1~0|") ; fade the last few milliseconds to eliminate clipping
SoX_VolumeDecimal(0.4)
SoX_Reverb(100,0,100,100,0,0,#True)
SoX_StereoPingPong(0.5,0.25)
Sox_Export(folder+"stereopingpong.wav")
snd.i=LoadSoXSound() : PlaySound(snd) : Delay(SoX_CurrentDuration()*1000)
There is also a set of procedures for reducing or increasing the stereo "width" - ie. how much divergence there is between the left and right channels.
SoX_ZeroStereo
SoX_StereoTouch
SoX_StereoWidth
SoX_StereoWidthEnvelope
SoX_StereoWidthSpline
With SoX_ZeroStereo, the wav will still be dual-channel, but the channels will be identical.

SoX_StereoTouch has no input parameters; it simply shifts the right channel by 96 samples, producing an immediate stereo effect.

For the other procedures, input values should be between 0 and 2:
0 = total centre (equivalent to SoX_ZeroStereo)
1 = unchanged
2 = maximise stereo width (equivalent to SoX_StereoTouch)

SoX_StereoWidthEnvelope takes a string of time+value couplets, exactly the same format as with SoX_VolumeEnvelope etc.

I am very pleased with these procedures because they enable dramatic effects to be achieved.

Code: Select all

XIncludeFile "PureSoX.pbi"
SoX_Synthesize(0.6,#SoxWave_Sine,800,100)
SoX_VolumeEnvelope("0.575~1|0.6~0|")
bare.s = SoX_HighPassFilter(200)
SoX_VolumeDecimal(0.25)
SoX_Reverb(100,0,100,100,100,0,#True)
SoX_HighPassFilter(800)
SoX_Left(12)
SoX_VolumeEnvelope("1~4|2~4|9~30|12~0|")
SoX_StereoWidthEnvelope("1~0|8~2|")
SoX_LowPassFilter(1500)
SoX_MixWith(1,bare,0.333)
snd.i=LoadSoXSound() : PlaySound(snd) : Delay(SoX_CurrentDuration()*1000)

Code: Select all

XIncludeFile "PureSoX.pbi"
SoX_Synthesize(0.1,#SoxWave_WhiteNoise)
bare.s = SoX_HighPassFilter(200)
SoX_VolumeDecimal(0.6)
rev.s = SoX_Reverb(100,0,100,100,25,0,#True)
SoX_HighPassFilter(800)
SoX_Left(10)
SoX_VolumeEnvelope("1~8|2~8|9~30|10~0|")
InitEngine3D() : sox\engine3d=#True
spl = CreateSpline(#PB_Any)
AddSplinePoint(spl,0,0,0)
AddSplinePoint(spl,0.25,0,0)
AddSplinePoint(spl,5,2,0)
AddSplinePoint(spl,10,0,0)
SoX_StereoWidthSpline(spl)
FreeSpline(spl)
SoX_LowPassFilter(1500)
SoX_MixWith(2,bare,0.3)
snd.i=LoadSoXSound() : PlaySound(snd) : Delay(SoX_CurrentDuration()*1000)
Other changes...

The constant has been added for the sawtooth waveform. I somehow missed that one out originally.

SoX_ClearCache now deletes all wav files in the temp folder, not just those created in the current session.

SoX_GenerateSoundtrack now operates as the first stage in an effect chain, so that further augmentation can be done (eg. volume normalising). This means that it no longer accepts an "output file" variable, and that you will need to follow it with SoX_Export in order to save the output to file.

SoX_GenerateSoundtrack, SoX_Concatenate, SoX_MixWith, SoX_Append and SoX_Prepend now work with external (ie. un-imported) files where possible. This will cut processing time.

That's probably all for this library, unless I can get access to the frequency domain (and back again). If somebody could provide working FFT and InverseFFT procedures, that would be much appreciated.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

WavFiles.pbi (see OP) has been updated and now has a much better system for handling WAVs.

There are also two new procedures, AddWavCue and SaveWav. The latter will save a WAV file with cue points (regions, markers) included. These can then be read by other programs (such as Sound Forge) or by the LoadWav or GetWavFileCues procedures. Thus, it is now possible to both read and write WAV cue points from PB.

Code: Select all

wavfn.s = "P:\my_wave_file.wav"
LoadWav(wavfn,@w.WavStructure)
AddWavCue(@w,5,"five-second marker")
SaveWav(@w,wavfn)
A cue point's time is stored as a sample number, so to get the time in seconds you will need to divide that by the wav's sample rate:

Code: Select all

wavfn.s = "P:\my_wave_file.wav"
LoadWav(wavfn,@w.WavStructure)
ForEach w\cue()
  Debug StrD(w\cue()\sample / w\sample_rate,2)+" secs: "+w\cue()\label
Next
Last edited by Seymour Clufley on Wed Jun 30, 2021 3:09 am, edited 1 time in total.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Keya »

this is very cool! excellent work :)
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

Thank you, Keya. :) I'm glad somebody finds it useful.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Kwai chang caine »

Yes really big works :shock:
I have listen space sounds coming from space :D
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
highend
Enthusiast
Enthusiast
Posts: 123
Joined: Tue Jun 17, 2014 4:49 pm

Re: PureSoX (audio synthesis, editing and augmentation)

Post by highend »

Great work (only using the WavFiles include atm)!

I've only tried a few files up to now but it spits out quite a few unknown chunk types:
SAUR, JUNK, id3 and even an empty one

One of the files doesn't even show a waveform at all...

Do you want me to write you a pm with two of these files (and a place where to download them)?
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

highend wrote: Sat Aug 21, 2021 8:25 amDo you want me to write you a pm with two of these files (and a place where to download them)?
Yes, please do.

Apparently SAUR is a custom chunk created by the Wavosaur program.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
knut_the_dude
New User
New User
Posts: 5
Joined: Tue Jun 21, 2022 5:19 pm

Re: PureSoX (audio synthesis, editing and augmentation)

Post by knut_the_dude »

Hello Seymour,

this is pretty helpful! I just managed to work with SoX from out of PureBasic using RunProgram(), and then I found your post here. This is much more elegant :o

Unfortunately, the 2 files Envelope.pbi and WavFiles.pbi are no longer available where you uploaded them. Is there any chance you re-up them, or post the code here?

Best regards,
knut_the_dude
pasbel
New User
New User
Posts: 1
Joined: Tue May 18, 2021 4:50 pm

Re: PureSoX (audio synthesis, editing and augmentation)

Post by pasbel »

Hi
Can't download Envelope.pbi and WavFiles.pbi
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

Sorry, Pasbel and Knut. I have been working on a new library to replace this one, so everything has got updated. I have changed the links in the OP to the latest versions of those support files. If they don't work, let me know.
Last edited by Seymour Clufley on Wed Jul 13, 2022 8:15 am, edited 1 time in total.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
knut_the_dude
New User
New User
Posts: 5
Joined: Tue Jun 21, 2022 5:19 pm

Re: PureSoX (audio synthesis, editing and augmentation)

Post by knut_the_dude »

Hello Seymour,

thank you very much, I just saw your reply :) I will give you feedback later, when I had the opportunity to try your new libs.

Best regards,
Knut

EDIT:
When I run (on Windows 10) either the very first or the very last piece of demo code you supplied in this thread, I keep getting the error massage:

Line 1062: 'b' is not a valid operator.

It points to line 1062 in WavFiles.pbi, which is:

Code: Select all

Macro DefeatThis(a,b)
  If a>b
      a=b
  EndIf
  ;a = Defeat(a,b)
EndMacro
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: PureSoX (audio synthesis, editing and augmentation)

Post by Seymour Clufley »

Knut,
I have updated the files, including the main PureSoX file. Links in the OP.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
knut_the_dude
New User
New User
Posts: 5
Joined: Tue Jun 21, 2022 5:19 pm

Re: PureSoX (audio synthesis, editing and augmentation)

Post by knut_the_dude »

Thank you so much, Seymour :) I got some of the code examples running. Some not. Guess I have to dive deeper into the structure of your code. That's what I will do. Your lib is extremely helpful.
Post Reply