Page 1 of 1

jeeswg's SoX (Sound eXchange) tutorial

Posted: 15 Mar 2017, 02:23
by jeeswg
Some examples for changing pitch/duration on audio files.
Including some fiddly maths examples and:
Speed up slow lecture videos.
Roundabout technique to watch a video at normal pitch but increased speed on Media Player Classic.

Code: Select all

;jeeswg's SoX (Sound eXchange) tutorial

;note: 12 semitones in an octave (7 whites notes, 5 black notes)
;speed n (change pitch, change duration) (make n times faster) ('Chipmunk effect')
;pitch n (change pitch, preserve duration) (make higher by n/100 semitones)
;pitch 100*n (change pitch, preserve duration) (make higher by n semitones)
;tempo n (preserve pitch, change duration) (make n times slower)

;note: to do 'speed' based on semitones see below
;note: to do 'pitch' based on faster/slower see below

vEffect1 := "" ;convert (no parameter)
vEffect2 := "trim 01:00 00:15" ;(start at point, get seconds after it) (e.g. 1:00 to 1:15)
vEffect3 := "reverse"
vEffect4 := "speed 2" ;faster (+ pitch higher)
vEffect5 := "speed 0.5" ;slower (+ pitch lower)
vEffect6 := "tempo 2" ;slower (+ preserve pitch)
vEffect7 := "tempo 0.5" ;slower (+ preserve pitch)
vEffect8 := "pitch 100" ;higher (+ preserve duration) (by 1 semitone) (e.g. C to C#)
vEffect9 := "pitch -100" ;lower (+ preserve duration) (by 1 semitone) (e.g. C# to C)
vEffect10 := "pitch 1200" ;higher (+ preserve duration) (by 1 octave)
vEffect11 := "pitch -1200" ;lower (+ preserve duration) (by 1 octave)
vEffect12 := "pitch 50" ;higher (+ preserve duration) (by 1/2 semitone)
vEffect13 := "pitch -50" ;lower (+ preserve duration) (by 1/2 semitone)
vEffect14 := "speed " 2**(1/12) ;higher (+ faster) (by 1 semitone)
vEffect15 := "speed " 2**(-1/12) ;lower (+ slower) (by 1 semitone)
vEffect16 := "speed " 2**(12/12) ;higher (+ faster) (by 1 octave)
vEffect17 := "speed " 2**(-12/12) ;lower (+ slower) (by 1 octave)
vEffect18 := "speed " 2**(0.5/12) ;higher (+ faster) (by 1/2 semitone)
vEffect19 := "speed " 2**(-0.5/12) ;lower (+ slower) (by 1/2 semitone)
vEffect20 := "pitch -702" ;lower (+ preserve duration) (by 7.02 semitones)

;vPathSox = %A_Desktop%\sox.exe
;vPath = %A_Desktop%\MyAudio.mp3
vDir2 = %A_Desktop%\z SoX
vCharClass := "[" Chr(1) "-" Chr(31) "\\/:*?""<>|]"
SplitPath, vPath, vName, vDir, vExt, vNameNoExt, vDrive
if FileExist(vPathSox) && FileExist(vPath)
{
	FileCreateDir, % vDir2
	Run, % vDir2
	Loop, 20
	{
		vEffect := vEffect%A_Index%
		vEffectX := Trim(RegExReplace(vEffect, vCharClass, " "))
		vPathNew := vDir2 "\" Format("{:02}", A_Index) RTrim(" " vEffectX) "." vExt
		;MsgBox, "%vPathSox%" "%vPath%" "%vPathNew%" %vEffect%
		if !FileExist(vPathNew)
			RunWait, "%vPathSox%" "%vPath%" "%vPathNew%" %vEffect%,, Hide
	}
}
MsgBox, % "done"
return

;==================================================

;DO 'SPEED' BASED ON SEMITONES
;speed n (change pitch, change duration) (make n times faster) ('Chipmunk effect')

;USAGE EXAMPLE:
;Shifting a song by half a semitone.
;Some songs, if you try and play along on the piano,
;appear not to match up with the notes, but fall in-between notes.
;If you adjust these songs by half a semitone,
;you should be able to play along.

vSemitones := 0.5
vEffect1 := "speed " 2**(vSemitones/12) ;higher (+ faster) (by n semitones)
vEffect2 := "speed " 2**(-vSemitones/12) ;lower (+ slower) (by n semitones)
RunWait, "%vPathSox%" "%vPath%" "%vPathNew1%" %vEffect1%,, Hide
RunWait, "%vPathSox%" "%vPath%" "%vPathNew2%" %vEffect2%,, Hide

;to make it higher by n semitones: 2^(n/12)
;to make it lower by n semitones: 2^(-n/12)
;e.g. 1/2 semitone
;2^(1/24) = 1.029302 ;24th root of 2
;2^(-1/24) = 0.971532 ;1/(24th root of 2)
;e.g. 1 semitone
;2^(1/12) = 1.059463 ;12th root of 2
;2^(-1/12) = 0.943874 ;1/(12th root of 2)

;==================================================

;DO 'PITCH' BASED ON FASTER/SLOWER
;pitch n (change pitch, preserve duration) (make higher by n/100 semitones)
;pitch 100*n (change pitch, preserve duration) (make higher by n semitones)

;note 2 ways for calculating vEffect2 that give the same answer
vMultiplier := 1.5
vEffect1 := "pitch " Round(1200 * Log(vMultiplier) / Log(2)) ;higher (+ preserve duration) (by multiplier) (e.g. 1.5 times higher)
vEffect2 := "pitch " Round(1200 * Log(1/vMultiplier) / Log(2)) ;lower (+ preserve duration) (by 1/multiplier) (e.g. 1.5 times lower, i.e. 0.667 times higher)
;vEffect2 := "pitch " -1200*Round(Log(vMultiplier)/Log(2)) ;lower (+ preserve duration) (by 1/multiplier) (e.g. 1.5 times lower, i.e. 0.667 times higher)
RunWait, "%vPathSox%" "%vPath%" "%vPathNew1%" %vEffect1%,, Hide
RunWait, "%vPathSox%" "%vPath%" "%vPathNew2%" %vEffect2%,, Hide

;mathematical explanation:

;desired pitch	required n for 'pitch n'
;0.25p	-2400
;0.5p	-1200
;p	0
;2p	1200
;4p	2400

;rewritten:
;desired pitch	required n for 'pitch n' parameter
;(2^-2)p	(-2)1200
;(2^-1)p	(-1)1200
;(2^-0)p	(0)1200
;(2^1)p		(1)1200
;(2^2)p		(2)1200

;in general:
;(2^x)p		1200x

;We know to double the pitch we need 'pitch 1200'.
;To multiply the pitch by m, we need 'pitch n',
;where m=2^x, and n=1200x.

;Since, m = 2^x
;so, x = log_2 m = log m / log 2
;so, n = 1200x = 1200(log m / log 2)

;this gives code: 1200 * Log(vMultiplier) / Log(2)
;we use Round because the parameter expects an integer:
;so, Round(1200 * Log(vMultiplier) / Log(2))

;==================================================

;DO 'PITCH' BASED ON FASTER/SLOWER
;pitch n (change pitch, preserve duration) (make higher by n/100 semitones)
;pitch 100*n (change pitch, preserve duration) (make higher by n semitones)

;USAGE EXAMPLE:
;Speed up slow lecture videos.
;Roundabout technique to watch a video at normal pitch but increased speed on Media Player Classic.
;In MPC, you can watch a video at increased speed but you get the Chipmunk effect.
;In MPC, you can watch a video while listening to a different audio file (via a playlist).
;Using FFmpeg, you can extract the audio from a video file.
;So extract the audio, lower the pitch (preserve the duration),
;create a playlist which links the video and the new audio,
;and then when you play it at a specific increased speed,
;it will be at normal pitch but increased speed, 'quick talking'.

;E.g. if we want to play the video 1.5 times faster,
;we make the audio 1.5 times lower i.e. 0.667 times higher,
;so that when the Chipmunk effect occurs, the pitch will be normal.

vMultiplier := (1/1.5) ;will give 'pitch -702'
vEffect := "pitch " Round(1200 * Log(vMultiplier) / Log(2))
;MsgBox, % vEffect
RunWait, "%vPathSox%" "%vPath%" "%vPathNew%" %vEffect%,, Hide
;do 'speed 1.5' on this new file to hear what it will sound like

;to sum up the procedure:
;original wave (pitch: p, duration: d)
;new wave (pitch: (1/1.5)p=(2/3)p, duration: d)
;wave when played at 1.5x by MPC (pitch: p, duration: (1/1.5)d=(2/3)d)

;example text for a mpcpl file (Media Player Classic playlist):
;MPCPLAYLIST
;1,type,0
;1,filename,%vPathVideo%
;1,filename,%vPathAlternativeAudio%

;==================================================
E.g. of getting information from StdErr:

Code: Select all

q:: ;SoX get file details
vPath = %A_Desktop%\MyAudio.mp3
vPathNew := "-n" ;null (i.e. no output file)
;vEffect := "stat"
vEffect := "stats" ;stat/stats give different information

vTarget = "%vPathSox%" "%vPath%" "%vPathNew%" %vEffect%
;vOutput := JEE_RunGetStdOut(vTarget)
vOutput := JEE_RunGetStdErr(vTarget)
Clipboard := vOutput
MsgBox, % vOutput
return

;==================================================

;JEE_RunWaitGetStdOut
JEE_RunGetStdOut(vTarget, vSize:="")
{
	DetectHiddenWindows, On
	vComSpec := A_ComSpec ? A_ComSpec : ComSpec
	Run, % vComSpec,, Hide, vPID
	WinWait, ahk_pid %vPID%
	DllCall("kernel32\AttachConsole", UInt,vPID)
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vTarget)
	vStdOut := ""
	if !(vSize = "")
		VarSetCapacity(vStdOut, vSize)
	while !oExec.StdOut.AtEndOfStream
		vStdOut := oExec.StdOut.ReadAll()
	DllCall("kernel32\FreeConsole")
	Process, Close, % vPID
	return vStdOut
}

;==================================================

;JEE_RunWaitGetStdErr
JEE_RunGetStdErr(vTarget, vSize:="")
{
	DetectHiddenWindows, On
	vComSpec := A_ComSpec ? A_ComSpec : ComSpec
	Run, % vComSpec,, Hide, vPID
	WinWait, ahk_pid %vPID%
	DllCall("kernel32\AttachConsole", UInt,vPID)
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vTarget)
	vStdErr := ""
	if !(vSize = "")
		VarSetCapacity(vStdErr, vSize)
	while !oExec.StdErr.AtEndOfStream
		vStdErr := oExec.StdErr.ReadAll()
	DllCall("kernel32\FreeConsole")
	Process, Close, % vPID
	return vStdErr
}

;==================================================