Sunday, September 25, 2011

Python for Alto Saxophone: Intonation, Transcription.

Two things (out of the many) I hope to work on my saxophone, is to transcribe music, so that I can re-arrange it, as well as improve on my intonation. The following script is to aid me in this endevour:

from wave import open as waveOpen
from ossaudiodev import open as ossOpen
import sys, tty, termios
import random

ver = "1.1"

def playTone(myToneFile):
# http://stackoverflow.com/questions/307305/play-a-sound-with-python
	s = waveOpen(myToneFile,'rb')
	(nc,sw,fr,nf,comptype, compname) = s.getparams( )
	dsp = ossOpen('/dev/dsp','w')
	try:
  		from ossaudiodev import AFMT_S16_NE
	except ImportError:
 		if byteorder == "little":
 	   		AFMT_S16_NE = ossaudiodev.AFMT_S16_LE
  		else:
  			AFMT_S16_NE = ossaudiodev.AFMT_S16_BE
	dsp.setparameters(AFMT_S16_NE, nc, fr)
	data = s.readframes(nf)
	s.close()
	dsp.write(data)
	dsp.close()

def _getch():
# http://stackoverflow.com/questions/1052107/reading-a-single-character-getch-style-in-python-is-not-working-in-unix
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

def getNextRand(currentRand, randRangeMax):
	newRand = random.randrange(0,randRangeMax)
	while (newRand == currentRand):
		newRand = random.randrange(0,randRangeMax)
	return newRand

# Setup dictionary of tones here
# Full range of tones:

fullRange = (
("Low Bb", "low_Bb.wav"),
("Low B", "low_B.wav"),
("Low C", "low_C.wav"),
("Low C#", "low_C#.wav"),
("Low D", "low_D.wav"),
("Low D#", "low_D#.wav"),
("Low E", "low_E.wav"),
("Low F", "low_F.wav"),
("Low F#", "low_F#.wav"),
("Low G", "low_G.wav"),
("Low G#", "low_G#.wav"),
("Low A", "low_A.wav"),
("Low A#", "low_A#.wav"),
("Middle B", "middle_B.wav"),
("Middle C", "middle_C.wav"),
("Middle C#", "middle_C#.wav"),
("Middle D", "middle_D.wav"),
("Middle D#", "middle_D#.wav"),
("Middle E", "middle_E.wav"),
("Middle F", "middle_F.wav"),
("Middle F#", "middle_F#.wav"),
("Middle G", "middle_G.wav"),
("Middle G#", "middle_G#.wav"),
("Middle A", "middle_A.wav"),
("Middle A#", "middle_A#.wav"),
("High B", "high_B.wav"),
("High C", "high_C.wav"),
("High C#", "high_C#.wav"),
("High D", "high_D.wav"),
("High D#", "high_D#.wav"),
("High E", "high_E.wav"),
("High F", "high_F.wav"),
("High F#", "high_F#.wav"))

middleRange = fullRange[7:21]
lowMiddleRange = fullRange[0:21]

soundDictionary = middleRange

print "Intonation/Transcription Ear Developer " + ver
print "\nSpace to play tone, 0 for answer, Enter for next tone, q to quit"

kbValue = ord(_getch())
# set a random note at start

random.seed()

currentRand = random.randrange(0,len(soundDictionary))

currentSound = soundDictionary[currentRand-1]



while 1:
	if kbValue == 32:
		print "Playing tone." 
		playTone(currentSound[1])
		playTone(currentSound[1])
	elif kbValue == 13:
		currentRand = getNextRand(currentRand, len(soundDictionary))
		currentSound = soundDictionary[currentRand-1]
		print "Switched to another tone." 
		
	elif kbValue == 48:
		print "The answer is: " + currentSound[0]
		
	elif kbValue == 113:
		break

	kbValue = ord(_getch())
	


So basically what it does is very simple, play back a specific sound sample, which I will try to use my ears and saxophone to match. In essence this will achieve two things.

First, figuring out a particular note will aid recognition of the particular note, and that will aid transcription. Secondly, with the playback, assuming I can figure out the note, if I can harmonize with the note should help with my intonation.

Hope it works :)

Edit: 27th September 2011
After some real world use, I found that randrange kept giving very close values for the small range I'm using, so additional code was used to ensure the previous tone would not be the next one as well.

No comments: