root/data2scale.py

User picture

Author: therm000

Revision: 30 («Previous)

File Size 5.48 KB

(July 18, 2011 UTC) 7 months ago

data2scale script for converting data into music

 
Show/hide line numbers
import sys
from math import cos,pi,log
import getopt

__doc__ = """
Python Data2Scale app for Linux, version 0.1.
author: Jose I. Orlicki
example:
# cat data2scale.py | python data2scale.py -t 0.05 -s harmonic_minor -i

-h --help    : this help
-s --scale=  : choose scale, pentatonic is default (diatonic, chromatic, natural_minor or harmonic_minor)
-l --length= : choose data length, default is "nibble", other option is "byte" 
-i --inverse : inverse endianess, only for nibble, false or true, default false.
-t --time=   : in seconds per note, max 0.5 secs.
-u --sustain : sustains the repeated notes.


"""

class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()

# begin getch
class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        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

class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()

getch = _Getch()
# end getch


def freq(fq=4096, bytes=4096):
#	fq = bytes = 2046
#	fq = fq * float(512) / 7000
	ret = ''
	for i in range(bytes):
		val = float(i) / bytes
		myord = int(((cos(pi * fq * val) + 1.0) / 2) * 255)
		ret += chr( myord )
	return ret

def play_freq(fq, bytes):
	open('/dev/dsp','w').write(freq(fq, bytes))

def note(nro_nota, frac=0.125):
	data = freq(110 * (2**(1.0/12))**nro_nota )
	return data[:int(frac*len(data))]

def play_note(nr_note):
	open('/dev/dsp','w').write(note(nr_note))

def notes_demo():
	dev = open('/dev/dsp','w')
	for i in range(48):
		dev.write( note(i, 0.125) )
	for i in range(48,0,-1):
		dev.write( note(i, 0.125) )
	dev.close()

def chromatic_scale(note_nr):
	return note_nr

def diatonic_scale(note_nr):
	return (note_nr / 7) * 12 + diatonic(note_nr % 7)

def diatonic(note_nr):
	diat_map = {0:0, 1:2, 2:4, 3:5, 4:7, 5:9, 6:11}
	if note_nr in diat_map:
		return diat_map[note_nr]
	else:
		raise Exception('bad note number!') 

def pentatonic_scale(note_nr):
	return (note_nr / 5) * 12 + diatonic(note_nr % 5)

def pentatonic(note_nr):
	penta_map = {0:0, 1:2, 2:4, 3:7, 4:9}
	if note_nr in penta_map:
		return penta_map[note_nr]
	else:
		raise Exception('bad note number!') 

def natural_minor_scale(note_nr):
	return (note_nr / 7) * 12 + natural_minor(note_nr % 7)

def natural_minor(note_nr):
	nm_map = {0:0, 1:2, 2:3, 3:5, 4:7, 5:8, 6:10}
	if note_nr in nm_map:
		return nm_map[note_nr]
	else:
		raise Exception('bad note number!') 

def harmonic_minor_scale(note_nr):
	return (note_nr / 7) * 12 + harmonic_minor(note_nr % 7)

def harmonic_minor(note_nr):
	hm_map = {0:0, 1:2, 2:3, 3:5, 4:7, 5:9, 6:10}
	if note_nr in hm_map:
		return hm_map[note_nr]
	else:
		raise Exception('bad note number!') 

buffer = []

def data_music(data, scale_func=chromatic_scale, frac=0.125, length='byte', inverse=False, sustain=False):
	global buffer
	dev = open('/dev/dsp','w')

	notes = ''

	for char in data:
		if length == 'nibble':
			fst = ord(char) >> 4
			snd = ord(char) & ord('\x0f')
			if not inverse:
				buffer.append(fst)
				if len(buffer)>1 and buffer[-1]!=buffer[-2]:
					dev.write( note( scale_func( buffer[-2] ), frac ) * (len(buffer)-1))
					buffer = buffer[-1:]
				buffer.append(snd)
			else:
				buffer.append(snd)
				if len(buffer)>1 and buffer[-1]!=buffer[-2]:
					dev.write( note( scale_func( buffer[-2] ), frac ) * (len(buffer)-1))
					buffer = buffer[-1:]
				buffer.append(fst)
		else:
			n = ord(char) / 1
			buffer.append(n)
		if len(buffer)>1 and buffer[-1]!=buffer[-2]:
			dev.write( note( scale_func( buffer[-2] ), frac ) * (len(buffer)-1))
			buffer = buffer[-1:]
	dev.close()

#data_music('AAAAAAAAAA')		

	
def main():
    # parse command line options
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hs:l:it:u", ["help","scale","length","inverse","time","sustain"])
	# process options
	scale, length, time, inverse, sustain = pentatonic_scale, 'nibble', 0.2, False, False
	scale_map = {
		'pentatonic':pentatonic_scale, 
		'diatonic':diatonic_scale, 
		'natural_minor':natural_minor_scale,
		'harmonic_minor':harmonic_minor_scale,
		'chromatic':chromatic_scale,
		}
        for o, a in opts:
            if o in ("-h", "--help"):
                print __doc__
                sys.exit(0)
            elif o in ("-s", "--scale"):
	        scale = scale_map[a]
            elif o in ("-l", "--length"):
    	        length = a
            elif o in ("-i", "--inverse"):
	        inverse = True
            elif o in ("-t", "--time"):
                time = float(a) * 2.0
            elif o in ("-u", "--sustain"):
	        sustain = True
            else:
	        raise Exception('Unknown parameter!: %s' % o)

        while True:
	    try:
		    char = getch()
	    except Exception, e:
		    char = sys.stdin.read(1)
	    if not char: break
            data_music( char, scale, time, length, inverse, sustain )

    except getopt.error, msg:
        print msg
        print "for help use --help"
        sys.exit(2)
    except Exception, e:
        print 'Bad something: ' + str(e)
        print "for help use --help or -h"

if __name__ == "__main__":
    main()