#!/usr/bin/env python """ 12tone.py -- print 12-tone matrix Copyright (c) 2004 L. Peter Deutsch. All rights reserved. See below for copying permission. This program prints the matrix of transposed prime and inverted rows derived from a 12-tone row. Its usage is: """ USAGE = 'Usage: python 12tone.py note1 ... note12 > matrix.txt' """ Each note is a letter A..G (upper or lower case) optionally followed by a # or + for sharp or a &, b, or - for flat. There must be exactly 12 notes with no repetitions of the same pitch. The matrix always uses sharps to indicate displaced tones. As usual, the original row is the top row, and its inversion is the left column. Example of usage (with a row by Dalla Piccola): python 12tone.py A# B D# F# G# D Db G C A E > matrix.txt ---------------- Copying permission ---------------- This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. If you redistribute modified sources, we would appreciate that you include in them history information documenting your changes. """ import sys from cStringIO import StringIO PITCH2NOTE = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') NOTE2PITCH = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11} ACCDELTA = {'#': 1, '+': 1, '&': -1, 'b': -1, '-': -1} def formatRow(items, sepr = '|'): buf = StringIO() buf.write(sepr) for item in items: buf.write(item.rjust(3) + ' ' + sepr) return buf.getvalue() def pitch(row, rownum, colnum): return (row[colnum] + row[0] - row[rownum] + 12) % 12 def interval(lower, upper): diff = upper - lower if diff < 0: diff += 12 return diff def printMatrix(row): print rule = ' +' + 12 * '----+' top = ' ' + formatRow(map(lambda p: str(interval(row[0], p)), row), ' ') print top print rule for i in range(12): notes = map(lambda j: PITCH2NOTE[pitch(row, i, j)].ljust(2), range(12)) label = ' %2d ' % interval(row[0], pitch(row, i, 0)) print label + formatRow(notes) + label print rule print top def main(): args = sys.argv[1:] if len(args) != 12: print USAGE return row = [] for note in args: try: if len(note) == 1: pitch = NOTE2PITCH[note.upper()] elif len(note) == 2: pitch = NOTE2PITCH[note[0].upper()] + ACCDELTA[note[1]] else: pitch = {}[note] # causes KeyError except KeyError: print 'Invalid note:', note return if not 0 <= pitch < 12: print 'Invalid note:', note return row.append(pitch) copy = row[:] copy.sort() if copy != range(12): print 'Duplicate note(s) not allowed' return printMatrix(row) if __name__ == '__main__': main()