From 9562a463e97fac3eea80ea09519a50eab4cebfa3 Mon Sep 17 00:00:00 2001 From: olpc user Date: Tue, 04 Mar 2014 01:27:09 +0000 Subject: modified: MPScore.py modified: Musicpainter.py new file: common/Config.py new file: common/ConfigR.py new file: common/Util/CSoundClient.py new file: common/Util/CSoundNote.py new file: common/Util/InstrumentDB.py new file: common/Util/Instruments.py new file: common/Util/NoteDB.py --- diff --git a/MPScore.py b/MPScore.py index e0d0b0a..e8025c6 100644 --- a/MPScore.py +++ b/MPScore.py @@ -1,5 +1,7 @@ import os +from common.Util.CSoundNote import CSoundNote + from Brick import * from copy import deepcopy @@ -382,7 +384,10 @@ class MPScore: eventStr = "i" + str(inst) + " 0 0.8 1 " + self.note2str(note) if _showCSoundMsg: print "CSound Event: " + eventStr - csound.sendLinevt(eventStr) + #csound.sendLinevt(eventStr) + print str(note) + csnote = CSoundNote(0, note, 0.7, -0.5, 0.5, 0, 23) + self.main.csnd.play(csnote, 0.3) def note_off(self, csound, inst, note): if inst == 8: @@ -399,7 +404,7 @@ class MPScore: eventStr = "i-5" + str(inst) + " " + str(d) + " 0 1 " + self.note2str(note) if _showCSoundMsg: print "CSound Event: " + eventStr - csound.sendLinevt(eventStr) + #csound.sendLinevt(eventStr) def drag_on_vol(self, csound, toolsel, colorsel, gy, v): if colorsel == 7: diff --git a/Musicpainter.py b/Musicpainter.py index 96301ea..d30de53 100644 --- a/Musicpainter.py +++ b/Musicpainter.py @@ -9,8 +9,8 @@ import pygtk pygtk.require('2.0') import gtk -#import gobject -from gi.repository import GObject +import gobject +#from gi.repository import GObject import cairo import pango import random @@ -43,6 +43,7 @@ from common.Util.NoteDB import Note from common.Util.CSoundNote import CSoundNote from common.Util.CSoundClient import new_csound_client from common.Util import InstrumentDB +from common.Util import Instruments #from CsoundXOAgent import CsoundXOAgent @@ -184,6 +185,15 @@ class Musicpainter: if not _noCsound and self.hasCSound == False: self.instrumentDB = InstrumentDB.getRef() self.csnd = new_csound_client() + self.csnd.setTempo(Config.PLAYER_TEMPO) + time.sleep(0.01) + for i in range(21): + self.csnd.setTrackVolume(100, i) + for i in range(10): + r = str(i+1) + self.csnd.load_instrument("guidice"+r) + self.csnd.load_instrument("piano") + self.csound = 0 # self.csound = CsoundXOAgent("localhost", random, self.score.timeUpdate, self.platform) # time.sleep(2) # self.hasCSound = (self.csound.status == 1) diff --git a/common/Config.py b/common/Config.py new file mode 100644 index 0000000..3f11242 --- /dev/null +++ b/common/Config.py @@ -0,0 +1,331 @@ +# -*- coding: utf-8 -*- +import os +import sys +import time +import gettext +import logging +from os.path import join + +#from gi.repository import Gdk + +from sugar.activity.activity import get_bundle_path, get_activity_root +from sugar import env + + +#QUICKLOAD = os.path.isfile("QUICKLOAD") # skip loading inessential comenents to speed things up + +FEATURES_OGG = True +FEATURES_MIC = None +FEATURES_LAB = None +FEATURES_NEWSOUNDS = None +FEATURES_GRAB_MOUSE = None + +if os.path.isfile("DEBUG"): + f = open("DEBUG") + l = f.read(10) + f.close() + if len(l): + DEBUG = int(l) + else: + DEBUG = 99 +else: + DEBUG = int(os.getenv("TAMTAM_DEBUG", "0")) + +logging.debug("Debug Level %d" % (DEBUG)) + +#PATHS + +TAM_TAM_ROOT = get_bundle_path() +INSTANCE_DIR = join(get_activity_root(), 'instance') +TMP_DIR = join(get_activity_root(), 'tmp') + +logging.debug('INFO: loaded TAMTAM_ROOT=%s' % TAM_TAM_ROOT) + +DATA_DIR = join(get_activity_root(), 'data') +SNDS_INFO_DIR = join(get_activity_root(), 'data', 'snds_info') +FILES_DIR = join(TAM_TAM_ROOT, "common", "Resources") +SOUNDS_DIR = join(FILES_DIR, "Sounds", "") +IMAGE_ROOT = join(FILES_DIR, "Images", "") + +for i in (INSTANCE_DIR, DATA_DIR, SNDS_INFO_DIR, TMP_DIR): + if not os.path.isdir(i): + os.makedirs(i) + +#PLUGIN +PLUGIN_DEBUG = os.getenv("CSOUND_LOGFILE", "") +PLUGIN_VERBOSE = DEBUG +PLUGIN_UNIVORC = join(FILES_DIR, "tamtamorc.csd") +PLUGIN_RATE = 16000 + +try: + from sugar3.graphics.toolbarbox import ToolbarBox, ToolbarButton + HAVE_TOOLBOX = True +except ImportError: + HAVE_TOOLBOX = False + +############## +## SOUNDS +############## + +ARECORD = "arecord " + os.getenv("TAMTAM_ARECORD", + "-f S16_LE -t wav -r 16000 -c2 -D hw:0,0") + +LOW, MID, HIGH, PUNCH = range(4) + +INSTRUMENT_TABLE_OFFSET = 5000 +INST_FREE = 5000 +INST_TIED = 5001 +INST_SIMP = 5011 +INST_PERC = 5021 + +CATEGORIES = ['all', 'animals', 'concret', 'keyboard', 'people', 'percussions', 'strings', 'winds'] + +if FEATURES_MIC or FEATURES_LAB: + CATEGORIES.append('mysounds') + +#CSOUND COMMANDS +CSOUND_LOAD_INSTRUMENT = 'f%d 0 0 -1 "%s" 0 0 0' +CSOUND_MIC_RECORD = 'i5201 0 5 %d' +CSOUND_UNLOAD_TABLES = 'i%d 0 0.1 %d' % (INST_FREE, 150) # removed magic number +CSOUND_NOTE_OFF = 'i %s.%s .2 0.01 1. 0. 0. 0.5 %d 0 0 0 0' % ('%d', '%d', INSTRUMENT_TABLE_OFFSET) +CSOUND_LOAD_LS_INSTRUMENT = 'f4999 0 0 -1 \"%s\" 0 0 0' +CSOUND_PLAY_LS_NOTE = 'i %i 0 -1' +CSOUND_STOP_LS_NOTE = 'i 5022 0 0.5' +CSOUND_RECORD_PERF = 'i5400 0 -1 "%s"' +CSOUND_STOP_RECORD_PERF = 'i5401 4 1 "%s"' + + +################# +## GUI CONSTANTS +################# + +#if max(Gdk.Screen.width(), Gdk.Screen.height()) <= 800: + # Images created using `convert $i -resize 73%` command +# IMAGE_ROOT_SCALED = join(IMAGE_ROOT, '73', '') +# scale = lambda x: int(x * .73) +#else: +IMAGE_ROOT_SCALED = IMAGE_ROOT +scale = lambda x: x + +LANGUAGE = 'En' +MAIN_WINDOW_PADDING = 5 + +BG_COLOR = '#404040' +FG_COLOR = '#818286' + +NOTE_HEIGHT = scale(9) # pixels +NOTE_IMAGE_PADDING = scale(6) +NOTE_IMAGE_PADDING_MUL2 = NOTE_IMAGE_PADDING * 2 +NOTE_IMAGE_TAIL = scale(1059) +NOTE_IMAGE_ENDLENGTH = scale(12) +HIT_HEIGHT = scale(13) # pixels +HIT_IMAGE_PADDING = scale(6) +HIT_IMAGE_PADDING_MUL2 = HIT_IMAGE_PADDING * 2 +TRACK_SPACING = scale(4) +TRACK_SPACING_DIV2 = TRACK_SPACING//2 +TRACK_COLORS = [("#00290B", "#00E847"), \ + ("#3F0200", "#E72500"), \ + ("#002642", "#0090EA"), \ + ("#313D00", "#F9EF00"), \ + ("#17083B", "#4A00ED")] +#TRACK_COLORS = [ ( "#00591B", "#00E847" ), \ +# ( "#6F1200", "#E72500" ), \ +# ( "#004682", "#0090EA" ), \ +# ( "#716D00", "#F9EF00" ), \ +# ( "#37187B", "#4A00ED" ) ] +BEAT_COLOR = "#999999" +BEAT_LINE_SIZE = 2 +PLAYHEAD_COLOR = "#666666" +PLAYHEAD_SIZE = 2 +PLAYHEAD_SIZE_DIV2 = PLAYHEAD_SIZE / 2.0 +MARQUEE_COLOR = "#FFFFFF" +MARQUEE_SIZE = 2 + +PAGE_BORDER_SIZE = 2 +PAGE_SELECTED_BORDER_SIZE = 5 +PAGE_WIDTH = scale(100) +PAGE_HEIGHT = scale(25) + +PAGE_THUMBNAIL_WIDTH = scale(92) +PAGE_THUMBNAIL_WIDTH_DIV2 = PAGE_THUMBNAIL_WIDTH / 2 +PAGE_THUMBNAIL_HEIGHT = scale(65) + +THUMBNAIL_TRACK_RECT = [ + (scale(2), scale(4), scale(83), scale(10)), + (scale(2), scale(14), scale(83), scale(10)), + (scale(2), scale(24), scale(83), scale(10)), + (scale(2), scale(34), scale(83), scale(10)), + (scale(2), scale(44), scale(83), scale(13)), + ] +THUMBNAIL_DRAG_COLOR = "#000000" +THUMBNAIL_TRACK_COLOR = "#FF0000" +THUMBNAIL_SELECTED_COLOR = "#2266FF" +THUMBNAIL_DISPLAYED_COLOR = "#CC1133" + +TOOLBAR_BCK_COLOR = '#404040' +WHITE_COLOR = '#FFFFFF' +### miniTamTam/SYNTHLAB SPECIFIC ### +INST_BCK_COLOR = '#999999' +PANEL_BCK_COLOR = '#CCCCCC' +PANEL_COLOR = '#CCCCCC' +SL_LINE_COLOR = "#666666" +SL_HIGHLIGHT_COLOR = "#FFFFFF" +SL_OVER_WIRE_COLOR = "#FFFFFF" +SL_OVER_GATE_COLOR = "#00FF18" +SL_OVER_GATE_REJECT_COLOR = "#B30000" +PANEL_RADIUS = 10 +PANEL_SPACING = 2 + +###Instrument Panel### +CATEGORY_BCK_COLOR = '#222222' +INSTRUMENT_GRID_COLOR = '#CCCCCC' + +###Welcome Screen Specific### +WS_PANEL_COLOR = '#404040' +WS_BCK_COLOR = '#CCCCCC' + +# hardware keycodes for mod keys +MOD_LSHIFT = 50 +MOD_RSHIFT = 62 +MOD_LCTRL = 37 +MOD_RCTRL = 109 +MOD_LALT = 64 +MOD_RALT = 113 + +######## +## Things that don't belong! +####### + + +class _ModKeys: + + def __init__(self): + self.shiftDown = False + self.ctrlDown = False + self.altDown = False + + def keyPress(self, code): + if code == MOD_LSHIFT or code == MOD_RSHIFT: + self.shiftDown = True + elif code == MOD_LCTRL or code == MOD_RCTRL: + self.ctrlDown = True + elif code == MOD_LALT or code == MOD_RALT: + self.altDown = True + + def keyRelease(self, code): + if code == MOD_LSHIFT or code == MOD_RSHIFT: + self.shiftDown = False + elif code == MOD_LCTRL or code == MOD_RCTRL: + self.ctrlDown = False + elif code == MOD_LALT or code == MOD_RALT: + self.altDown = False + +ModKeys = _ModKeys() + + +############ +## EDIT DEFAULTS +############ + +#DEFAULTS +PLAYER_TEMPO = 100 +PLAYER_TEMPO_LOWER = 30 +PLAYER_TEMPO_UPPER = 160 +DEFAULT_VOLUME = 50 + +#NUMERICAL CONSTANTS +NUMBER_OF_POSSIBLE_PITCHES = 25 +MINIMUM_PITCH = 24 +MAXIMUM_PITCH = MINIMUM_PITCH + NUMBER_OF_POSSIBLE_PITCHES - 1 +NUMBER_OF_POSSIBLE_PITCHES_DRUM = 13 +PITCH_STEP_DRUM = 2 +MINIMUM_PITCH_DRUM = 24 +MAXIMUM_PITCH_DRUM = MINIMUM_PITCH_DRUM + PITCH_STEP_DRUM*(NUMBER_OF_POSSIBLE_PITCHES_DRUM - 1) +MINIMUM_NOTE_DURATION = 1 # ticks +MS_PER_MINUTE = 60000.0 +TICKS_PER_BEAT = 12 +TICKS_PER_BEAT_DIV2 = TICKS_PER_BEAT/2 +MAXIMUM_BEATS = 12 # maximum beats per page +NUMBER_OF_TRACKS = 5 +NUMBER_OF_PAGES = 2 + +MINIMUM_AMPLITUDE = 0 +MAXIMUM_AMPLITUDE = 1 + +DEFAULT_GRID = 3 +DEFAULT_GRID_DIV2 = DEFAULT_GRID / 2.0 + +#################### +## KeyMapping +#################### + +LOOP_KEYS = [17, 18, 19, 20, 21, 32, 33, 34, 35, 45, 46, 47, 48, 51, 60, 61] +# Key = Hardware Keycode Value = Note + +KEY_MAP_PIANO = {24: 36, # Q + 25: 38, # W + 26: 40, # E + 27: 41, # R + 28: 43, # T + 29: 45, # Y + 30: 47, # U + 31: 48, # I + + 11: 37, # 2 + 12: 39, # 3 + 14: 42, # 5 + 15: 44, # 6 + 16: 46, # 7 + + 39: 25, # S + 40: 27, # D + 42: 30, # G + 43: 32, # H + 44: 34, # J + 46: 37, # L + + 52: 24, # Z + 53: 26, # X + 54: 28, # C + 55: 29, # V + 56: 31, # B + 57: 33, # N + 58: 35, # M + 59: 36} # , + + +KEY_MAP_NOTPIANO = {24: 24, # Q + 25: 25, # W + 26: 26, # E + 27: 27, # R + 28: 28, # T + 29: 29, # Y + 30: 30, # U + 31: 31, # I + 32: 32, # O + 33: 33, # P + + 38: 34, # A + 39: 35, # S + 40: 36, # D + 41: 37, # F + 42: 38, # G + 43: 39, # H + 44: 40, # J + 45: 41, # K + 46: 42, # L + + 52: 43, # Z + 53: 44, # X + 54: 45, # C + 55: 46, # V + 56: 47, # B + 57: 48} # N + +KEY_MAP = KEY_MAP_PIANO + + +def imagefile(filename): + if filename and not filename.startswith(os.sep): + filename = IMAGE_ROOT_SCALED + filename + return filename diff --git a/common/ConfigR.py b/common/ConfigR.py new file mode 100644 index 0000000..316aace --- /dev/null +++ b/common/ConfigR.py @@ -0,0 +1,332 @@ +# -*- coding: utf-8 -*- +import os +import sys +import time +import gettext +import logging +from os.path import join + +from gi.repository import Gdk + +#QUICKLOAD = os.path.isfile("QUICKLOAD") # skip loading inessential comenents to speed things up + +FEATURES_OGG = True +FEATURES_MIC = None +FEATURES_LAB = None +FEATURES_NEWSOUNDS = None +FEATURES_GRAB_MOUSE = None + +if os.path.isfile("DEBUG"): + f = open("DEBUG") + l = f.read(10) + f.close() + if len(l): + DEBUG = int(l) + else: + DEBUG = 99 +else: + DEBUG = int(os.getenv("TAMTAM_DEBUG", "0")) + +logging.debug("Debug Level %d" % (DEBUG)) + +#PATHS + +TAM_TAM_ROOT = get_bundle_path() +INSTANCE_DIR = join(get_activity_root(), 'instance') +TMP_DIR = join(get_activity_root(), 'tmp') + +logging.debug('INFO: loaded TAMTAM_ROOT=%s' % TAM_TAM_ROOT) + +DATA_DIR = join(get_activity_root(), 'data') +SNDS_INFO_DIR = join(get_activity_root(), 'data', 'snds_info') +FILES_DIR = join(TAM_TAM_ROOT, "common", "Resources") +SOUNDS_DIR = join(FILES_DIR, "Sounds", "") +IMAGE_ROOT = join(FILES_DIR, "Images", "") + +for i in (INSTANCE_DIR, DATA_DIR, SNDS_INFO_DIR, TMP_DIR): + if not os.path.isdir(i): + os.makedirs(i) + +#PLUGIN +PLUGIN_DEBUG = os.getenv("CSOUND_LOGFILE", "") +PLUGIN_VERBOSE = DEBUG +PLUGIN_UNIVORC = join(FILES_DIR, "tamtamorc.csd") +PLUGIN_RATE = 16000 + +try: + from sugar3.graphics.toolbarbox import ToolbarBox, ToolbarButton + HAVE_TOOLBOX = True +except ImportError: + HAVE_TOOLBOX = False + +############## +## SOUNDS +############## + +ARECORD = "arecord " + os.getenv("TAMTAM_ARECORD", + "-f S16_LE -t wav -r 16000 -c2 -D hw:0,0") + +LOW, MID, HIGH, PUNCH = range(4) + +INSTRUMENT_TABLE_OFFSET = 5000 +INST_FREE = 5000 +INST_TIED = 5001 +INST_SIMP = 5011 +INST_PERC = 5021 + +CATEGORIES = ['all', 'animals', 'concret', 'keyboard', 'people', 'percussions', 'strings', 'winds'] + +if FEATURES_MIC or FEATURES_LAB: + CATEGORIES.append('mysounds') + +#CSOUND COMMANDS +CSOUND_LOAD_INSTRUMENT = 'f%d 0 0 -1 "%s" 0 0 0' +CSOUND_MIC_RECORD = 'i5201 0 5 %d' +CSOUND_UNLOAD_TABLES = 'i%d 0 0.1 %d' % (INST_FREE, 150) # removed magic number +CSOUND_NOTE_OFF = 'i %s.%s .2 0.01 1. 0. 0. 0.5 %d 0 0 0 0' % ('%d', '%d', INSTRUMENT_TABLE_OFFSET) +CSOUND_LOAD_LS_INSTRUMENT = 'f4999 0 0 -1 \"%s\" 0 0 0' +CSOUND_PLAY_LS_NOTE = 'i %i 0 -1' +CSOUND_STOP_LS_NOTE = 'i 5022 0 0.5' +CSOUND_RECORD_PERF = 'i5400 0 -1 "%s"' +CSOUND_STOP_RECORD_PERF = 'i5401 4 1 "%s"' + + +################# +## GUI CONSTANTS +################# + +if max(Gdk.Screen.width(), Gdk.Screen.height()) <= 800: + # Images created using `convert $i -resize 73%` command + IMAGE_ROOT_SCALED = join(IMAGE_ROOT, '73', '') + scale = lambda x: int(x * .73) +else: + IMAGE_ROOT_SCALED = IMAGE_ROOT + scale = lambda x: x + +LANGUAGE = 'En' +MAIN_WINDOW_PADDING = 5 + +BG_COLOR = '#404040' +FG_COLOR = '#818286' + +NOTE_HEIGHT = scale(9) # pixels +NOTE_IMAGE_PADDING = scale(6) +NOTE_IMAGE_PADDING_MUL2 = NOTE_IMAGE_PADDING * 2 +NOTE_IMAGE_TAIL = scale(1059) +NOTE_IMAGE_ENDLENGTH = scale(12) +HIT_HEIGHT = scale(13) # pixels +HIT_IMAGE_PADDING = scale(6) +HIT_IMAGE_PADDING_MUL2 = HIT_IMAGE_PADDING * 2 +TRACK_SPACING = scale(4) +TRACK_SPACING_DIV2 = TRACK_SPACING//2 +TRACK_COLORS = [("#00290B", "#00E847"), \ + ("#3F0200", "#E72500"), \ + ("#002642", "#0090EA"), \ + ("#313D00", "#F9EF00"), \ + ("#17083B", "#4A00ED")] +#TRACK_COLORS = [ ( "#00591B", "#00E847" ), \ +# ( "#6F1200", "#E72500" ), \ +# ( "#004682", "#0090EA" ), \ +# ( "#716D00", "#F9EF00" ), \ +# ( "#37187B", "#4A00ED" ) ] +BEAT_COLOR = "#999999" +BEAT_LINE_SIZE = 2 +PLAYHEAD_COLOR = "#666666" +PLAYHEAD_SIZE = 2 +PLAYHEAD_SIZE_DIV2 = PLAYHEAD_SIZE / 2.0 +MARQUEE_COLOR = "#FFFFFF" +MARQUEE_SIZE = 2 + +PAGE_BORDER_SIZE = 2 +PAGE_SELECTED_BORDER_SIZE = 5 +PAGE_WIDTH = scale(100) +PAGE_HEIGHT = scale(25) + +PAGE_THUMBNAIL_WIDTH = scale(92) +PAGE_THUMBNAIL_WIDTH_DIV2 = PAGE_THUMBNAIL_WIDTH / 2 +PAGE_THUMBNAIL_HEIGHT = scale(65) + +THUMBNAIL_TRACK_RECT = [ + (scale(2), scale(4), scale(83), scale(10)), + (scale(2), scale(14), scale(83), scale(10)), + (scale(2), scale(24), scale(83), scale(10)), + (scale(2), scale(34), scale(83), scale(10)), + (scale(2), scale(44), scale(83), scale(13)), + ] +THUMBNAIL_DRAG_COLOR = "#000000" +THUMBNAIL_TRACK_COLOR = "#FF0000" +THUMBNAIL_SELECTED_COLOR = "#2266FF" +THUMBNAIL_DISPLAYED_COLOR = "#CC1133" + +TOOLBAR_BCK_COLOR = '#404040' +WHITE_COLOR = '#FFFFFF' +### miniTamTam/SYNTHLAB SPECIFIC ### +INST_BCK_COLOR = '#999999' +PANEL_BCK_COLOR = '#CCCCCC' +PANEL_COLOR = '#CCCCCC' +SL_LINE_COLOR = "#666666" +SL_HIGHLIGHT_COLOR = "#FFFFFF" +SL_OVER_WIRE_COLOR = "#FFFFFF" +SL_OVER_GATE_COLOR = "#00FF18" +SL_OVER_GATE_REJECT_COLOR = "#B30000" +PANEL_RADIUS = 10 +PANEL_SPACING = 2 + +###Instrument Panel### +CATEGORY_BCK_COLOR = '#222222' +INSTRUMENT_GRID_COLOR = '#CCCCCC' + +###Welcome Screen Specific### +WS_PANEL_COLOR = '#404040' +WS_BCK_COLOR = '#CCCCCC' + +# hardware keycodes for mod keys +MOD_LSHIFT = 50 +MOD_RSHIFT = 62 +MOD_LCTRL = 37 +MOD_RCTRL = 109 +MOD_LALT = 64 +MOD_RALT = 113 + +######## +## Things that don't belong! +####### + + +class _ModKeys: + + def __init__(self): + self.shiftDown = False + self.ctrlDown = False + self.altDown = False + + def keyPress(self, code): + if code == MOD_LSHIFT or code == MOD_RSHIFT: + self.shiftDown = True + elif code == MOD_LCTRL or code == MOD_RCTRL: + self.ctrlDown = True + elif code == MOD_LALT or code == MOD_RALT: + self.altDown = True + + def keyRelease(self, code): + if code == MOD_LSHIFT or code == MOD_RSHIFT: + self.shiftDown = False + elif code == MOD_LCTRL or code == MOD_RCTRL: + self.ctrlDown = False + elif code == MOD_LALT or code == MOD_RALT: + self.altDown = False + +ModKeys = _ModKeys() + + +############ +## EDIT DEFAULTS +############ + +#DEFAULTS +PLAYER_TEMPO = 100 +PLAYER_TEMPO_LOWER = 30 +PLAYER_TEMPO_UPPER = 160 +DEFAULT_VOLUME = 50 + +#NUMERICAL CONSTANTS +NUMBER_OF_POSSIBLE_PITCHES = 25 +MINIMUM_PITCH = 24 +MAXIMUM_PITCH = MINIMUM_PITCH + NUMBER_OF_POSSIBLE_PITCHES - 1 +NUMBER_OF_POSSIBLE_PITCHES_DRUM = 13 +PITCH_STEP_DRUM = 2 +MINIMUM_PITCH_DRUM = 24 +MAXIMUM_PITCH_DRUM = MINIMUM_PITCH_DRUM + PITCH_STEP_DRUM*(NUMBER_OF_POSSIBLE_PITCHES_DRUM - 1) +MINIMUM_NOTE_DURATION = 1 # ticks +MS_PER_MINUTE = 60000.0 +TICKS_PER_BEAT = 12 +TICKS_PER_BEAT_DIV2 = TICKS_PER_BEAT/2 +MAXIMUM_BEATS = 12 # maximum beats per page +NUMBER_OF_TRACKS = 5 +NUMBER_OF_PAGES = 2 + +MINIMUM_AMPLITUDE = 0 +MAXIMUM_AMPLITUDE = 1 + +DEFAULT_GRID = 3 +DEFAULT_GRID_DIV2 = DEFAULT_GRID / 2.0 + +#################### +## KeyMapping +#################### + +LOOP_KEYS = [17, 18, 19, 20, 21, 32, 33, 34, 35, 45, 46, 47, 48, 51, 60, 61] +# Key = Hardware Keycode Value = Note + +KEY_MAP_PIANO = {24: 36, # Q + 25: 38, # W + 26: 40, # E + 27: 41, # R + 28: 43, # T + 29: 45, # Y + 30: 47, # U + 31: 48, # I + + 11: 37, # 2 + 12: 39, # 3 + 14: 42, # 5 + 15: 44, # 6 + 16: 46, # 7 + + 39: 25, # S + 40: 27, # D + 42: 30, # G + 43: 32, # H + 44: 34, # J + 46: 37, # L + + 52: 24, # Z + 53: 26, # X + 54: 28, # C + 55: 29, # V + 56: 31, # B + 57: 33, # N + 58: 35, # M + 59: 36} # , + + +KEY_MAP_NOTPIANO = {24: 24, # Q + 25: 25, # W + 26: 26, # E + 27: 27, # R + 28: 28, # T + 29: 29, # Y + 30: 30, # U + 31: 31, # I + 32: 32, # O + 33: 33, # P + + 38: 34, # A + 39: 35, # S + 40: 36, # D + 41: 37, # F + 42: 38, # G + 43: 39, # H + 44: 40, # J + 45: 41, # K + 46: 42, # L + + 52: 43, # Z + 53: 44, # X + 54: 45, # C + 55: 46, # V + 56: 47, # B + 57: 48} # N + +KEY_MAP = KEY_MAP_PIANO + +def get_bundle_path(): + return "./" + +def get_activity_root(): + return "./" + +def imagefile(filename): + if filename and not filename.startswith(os.sep): + filename = IMAGE_ROOT_SCALED + filename + return filename diff --git a/common/Util/CSoundClient.py b/common/Util/CSoundClient.py new file mode 100644 index 0000000..131802f --- /dev/null +++ b/common/Util/CSoundClient.py @@ -0,0 +1,412 @@ +import os +import socket +import select +import sys +import threading +import time +import array +from math import sqrt + +from common.Util.Clooper import * +import common.Config as Config + +from common.Generation.GenerationConstants import GenerationConstants +from common.Util import NoteDB +import common.Util.InstrumentDB as InstrumentDB + +loadedInstruments = [] + +_note_template = array.array('f', [0] * 19) + + +def _new_note_array(): + return _note_template.__copy__() + +def _noteid(dbnote): + return (dbnote.page << 16) + dbnote.id + +_loop_default=0 + + +class _CSoundClientPlugin: + + #array index constants for csound + (INSTR_TRACK, \ + ONSET, \ + DURATION, \ + PITCH, + REVERBSEND, \ + AMPLITUDE, \ + PAN, \ + INST_ID, \ + ATTACK, \ + DECAY, \ + FILTERTYPE, \ + FILTERCUTOFF, \ + INSTRUMENT2 ) = range(13) + + def __init__(self): + sc_initialize( Config.PLUGIN_UNIVORC, Config.PLUGIN_DEBUG, + Config.PLUGIN_VERBOSE, Config.PLUGIN_RATE) + self.on = False + #self.masterVolume = 100.0 + self.periods_per_buffer = 2 + global _loop_default + _loop_default = self.loopCreate() + self.instrumentDB = InstrumentDB.getRef() + + self.jamesSux = {} # temporyary dictionary of loopId: loopNumTicks, while I wait for james to implement it properly + + def __del__(self): + self.connect(False) + sc_destroy() + + def setChannel(self, name, val): + sc_setChannel(name, val) + + def setMasterVolume(self, volume): + sc_setChannel('masterVolume', volume) + + def setTrackVolume(self, volume, trackId): + sc_setChannel('trackVolume' + str(trackId + 1), volume) + + def setTrackpadX(self, value): + sc_setChannel('trackpadX', value) + + def setTrackpadY(self, value): + sc_setChannel('trackpadY', value) + + def micRecording(self, table): + sc_inputMessage(Config.CSOUND_MIC_RECORD % table) + + def load_mic_instrument(self, inst): + fileName = Config.DATA_DIR + '/' + inst + instrumentId = Config.INSTRUMENT_TABLE_OFFSET + self.instrumentDB.instNamed[inst].instrumentId + sc_inputMessage(Config.CSOUND_LOAD_INSTRUMENT % (instrumentId, fileName)) + + def load_synth_instrument(self, inst): + fileName = Config.DATA_DIR + '/' + inst + instrumentId = Config.INSTRUMENT_TABLE_OFFSET + self.instrumentDB.instNamed[inst].instrumentId + sc_inputMessage(Config.CSOUND_LOAD_INSTRUMENT % (instrumentId, fileName)) + + def load_ls_instrument(self, inst): + fileName = Config.DATA_DIR + '/' + inst + sc_inputMessage(Config.CSOUND_LOAD_LS_INSTRUMENT % fileName) + + def load_instruments(self): + for instrumentSoundFile in self.instrumentDB.instNamed.keys(): + if instrumentSoundFile[0:3] == 'mic' or instrumentSoundFile[0:3] == 'lab' or self.instrumentDB.instNamed[instrumentSoundFile].category == 'mysounds': + fileName = Config.DATA_DIR + '/' + instrumentSoundFile + else: + fileName = Config.SOUNDS_DIR + "/" + instrumentSoundFile + instrumentId = Config.INSTRUMENT_TABLE_OFFSET + self.instrumentDB.instNamed[ instrumentSoundFile ].instrumentId + sc_inputMessage( Config.CSOUND_LOAD_INSTRUMENT % (instrumentId, fileName) ) + + def load_instrument(self, inst): + if not inst in loadedInstruments: + if inst[0:3] == 'mic' or inst[0:3] == 'lab' or self.instrumentDB.instNamed[inst].category == 'mysounds': + if os.path.isfile(os.path.join(Config.DATA_DIR, inst)): + fileName = os.path.join(Config.DATA_DIR, inst) + else: + fileName = os.path.join(Config.SOUNDS_DIR, 'armbone') + else: + fileName = os.path.join(Config.SOUNDS_DIR, inst) + instrumentId = Config.INSTRUMENT_TABLE_OFFSET + self.instrumentDB.instNamed[ inst ].instrumentId + sc_inputMessage( Config.CSOUND_LOAD_INSTRUMENT % (instrumentId, fileName) ) + loadedInstruments.append(inst) + + def load_drumkit(self, kit): + if not kit in loadedInstruments: + for i in self.instrumentDB.instNamed[kit].kit.values(): + fileName = Config.SOUNDS_DIR + "/" + i + instrumentId = Config.INSTRUMENT_TABLE_OFFSET + self.instrumentDB.instNamed[ i ].instrumentId + sc_inputMessage( Config.CSOUND_LOAD_INSTRUMENT % (instrumentId, fileName) ) + loadedInstruments.append(i) + loadedInstruments.append(kit) + + def connect(self, init=True): + def reconnect(): + if sc_start(self.periods_per_buffer) : + if (Config.DEBUG > 0) : print 'ERROR connecting' + else: + self.on = True + def disconnect(): + if sc_stop() : + if (Config.DEBUG > 0) : print 'ERROR connecting' + else: + self.on = False + + if init and not self.on : + reconnect() + if not init and self.on : + disconnect() + + def destroy(self): + self.connect(False) + sc_destroy() + + def inputMessage(self,msg): + sc_inputMessage(msg) + + def getTick(self): + return sc_getTickf() + + def adjustTick(self, amt): + sc_adjustTick(amt) + + def setTempo(self,t): + if (Config.DEBUG > 3) : print 'INFO: loop tempo: %f -> %f' % (t, 60.0 / (Config.TICKS_PER_BEAT * t)) + sc_setTickDuration(60.0 / (Config.TICKS_PER_BEAT * t)) + + + def loopCreate(self): + return sc_loop_new() + + def loopDestroy(self, loopId): + sc_loop_delete(loopId) + try: + del self.jamesSux[ loopId ] + except: + pass + + def loopClear(self): + global _loop_default + sc_loop_delete(_loop_default) + _loop_default = sc_loop_new() + try: + del self.jamesSux[ loopId ] + except: + pass + + + # this is function deletes an Event from a loop + # TODO: rename this function + def loopDelete(self, dbnote, loopId=_loop_default): + sc_loop_delScoreEvent( loopId, _noteid(dbnote)) + + def loopDelete1(self, page, id, loopId=_loop_default): + sc_loop_delScoreEvent( loopId, (page << 16) + id) + + def loopStart(self, loopId=_loop_default): + sc_loop_playing(loopId, 1) + + def loopPause(self, loopId=_loop_default): + sc_loop_playing(loopId, 0) + + def loopSetTick(self,t, loopId=_loop_default): + sc_loop_setTickf(loopId, t) + + def loopGetTick(self, loopId=_loop_default): + return sc_loop_getTickf(loopId) + + def loopSetNumTicks(self,n, loopId=_loop_default): + sc_loop_setNumTicks(loopId, n) + self.jamesSux[loopId] = n + + def loopGetNumTicks( self, loopId = _loop_default ): + return self.jamesSux[loopId] + + def loopSetTickDuration(self,d, loopId=_loop_default): + sc_loop_setTickDuration(loopId, d) + james + + def loopDeactivate(self, note = 'all', loopId=_loop_default): + if note == 'all': + sc_loop_deactivate_all(loopId) + else: + if (Config.DEBUG > 0) : print 'ERROR: deactivating a single note is not implemented' + + def loopUpdate(self, note, parameter, value,cmd, loopId=_loop_default): + page = note.page + track = note.track + id = note.id + if note.cs.mode == 'mini': + instrument_id_offset = 0 + elif note.cs.mode == 'edit': + if self.instrumentDB.instId[note.cs.instrumentId].kit != None: + instrument_id_offset = 0 + else: + instrument_id_offset = 100 + if (parameter == NoteDB.PARAMETER.ONSET): + if (Config.DEBUG > 2): print 'INFO: updating onset', (page<<16)+id, value + sc_loop_updateEvent( loopId, (page<<16)+id, 1, value, cmd) + elif (parameter == NoteDB.PARAMETER.PITCH): + if (Config.DEBUG > 2): print 'INFO: updating pitch', (page<<16)+id, value + pitch = value + if self.instrumentDB.instId[note.cs.instrumentId].kit != None: + instrument = self.instrumentDB.instNamed[ + self.instrumentDB.instId[note.cs.instrumentId].kit[pitch]] + csoundInstId = instrument.csoundInstrumentId + csoundTable = Config.INSTRUMENT_TABLE_OFFSET + instrument.instrumentId + if (Config.DEBUG > 2): print 'INFO: updating drum instrument (pitch)', (page<<16)+id, instrument.name, csoundInstId + sc_loop_updateEvent( loopId, (page<<16)+id, 0, (csoundInstId + instrument_id_offset) + note.track * 0.01, -1 ) + sc_loop_updateEvent( loopId, (page<<16)+id, 7, csoundTable , -1 ) + pitch = 1 + else: + pitch = GenerationConstants.TRANSPOSE[ pitch - 24 ] + sc_loop_updateEvent( loopId, (page<<16)+id, 3, pitch, cmd) + elif (parameter == NoteDB.PARAMETER.AMPLITUDE): + if (Config.DEBUG > 2): print 'INFO: updating amp', (page<<16)+id, value + sc_loop_updateEvent( loopId, (page<<16)+id, 5, value, cmd) + elif (parameter == NoteDB.PARAMETER.DURATION): + if (Config.DEBUG > 2): print 'INFO: updating duration', (page<<16)+id, value + sc_loop_updateEvent( loopId, (page<<16)+id, self.DURATION, value, cmd) + elif (parameter == NoteDB.PARAMETER.INSTRUMENT): + pitch = note.cs.pitch + instrument = self.instrumentDB.instId[value] + if instrument.kit != None: + instrument = self.instrumentDB.instNamed[instrument.kit[pitch]] + csoundInstId = instrument.csoundInstrumentId + csoundTable = Config.INSTRUMENT_TABLE_OFFSET + instrument.instrumentId + loopStart = instrument.loopStart + loopEnd = instrument.loopEnd + crossDur = instrument.crossDur + if (Config.DEBUG > 2): print 'INFO: updating instrument', (page<<16)+id, instrument.name, csoundInstId + sc_loop_updateEvent( loopId, (page<<16)+id, 0, (csoundInstId + (track+1) + instrument_id_offset) + note.track * 0.01, cmd ) + sc_loop_updateEvent( loopId, (page<<16)+id, 7, csoundTable, -1 ) + sc_loop_updateEvent( loopId, (page<<16)+id, 12, loopStart, -1 ) + sc_loop_updateEvent( loopId, (page<<16)+id, 13, loopEnd, -1 ) + sc_loop_updateEvent( loopId, (page<<16)+id, 14, crossDur , -1 ) + elif (parameter == NoteDB.PARAMETER.PAN): + sc_loop_updateEvent( loopId, (page<<16)+id, self.PAN, value, cmd) + elif (parameter == NoteDB.PARAMETER.REVERB): + sc_loop_updateEvent( loopId, (page<<16)+id, self.REVERBSEND, value, cmd) + elif (parameter == NoteDB.PARAMETER.ATTACK): + sc_loop_updateEvent( loopId, (page<<16)+id, self.ATTACK, value, cmd) + elif (parameter == NoteDB.PARAMETER.DECAY): + sc_loop_updateEvent( loopId, (page<<16)+id, self.DECAY, value, cmd) + elif (parameter == NoteDB.PARAMETER.FILTERTYPE): + sc_loop_updateEvent( loopId, (page<<16)+id, self.FILTERTYPE, value, cmd) + elif (parameter == NoteDB.PARAMETER.FILTERCUTOFF): + sc_loop_updateEvent( loopId, (page<<16)+id, self.FILTERCUTOFF, value, cmd) + elif (parameter == NoteDB.PARAMETER.INSTRUMENT2): + sc_loop_updateEvent( loopId, (page<<16)+id, self.INSTRUMENT2, value, cmd) + else: + if (Config.DEBUG > 0): print 'ERROR: loopUpdate(): unsupported parameter change' + + def loopPlay(self, dbnote, active, storage=_new_note_array(), + loopId=_loop_default ): + qid = (dbnote.page << 16) + dbnote.id + sc_loop_addScoreEvent( loopId, qid, 1, active, 'i', + self.csnote_to_array(dbnote.cs, storage)) + + def play(self, csnote, secs_per_tick, storage=_new_note_array()): + a = self.csnote_to_array(csnote, storage) + a[self.DURATION] = a[self.DURATION] * secs_per_tick + a[self.ATTACK] = max(a[self.ATTACK]*a[self.DURATION], 0.002) + a[self.DECAY] = max(a[self.DECAY]*a[self.DURATION], 0.002) + sc_scoreEvent('i', a) + + def csnote_to_array(self, csnote, storage): + return self._csnote_to_array1(storage, + csnote.onset, + csnote.pitch, + csnote.amplitude, + csnote.pan, + csnote.duration, + csnote.trackId, + csnote.attack, + csnote.decay, + csnote.reverbSend, + csnote.filterType, + csnote.filterCutoff, + csnote.tied, + csnote.instrumentId, + csnote.mode, + csnote.instrumentId2 ) + + def _csnote_to_array1(self, storage, onset, pitch, amplitude, pan, duration, + trackId, attack, decay, reverbSend, filterType, filterCutoff, + tied, instrumentId, mode, instrumentId2 = -1): + + rval=storage + instrument = self.instrumentDB.instId[instrumentId] + + if instrument.volatile != None: + sound = os.path.join(Config.DATA_DIR, instrument.name) + if os.path.isfile(sound): + st_mtime = os.stat(sound).st_mtime + if st_mtime != instrument.volatile: + instrument.volatile = st_mtime + loadedInstruments.remove(instrument.name) + self.load_instrument(instrument.name) + time.sleep(0.2) + + if instrument.kit != None: + instrument = self.instrumentDB.instNamed[instrument.kit[pitch]] + pitch = 1 + time_in_ticks = 0 + else: + pitch = GenerationConstants.TRANSPOSE[ pitch % 24 ] + time_in_ticks = 1 + + instrument_id_offset = 0 + # condition for tied notes + if instrument.csoundInstrumentId == Config.INST_TIED: + if tied: + if mode == 'mini': + duration = -1 + instrument_id_offset = 0 + elif mode == 'edit': + instrument_id_offset = 0 + if duration < 0: + duration = -1 + else: + if mode == 'mini': + instrument_id_offset = 0 + elif mode == 'edit': + instrument_id_offset = 100 + + if instrument.csoundInstrumentId == Config.INST_SIMP: + if mode == 'mini': + instrument_id_offset = 0 + elif mode == 'edit': + if instrument.name[0:4] == 'drum': + instrument_id_offset = 0 + else: + instrument_id_offset = 100 + + amplitude = amplitude / sqrt(pitch) * instrument.ampScale + rval[0] = (instrument.csoundInstrumentId + \ + (trackId+1) + instrument_id_offset) + trackId * 0.01 + rval[1] = onset + rval[2] = duration + rval[3] = pitch + rval[4] = reverbSend + rval[5] = amplitude + rval[6] = pan + rval[7] = Config.INSTRUMENT_TABLE_OFFSET + instrument.instrumentId + rval[8] = attack + rval[9] = decay + rval[10]= filterType + rval[11]= filterCutoff + rval[12]= float(instrument.loopStart) + rval[13]= float(instrument.loopEnd) + rval[14]= float(instrument.crossDur) + + if instrumentId2 != -1: + instrument2 = self.instrumentDB.instId[instrumentId2] + csInstrumentId2 = (instrument2.csoundInstrumentId + 100) * 0.0001 + rval[15] = Config.INSTRUMENT_TABLE_OFFSET + instrumentId2 + csInstrumentId2 + rval[16] = instrument2.loopStart + rval[17] = instrument2.loopEnd + rval[18] = instrument2.crossDur + else: + rval[15] = -1 + rval[16] = 0 + rval[17] = 0 + rval[18] = 0 + + return rval + +_Client = None + +def new_csound_client(): + global _Client + if _Client == None: + _Client = _CSoundClientPlugin() + _Client.connect(True) + _Client.setMasterVolume(100.0) + #_Client.load_instruments() + time.sleep(0.2) + return _Client diff --git a/common/Util/CSoundNote.py b/common/Util/CSoundNote.py new file mode 100644 index 0000000..a188a62 --- /dev/null +++ b/common/Util/CSoundNote.py @@ -0,0 +1,80 @@ +import common.Config as Config +import common.Util.InstrumentDB as InstrumentDB + +class CSoundNote : + def __init__( self, + onset, + pitch, + amplitude, + pan, + duration, + trackId, + instrumentId = 1, #self.instrumentDB.instNamed["flute"].instrumentId, + attack = 0.005, + decay = 0.098, + reverbSend = 0.1, + filterType = 0, + filterCutoff = 1000, + tied = False, + mode = 'edit', + instrumentId2 = -1 ): + + self.instrumentDB = InstrumentDB.getRef() + + self.onset = onset + self.pitch = pitch + self.amplitude = amplitude + self.pan = pan + self.duration = duration + self.trackId = trackId + self.instrumentId = instrumentId + #temp: catch old code trying to pass in instrument names here + int(instrumentId) + self.attack = attack + self.decay = decay + self.reverbSend = reverbSend + self.filterType = filterType + self.filterCutoff = filterCutoff + self.tied = tied + self.mode = mode + self.instrumentId2 = instrumentId2 + + def clone( self ): + return CSoundNote( self.onset, self.pitch, self.amplitude, self.pan, + self.duration, self.trackId, self.instrumentId, + self.attack, self.decay, self.reverbSend, + self.filterType, self.filterCutoff, self.tied, + self.mode, self.instrumentId2 ) + + def __getstate__unused(self): + return {'onset': self.onset, + 'pitch': self.pitch, + 'amplitude': self.amplitude, + 'pan': self.pan, + 'duration': self.duration, + 'trackId': self.trackId, + 'instrumentId': self.instrumentId, + 'attack': self.attack, + 'decay': self.decay, + 'reverbSend': self.reverbSend, + 'filterType': self.filterType, + 'filterCutoff': self.filterCutoff, + 'tied': self.tied, + 'mode': self.mode } + + def __setstate__unused(self,dict): + self.onset = dict['onset'] + self.pitch = dict['pitch'] + self.amplitude = dict['amplitude'] + self.pan = dict['pan'] + self.duration = dict['duration'] + self.trackId = dict['trackId'] + self.instrumentId = dict['instrumentId'] + self.attack = dict['attack'] + self.decay = dict['decay'] + self.reverbSend = dict['reverbSend'] + self.filterType = dict['filterType'] + self.filterCutoff = dict['filterCutoff'] + self.tied = dict['tied'] + self.mode = dict['mode'] + self.nchanges = 0 diff --git a/common/Util/InstrumentDB.py b/common/Util/InstrumentDB.py new file mode 100644 index 0000000..b8a878e --- /dev/null +++ b/common/Util/InstrumentDB.py @@ -0,0 +1,143 @@ +import os + +############## +## SOUNDS +############## + +class Instrument: + def __init__(self, id): + self.instrumentId = id + self.kitStage = False + self.volatile = None + + # build an Instrument instance from argument list + def loadFromArgs( self, name, csoundInstrumentId, register, loopStart, + loopEnd, crossDur, ampScale, kit, wav, img, category, nameTooltip = "" ): + self.name = name + self.csoundInstrumentId = csoundInstrumentId + print "loadFromArgs:" + name + ":" + str(csoundInstrumentId) + self.instrumentRegister = register + self.loopStart = loopStart + self.loopEnd = loopEnd + self.crossDur = crossDur + self.ampScale = ampScale + self.kit = kit + self.wav = wav + self.img = img + self.category = category + self.nameTooltip = nameTooltip or name + + # build an Instrument instance by parsing a file + def loadFromPath(self, path ): + f = file(path, 'r') + magic = f.readline()[:-1] + if (magic != 'TamTam idf v1'): + raise 'given file has wrong header' + self.name = f.readline()[:-1] + self.csoundInstrumentId = float(f.readline()[:-1]) + self.register = f.readline()[:-1] + self.loopStart = float( f.readline()[:-1]) + self.loopEnd = float( f.readline()[:-1]) + self.crossDur = float( f.readline()[:-1]) + self.ampScale = float( f.readline()[:-1]) + self.kit = None + self.wav = f.readline()[:-1] + self.img = f.readline()[:-1] + self.category = f.readline() + f.close() + +class InstrumentDB: + + # initialize an empty InstrumentDB instance + def __init__(self): + self.labelSet = {'All':set([])} # -> all instruments labelled by + self.inst = [] # all instruments + self.instNamed = {} # -> instrument with that name + self.instId = {} # -> instrument + + # TEMP? add instrument from args + def addInstrumentFromArgs( self, name, csoundInstrumentId, register, loopStart, + loopEnd, crossDur, ampScale, kit, wav, img, category, + kitStage = False, volatile = False, nameTooltip = "" ): + i = Instrument(len(self.inst)) + self.inst += [ i ] + i.loadFromArgs( name, csoundInstrumentId, register, loopStart, loopEnd, crossDur, ampScale, kit, wav, img, category, nameTooltip ) + i.kitStage = kitStage + if volatile: i.volatile = 0 + self.instNamed[ i.name ] = i + self.instId[i.instrumentId] = i + print i.name + ":" + str(i.instrumentId) + + self.labelSet['All'].add(i) + if not self.labelSet.has_key(category): + self.labelSet[category] = set([]) + self.labelSet[category].add( i ) + + # add an instrument to the DB by reading from an instrument definition file + def addInstrument( self, path ): + from common.Util.CSoundClient import new_csound_client + csnd = new_csound_client() + i = Instrument(len(self.inst)) + self.inst += [ i ] + i.loadFromPath( path ) + self.instNamed[ i.name ] = i + self.instId[i.instrumentId] = i + print i.name + ":" + str(i.instrumentId) + #print 'labelSet... ', self.labelSet + self.labelSet['All'].add(i) + if not self.labelSet.has_key(category): + self.labelSet[category] = set([]) + self.labelSet[category].add( i ) + csnd.load_instrument(i.name) + + # try to load each file in a given folder as an instrument def. file + def scanInstrumentDir( self, path ): + dirlist = os.listdir( path ) + for fpath in dirlist: + try : + self.addInstrument( path + fpath ) + except : + print 'ERROR: scanning instrument path %s: file %s invalid' % (path, fpath) + + def getLabels( self ): + return self.labelSet.keys() + + def getSet( self, label ): + return self.labelSet[label] + + def getInstrument( self, id ): + return self.instId[id] + + def getInstrumentByName( self, name ): + try: + return self.instNamed[ name ] + except: + print "Instrument not found: ", name + return self.inst[0] + + def debug_summarize(self): + for i in self.inst: + print i.id, i.name + + for l in self.labelSet: + print l, [ i.name for i in self.labelSet[l]] + + +db_instance = None +def getRef(): + global db_instance + if (None == db_instance): + db_instance = InstrumentDB() + return db_instance + + +if __name__ == "__main__": + i1 = getRef() + i2 = getRef() + + print i1, i2 + + import sys + i1.scanInstrumentDir( sys.argv[1] ) + + i1.debug_summarize() diff --git a/common/Util/Instruments.py b/common/Util/Instruments.py new file mode 100644 index 0000000..1386b25 --- /dev/null +++ b/common/Util/Instruments.py @@ -0,0 +1,680 @@ +import os +from gettext import gettext as _ + +import common.Config as Config +from common.Config import imagefile +import common.Util.InstrumentDB as InstrumentDB + +LOW = Config.LOW +MID = Config.MID +HIGH = Config.HIGH +PUNCH = Config.PUNCH + +INSTRUMENT_TABLE_OFFSET = Config.INSTRUMENT_TABLE_OFFSET +INST_FREE = Config.INST_FREE +INST_TIED = Config.INST_TIED +INST_SIMP = Config.INST_SIMP +INST_PERC = Config.INST_PERC + +instrumentDB = InstrumentDB.getRef() + + +def _addInstrument(name, csoundInstrumentId, instrumentRegister, category, + loopStart, loopEnd, crossDur, ampScale=1, kit=None, kitStage=False, + volatile=False, nameTooltip=""): + instrumentDB.addInstrumentFromArgs(name, csoundInstrumentId, + instrumentRegister, loopStart, loopEnd, crossDur, ampScale, kit, + name, imagefile(name + '.png'), category, kitStage=kitStage, + volatile=volatile, nameTooltip=nameTooltip) + +if Config.FEATURES_MIC: + _addInstrument("mic1", INST_TIED, MID, 'mysounds', .01, 1.99, .01, 1, volatile=True) + _addInstrument("mic2", INST_TIED, MID, 'mysounds', .01, 1.99, .01, 1, volatile=True) + _addInstrument("mic3", INST_TIED, MID, 'mysounds', .01, 1.99, .01, 1, volatile=True) + _addInstrument("mic4", INST_TIED, MID, 'mysounds', .01, 1.99, .01, 1, volatile=True) + +if Config.FEATURES_LAB: + _addInstrument("lab1", INST_SIMP, MID, 'mysounds', 0, 0, 0, 1, volatile=True) + _addInstrument("lab2", INST_SIMP, MID, 'mysounds', 0, 0, 0, 1, volatile=True) + _addInstrument("lab3", INST_SIMP, MID, 'mysounds', 0, 0, 0, 1, volatile=True) + _addInstrument("lab4", INST_SIMP, MID, 'mysounds', 0, 0, 0, 1, volatile=True) + _addInstrument("lab5", INST_SIMP, MID, 'mysounds', 0, 0, 0, 1, volatile=True) + _addInstrument("lab6", INST_SIMP, MID, 'mysounds', 0, 0, 0, 1, volatile=True) + +_addInstrument('ounk', INST_SIMP, MID, 'animals', + 0, 0, 0, 1, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Guinea_pig + nameTooltip=_('Guinea Pig')) +_addInstrument('gam', INST_TIED, HIGH, 'percussions', + .69388, .7536, .02922, 1.4, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Gamelan + nameTooltip=_('Gamelan')) +_addInstrument('guit', INST_TIED, MID, 'strings', + .08592, .75126, .33571, 0.7, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Electric_guitar + nameTooltip=_('Electric Guitar')) +_addInstrument('guitmute', INST_SIMP, MID, 'strings', + 0, 0, 0, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Electric_guitar + nameTooltip=_('Electric Guitar')) +_addInstrument('guitshort', INST_SIMP, MID, 'strings', + 0, 0, 0, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Electric_guitar + nameTooltip=_('Electric Guitar')) +_addInstrument('koto', INST_TIED, HIGH, 'strings', + .56523, .70075, .05954, 1, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Koto_%28musical_instrument%29 + nameTooltip=_('Koto')) +_addInstrument('banjo', INST_TIED, MID, 'strings', + .8928046875, 1.6325390625, .0525, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Banjo + nameTooltip=_('Banjo')) +_addInstrument('ukulele', INST_TIED, MID, 'strings', + .64097090625, 1.0887984375, .17375, 0.35, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Ukulele + nameTooltip=_('Ukulele')) +_addInstrument('harpsichord', INST_TIED, MID, 'keyboard', + .57529609375, .936075, .2, 0.35, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Harpsichord + nameTooltip=_('Harpsichord')) +_addInstrument('clarinette', INST_TIED, MID, 'winds', + 1.635276375, 2.72956523438, .2, 0.3, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Clarinet + nameTooltip=_('Clarinet')) +_addInstrument('flute', INST_TIED, MID, 'winds', + .47169, .53693, .02481, 1.3, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Flute + nameTooltip=_('Flute')) +_addInstrument("drum1hatpedal", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1hatshoulder", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1hardride", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1ridebell", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1snare", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1snaresidestick", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1crash", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1splash", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1tom", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1floortom", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1chine", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum1kick", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument('piano', INST_TIED, MID, 'keyboard', + 0.8883, 1.420524, .13575, 1, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Piano + nameTooltip=_('Piano')) +_addInstrument('dog', INST_SIMP, MID, 'animals', + 0, 0, 0, 1, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Dog + nameTooltip=_('Dog')) +_addInstrument('chiken', INST_TIED, MID, 'animals', + .1972125, .8689675781, .02, 0.5, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Chicken + nameTooltip=_('Chicken')) +_addInstrument('duck', INST_SIMP, MID, 'animals', + 0, 0, 0, 0.7, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Duck + nameTooltip=_('Duck')) +_addInstrument('armbone', INST_SIMP, MID, 'people', + 0, 0, 0, 0.8, + # TRANS: The sound made by a person doing this + # TRANS: http://en.wikipedia.org/wiki/Blowing_a_raspberry + nameTooltip=_('Raspberry')) +_addInstrument("drum2darbukadoom", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2darbukapied", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2darbukapiedsoft", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2hatflanger", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2darbukatak", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2darbukafinger", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2darbukaroll", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2darbukaslap", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2hatpied", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2tambourinepied", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2hatpied2", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum2tambourinepiedsoft", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3cowbell", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3cowbelltip", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3cup", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3djembelow", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3djembemid", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3djembesidestick", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3djembeslap", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3djembestickmid", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3metalstand", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3pedalperc", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3rainstick", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3tambourinehigh", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum3tambourinelow", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument('harmonica', INST_TIED, MID, 'winds', + .1531, .19188, .01792, 1, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Harmonica + nameTooltip=_('Harmonica')) +_addInstrument('alarm', INST_TIED, MID, 'concret', + 1.37555859375, 2.0286015625, .0675, 0.4, + # TRANS: The sound of an alarm bell + nameTooltip=_('Alarm')) +_addInstrument('bird', INST_TIED, MID, 'animals', + .1, 1, .05, 1, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Bird + nameTooltip=_('Bird')) +_addInstrument('frogs', INST_TIED, MID, 'animals', + 1.954453125, 4.350234375, .2, 0.5, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Frog + nameTooltip=_('Frog')) +_addInstrument('cat', INST_SIMP, MID, 'animals', + 0, 0, 0, 0.8, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Cat + nameTooltip=_('Cat')) +_addInstrument('cow', INST_SIMP, MID, 'animals', + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Cow + 0, 0, 0, 0.7, nameTooltip=_('Cow')) +_addInstrument('cricket', INST_SIMP, MID, 'animals', + 0, 0, 0, 0.5, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Cricket_%28insect%29 + nameTooltip=_('Cricket')) +_addInstrument('duck2', INST_SIMP, MID, 'animals', + 0, 0, 0, 1, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Duck + nameTooltip=_('Duck')) +_addInstrument('bottle', INST_TIED, MID, 'concret', + .20532, .41064, .05292, 0.8, + # TRANS: The sound of a bottle being hit + # TRANS: http://en.wikipedia.org/wiki/Bottle + nameTooltip=_('Bottle')) +_addInstrument('clang', INST_SIMP, MID, 'concret', + 0, 0, 0, 0.8, + # TRANS: The sound of a garbage can + nameTooltip=_('Dump')) +_addInstrument('clang2', INST_SIMP, MID, 'percussions', + 0, 0, 0, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Cymbal + nameTooltip=_('Cymbal')) +_addInstrument('ow', INST_SIMP, MID, 'people', + 0, 0, 0, 1, + # TRANS: The sound of a person being hit + nameTooltip=_('Ow')) +_addInstrument('hey', INST_SIMP, MID, 'people', + 0, 0, 0, 0.5, + # TRANS: A surprised shout + nameTooltip=_('Hey')) +_addInstrument('sheep', INST_SIMP, MID, 'animals', + 0, 0, 0, 1, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Sheep + nameTooltip=_('Sheep')) +_addInstrument('water', INST_SIMP, MID, 'concret', + 0, 0, 0, 1, + # TRANS: The sound of water splashing + # TRANS: http://en.wikipedia.org/wiki/Water + nameTooltip=_('Water')) +_addInstrument('zap', INST_TIED, MID, 'concret', + .299, .7323, .09895, 0.8, + # TRANS: A science-fiction ray gun sound + nameTooltip=_('Ray Gun')) +_addInstrument('trumpet', INST_TIED, MID, 'winds', + .91195, 1.652909375, .05375, 0.3, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Trumpet + nameTooltip=_('Trumpet')) +_addInstrument('clavinet', INST_TIED, MID, 'keyboard', + .6398328125, .9401625, .094, 0.4, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Clavinet + nameTooltip=_('Clavinet')) +_addInstrument('flugel', INST_TIED, MID, 'winds', + 1.291740625, 2.37588007813, .065, 0.3, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Flugelhorn + nameTooltip=_('Flugelhorn')) +_addInstrument('foghorn', INST_TIED, LOW, 'winds', + 2.07005, 3.758775, .2, 0.5, + # TRANS: The sound of a fog horn or ship horn + nameTooltip=_('Foghorn')) +_addInstrument('bubbles', INST_TIED, MID, 'concret', + 0.02, 1.177, 0.02, 0.7, + # TRANS: The sound of air bubbling through water + nameTooltip=_('Bubbles')) +_addInstrument('marimba', INST_TIED, MID, 'percussions', + .18883789, .343623047, .07625, 0.4, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Marimba + nameTooltip=_('Marimba')) +_addInstrument('triangle', INST_TIED, MID, 'percussions', + 2.27261836, 3.2965453, .2, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Triangle_%28instrument%29 + nameTooltip=_('Triangle')) +_addInstrument('fingercymbals', INST_TIED, HIGH, 'percussions', + 1.29635195312, 1.92448125, .094, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Finger_cymbals + nameTooltip=_('Fingercymbals')) +_addInstrument('laugh', INST_SIMP, MID, 'people', + 0, 0, 0, 1, + # TRANS: The sound of a laugh + nameTooltip=_('Laugh')) +_addInstrument('babylaugh', INST_TIED, MID, 'people', + 0.72920078, 1.63253906, 0.01, 0.3, + # TRANS: The sound of a baby laughing + nameTooltip=_('Baby Laugh')) +_addInstrument('babyuhoh', INST_SIMP, MID, 'people', + 0, 0, 0, 0.3, + # TRANS: The sound of a child making a mistake + nameTooltip=_('Baby Uh-oh')) +_addInstrument('voix', INST_TIED, MID, 'people', + .89608, .96092, .02343, 0.8, + # TRANS: A synthesized voice sound + nameTooltip=_('Voice')) +_addInstrument('cling', INST_TIED, MID, 'concret', + .09096, .7878, .18026, 0.7, + # TRANS: A tinkling sound + nameTooltip=_('Cling')) +_addInstrument('byke', INST_SIMP, MID, 'concret', + 0, 0, 0, 1, + # TRANS: The sound of a bicycle bell + # TRANS: http://en.wikipedia.org/wiki/Bicycle + nameTooltip=_('Bicycle bell')) +_addInstrument('door', INST_SIMP, MID, 'concret', + 0, 0, 0, 1, + # TRANS: The sound of a door slamming shut + nameTooltip=_('Door')) +_addInstrument('basse', INST_TIED, MID, 'strings', + 0.50470875, 0.833315, 0.09375, 1.4, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Electric_bass + nameTooltip=_('Electric Bass')) +_addInstrument('acguit', INST_TIED, MID, 'strings', + 0.5123225, 0.7491675, 0.08475, 0.5, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Acoustic_guitar + nameTooltip=_('Acoustic Guitar')) +_addInstrument('diceinst', INST_SIMP, MID, 'concret', + 0, 0, 0, 1.3, + # TRANS: The sound of tumbling + # TRANS: http://en.wikipedia.org/wiki/Dice + nameTooltip=_('Dice')) +_addInstrument('didjeridu', INST_TIED, LOW, 'winds', + .55669, 1.73704, .09178, 1.5, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Didjeridu + nameTooltip=_('Didjeridu')) +_addInstrument('harmonium', INST_TIED, MID, 'keyboard', + .242032, .898165625, .2, 0.6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Harmonium + nameTooltip=_('Harmonium')) +_addInstrument('horse', INST_SIMP, MID, 'animals', + 0, 0, 0, 0.6, + # TRANS: The sound made by this animal + # TRANS: http://en.wikipedia.org/wiki/Horse + nameTooltip=_('Horse')) +_addInstrument('kalimba', INST_TIED, MID, 'percussions', + .20751, .30161, .04658, 1.3, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Thumb_piano + nameTooltip=_('Kalimba')) +_addInstrument('mando', INST_TIED, MID, 'strings', + 0.507107031, 0.934144531, 0.2, 0.5, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Mandolin + nameTooltip=_('Mandolin')) +_addInstrument('ocarina', INST_TIED, MID, 'winds', + .06612, .19033, .01776, 0.8, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Ocarina + nameTooltip=_('Ocarina')) +_addInstrument('rhodes', INST_TIED, MID, 'keyboard', + 0.58100625, 0.821625, 0.067, 0.7, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Rhodes_piano + nameTooltip=_('Rhodes')) +_addInstrument('saxo', INST_TIED, MID, 'winds', + .53722, .6583, .05264, 0.25, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Saxophone + nameTooltip=_('Saxophone')) +_addInstrument('saxsoprano', INST_TIED, HIGH, 'winds', + .90721015625, 1.71199335938, .07675, 0.25, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Soprano_saxophone + nameTooltip=_('Soprano Saxophone')) +_addInstrument('shenai', INST_TIED, MID, 'winds', + .29003, .33072, .00634, 0.5, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Shehnai + nameTooltip=_('Shehnai')) +_addInstrument('sitar', INST_TIED, MID, 'strings', + 1.1361625, 1.575134375, .183, 0.3, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Sitar + nameTooltip=_('Sitar')) +_addInstrument('tuba', INST_TIED, LOW, 'winds', + .51063, .58384, .035, 1.2, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Tuba + nameTooltip=_('Tuba')) +_addInstrument('violin', INST_TIED, MID, 'strings', + .105, .30656, .028, 1, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Violin + nameTooltip=_('Violin')) +_addInstrument("guidice1", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice2", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice3", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice4", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice5", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice6", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice7", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice8", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice9", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("guidice10", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4afrofeet", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4fingersn", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4mutecuic", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4stompbass", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tambouri", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr707clap", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr707open", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr808closed", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr808sn", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr909bass", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr909kick", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum4tr909sn", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5timablesslap", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5congagraveouvert", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5timablesaiguslap", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5congagraveferme", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5guiroretour", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5vibraslap", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5congaaiguouvert", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5quicamedium", INST_SIMP, PUNCH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5quicaaigu", INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5agogograve", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5bongoaiguouvert", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5agogoaigu", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum5bongograveouvert", INST_SIMP, HIGH, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument('camera', INST_SIMP, MID, 'concret', + 0, 0, 0, 1, + # TRANS: The sound of a camera shutter + # TRANS: http://en.wikipedia.org/wiki/Camera + nameTooltip=_('Camera')) +_addInstrument('car', INST_TIED, MID, 'concret', + .67, 1.05761, .01, 0.6, + # TRANS: The sound of a car starting + nameTooltip=_('Car')) +_addInstrument('carhorn', INST_SIMP, MID, 'concret', + 0, 0, 0, 0.4, + # TRANS: The sound of a car horn honking + nameTooltip=_('Car Horn')) +_addInstrument('cello', INST_TIED, MID, 'strings', + 0.4761, 0.92244375, 0.19125, .6, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Cello + nameTooltip=_('Cello')) +_addInstrument('chimes', INST_TIED, MID, 'percussions', + 4.104825, 5.644134375, .02, 1, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Chimes + nameTooltip=_('Chimes')) +_addInstrument("crash", INST_SIMP, MID, 'concret', + 0, 0, 0, 1, + # TRANS: The sound of glass breaking + nameTooltip=_("Crash")) +_addInstrument("guit2", INST_TIED, MID, 'strings', + 1.186341406, 1.929568266, .2, 0.25, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Electric_guitar + nameTooltip=_("Electric Guitar")) +_addInstrument('plane', INST_SIMP, MID, 'concret', + 0, 0, 0, 0.6, + # TRANS: The sound of this vehicle + # TRANS: http://en.wikipedia.org/wiki/Airplane + nameTooltip=_('Plane')) +_addInstrument('slap', INST_SIMP, MID, 'concret', + 0, 0, 0, 0.7, + # TRANS: The sound of something being hit + nameTooltip=_('Slap')) +_addInstrument('templebell', INST_SIMP, MID, 'percussions', + 0, 0, 0, 1, + # TRANS: The sound made by a bell + nameTooltip=_('Temple Bell')) +_addInstrument('sarangi', INST_SIMP, MID, 'strings', + 0, 0, 0, 1, + # TRANS: The sound made by this instrument + # TRANS: http://en.wikipedia.org/wiki/Sarangi + nameTooltip=_('Sarangi')) +_addInstrument("drum6madal00", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal01", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal02", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal03", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal04", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal05", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal06", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal07", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal08", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal09", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal10", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal11", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument("drum6madal12", INST_SIMP, LOW, 'percussions', + 0, 0, 0, 1, kitStage=True) +_addInstrument('au_pipes', INST_TIED, MID, 'winds', + 0, 1, 0, 0.9, + # TRANS: The sound of a Solomon Islands pipe, named "'au tahana" + # TRANS: http://www.jstor.org/pss/851365 + nameTooltip=_('\'au tahana')) + +if Config.FEATURES_NEWSOUNDS: + try: + files = os.listdir(Config.SNDS_INFO_DIR) + for file in files: + instrumentDB.addInstrument(Config.SNDS_INFO_DIR + '/' + file) + except: + pass + +#jamId = os.path.split(os.path.realpath("/home/olpc/isolation/1/bundle_id_to_gid/org.laptop.TamTamJam"))[1] + + +DRUM1KIT = {24: "drum1kick", + 26: "drum1floortom", + 28: "drum1tom", + 30: "drum1chine", + 32: "drum1splash", + 34: "drum1crash", + 36: "drum1snaresidestick", + 38: "drum1snaresidestick", + 40: "drum1snare", + 42: "drum1ridebell", + 44: "drum1hardride", + 46: "drum1hatshoulder", + 48: "drum1hatpedal"} + +DRUM2KIT = {24: "drum2darbukadoom", + 26: "drum2darbukapied", + 28: "drum2darbukapiedsoft", + 30: "drum2hatflanger", + 32: "drum2darbukatak", + 34: "drum2darbukatak", + 36: "drum2darbukafinger", + 38: "drum2darbukaroll", + 40: "drum2darbukaslap", + 42: "drum2hatpied", + 44: "drum2tambourinepied", + 46: "drum2hatpied2", + 48: "drum2tambourinepiedsoft"} + +DRUM3KIT = {24: "drum3djembelow", + 26: "drum3pedalperc", + 28: "drum3djembeslap", + 30: "drum3tambourinehigh", + 32: "drum3tambourinelow", + 34: "drum3rainstick", + 36: "drum3djembemid", + 38: "drum3djembesidestick", + 40: "drum3djembestickmid", + 42: "drum3cowbell", + 44: "drum3cowbelltip", + 46: "drum3cup", + 48: "drum3metalstand"} + +DRUM4KIT = {24: "drum4afrofeet", + 26: "drum4tr909kick", + 28: "drum4tr909bass", + 30: "drum4stompbass", + 32: "drum4tr707open", + 34: "drum4mutecuic", + 36: "drum4tr808sn", + 38: "drum4tr707clap", + 40: "drum4tr909sn", + 42: "drum4tambouri", + 44: "drum4fingersn", + 46: "drum4fingersn", + 48: "drum4tr808closed"} + +DRUM5KIT = {24: "drum5timablesslap", + 26: "drum5timablesaiguslap", + 28: "drum5congagraveouvert", + 30: "drum5quicamedium", + 32: "drum5guiroretour", + 34: "drum5vibraslap", + 36: "drum5congagraveferme", + 38: "drum5quicaaigu", + 40: "drum5congaaiguouvert", + 42: "drum5agogoaigu", + 44: "drum5bongograveouvert", + 46: "drum5agogograve", + 48: "drum5bongoaiguouvert"} + +DRUM6KIT = {24: "drum6madal00", + 26: "drum6madal01", + 28: "drum6madal02", + 30: "drum6madal03", + 32: "drum6madal04", + 34: "drum6madal05", + 36: "drum6madal06", + 38: "drum6madal07", + 40: "drum6madal08", + 42: "drum6madal09", + 44: "drum6madal10", + 46: "drum6madal11", + 48: "drum6madal12"} + +_addInstrument("drum1kit", 0, 0, "percussions", 0, 0, 0, 1, DRUM1KIT, + # TRANS: http://en.wikipedia.org/wiki/Drum_set + nameTooltip=_('Jazz / Rock Kit')) +_addInstrument("drum2kit", 0, 0, "percussions", 0, 0, 0, 1, DRUM2KIT, + nameTooltip=_('African Kit')) +_addInstrument("drum3kit", 0, 0, "percussions", 0, 0, 0, 1, DRUM3KIT, + nameTooltip=_('Arabic Kit')) +_addInstrument("drum4kit", 0, 0, "percussions", 0, 0, 0, 1, DRUM4KIT, + nameTooltip=_('Electronic Kit')) +_addInstrument("drum5kit", 0, 0, "percussions", 0, 0, 0, 1, DRUM5KIT, + nameTooltip=_('South American Kit')) +_addInstrument("drum6kit", 0, 0, "percussions", 0, 0, 0, 1, DRUM6KIT, + nameTooltip=_('Nepali')) + +DRUMCOUNT = 6 diff --git a/common/Util/NoteDB.py b/common/Util/NoteDB.py new file mode 100644 index 0000000..646f8d8 --- /dev/null +++ b/common/Util/NoteDB.py @@ -0,0 +1,813 @@ +import common.Util.InstrumentDB as InstrumentDB +import common.Config as Config + +class PARAMETER: + PAGE_BEATS, \ + PAGE_COLOR, \ + ONSET, \ + PITCH, \ + AMPLITUDE, \ + DURATION, \ + INSTRUMENT, \ + PAN, \ + REVERB, \ + ATTACK, \ + DECAY, \ + FILTERTYPE, \ + FILTERCUTOFF, \ + INSTRUMENT2 \ + = range(14) #python-stye enum + +class Note: + def __init__( self, page, track, id, cs ): + self.page = page + self.track = track + self.id = id + self.cs = cs + + self.csStack = [] + + def pushState( self ): + self.csStack.append( self.cs.clone() ) + + def popState( self ): + self.cs = self.csStack.pop() + +class Page: + def __init__( self, beats, color = 0, instruments = False, local = True ): # , tempo, insruments, color = 0 ): + self.instrumentDB = InstrumentDB.getRef() + self.beats = beats + self.ticks = beats*Config.TICKS_PER_BEAT + + self.color = color + + if not instruments: + self.instruments = [ self.instrumentDB.instNamed["kalimba"].instrumentId for i in range(Config.NUMBER_OF_TRACKS-1) ] + [ self.instrumentDB.instNamed["drum1kit"].instrumentId ] + else: + self.instruments = instruments[:] + + self.local = local # page local/global? + + self.nextNoteId = 0 # first note will be 1 + + def genId( self ): + self.nextNoteId += 1 + if self.nextNoteId == 65536: # short + print "note id overflow!" + # TODO think of how to handle this!? + return self.nextNoteId + + def setLocal( self, local ): + self.local = local + +class PageListener: + def notifyPageAdd( self, id, at ): + pass + + def notifyPageDelete( self, which, safe ): + pass + + def notifyPageDuplicate( self, new, at ): + pass + + def notifyPageMove( self, which, low, high ): + pass + + def notifyPageUpdate( self, which, parameter, value ): + pass + +class NoteListener: + def notifyNoteAdd( self, page, track, id ): + pass + def notifyNoteDelete( self, page, track, id ): + pass + def notifyNoteUpdate( self, page, track, id, parameter, value ): + pass + +class NoteDB: + def __init__( self ): + self.instrumentDB = InstrumentDB.getRef() + self.noteD = {} # bins containing all the notes by page, track, and id + # structure self.noteD[pageId][trackIndex][noteId] + + self.noteS = {} # bins containing all the notes by page and track + # first sorted by onset then by pitch (for drum hits) + # structure self.noteS[pageId][trackIndex][noteIndex] + + self.pages = {} # dict of Pages indexed by pageId + + self.tune = [] # list of pageIds ordered by tune + + #self.beatsBefore = {} # count of beats on previous pages indexed by page id + + self.listeners = [] # complete list of listeners + self.pageListeners = [] # list of listeners who want page notifications + self.noteListeners = [] # list of listeners who want note notifications + self.parasiteList = {} # dict of parasites indexed by listener + + self.parasiteD = {} # bin of parasites indexed by listener + self.parasiteS = {} # parasites sorted as in self.noteS, + + self.nextId = 0 # base id, first page will be 1 + + self.clipboard = [] # stores copied cs notes + self.clipboardArea = [] # stores the limits and tracks for each page in the clipboard + + def dumpToStream( self, ostream, localOnly = False ): + for pid in self.tune: + if not localOnly or self.pages[pid].local: + ostream.page_add(pid, self.pages[pid]) + for note in self.getNotesByPage( pid ): + ostream.note_add( note ) + + #-- private -------------------------------------------- + def _genId( self ): + self.nextId += 1 + if self.nextId == 65536: # short + print "page id overflow!" + # TODO think of how to handle this!? + return self.nextId + + #======================================================= + # Page Functions + + def addPage( self, pid, page, after = False ): + pid = self._newPage( pid, page ) + at = self._insertPage( pid, after ) + + #self._updateBeatsBefore( at ) + + for l in self.pageListeners: + l.notifyPageAdd( pid, at ) + + return pid + + def deletePages( self, which, instruments = False ): + beats = self.pages[self.tune[0]].beats + + low = 999999 + ind = -1 + for id in which: + ind = self.tune.index(id) + if ind < low: low = ind + + for t in range(Config.NUMBER_OF_TRACKS): + for n in self.noteD[id][t].keys(): + self.deleteNote( id, t, n ) + + #del self.beatsBefore[id] + + del self.noteD[id] + del self.noteS[id] + del self.parasiteD[id] + del self.parasiteS[id] + del self.pages[id] + + at = self.tune.index( id ) + self.tune.pop(at) + + if not len(self.tune): + self.addPage( -1, Page(beats, instruments = instruments) ) # always have at least one page + safe = self.tune[0] + #self._updateBeatsBefore(0) + else: + safe = self.tune[max(ind-1,0)] + #self._updateBeatsBefore(low) + + for l in self.pageListeners: + l.notifyPageDelete( which, safe ) + + def duplicatePages( self, which, after = False ): + sorted = [] + if after: first = self.tune.index(after)+1 + else: first = 0 + + i = j = 0 + while i < len(self.tune) and j < len(which): + if self.tune[i] in which: + sorted.append(self.tune[i]) + j += 1 + i += 1 + + new = {} + for cp in sorted: + id = self._newPage( -1, Page(self.pages[cp].beats,self.pages[cp].color,self.pages[cp].instruments) ) + self._insertPage( id, after ) + after = id + new[cp] = id + + #self._updateBeatsBefore( first ) + + for l in self.pageListeners: + l.notifyPageDuplicate( new, first ) + + for cp in sorted: + for t in range(Config.NUMBER_OF_TRACKS): + for n in self.noteD[cp][t].keys(): + self.duplicateNote( cp, t, n, new[cp], t, 0 ) + + return new + + def movePages( self, which, after = False ): + sorted = [] + if after: at = self.tune.index(after)+1 + else: at = 0 + low = high = at + + i = j = 0 + while i < len(self.tune): + if self.tune[i] in which: + sorted.append(self.tune[i]) + self.tune.pop(i) + if i < low: low = i + if i > high: high = i + if i < at: at -= 1 + j += 1 + else: + i += 1 + + self.tune = self.tune[:at] + sorted + self.tune[at:] + + #self._updateBeatsBefore( low ) + + for l in self.pageListeners: + l.notifyPageMove( sorted, low, high ) + + def updatePage( self, page, parameter, value ): + if parameter == PARAMETER.PAGE_BEATS: + ticks = value*Config.TICKS_PER_BEAT + if self.pages[page].beats > value: # crop some notes + dstream = [] + ustream = [] + for track in range(Config.NUMBER_OF_TRACKS): + dsub = [] + usub = [] + for note in self.getNotesByTrack(page, track): + if ticks <= note.cs.onset: + dsub.append( note.id ) + elif ticks < note.cs.onset + note.cs.duration: + usub.append( note.id ) + usub.append( ticks - note.cs.onset ) + if len(dsub): + dstream += [ page, track, len(dsub) ] + dsub + if len(usub): + ustream += [ page, track, PARAMETER.DURATION, len(usub)//2 ] + usub + if len(dstream): + self.deleteNotes( dstream + [-1] ) + if len(ustream): + self.updateNotes( ustream + [-1] ) + + self.pages[page].beats = value + self.pages[page].ticks = ticks + #self._updateBeatsBefore(self.tune.index(page)) + elif parameter == PARAMETER.PAGE_COLOR: + self.pages[page].color = value + + for l in self.pageListeners: + l.notifyPageUpdate( page, parameter, value ) + + # stream format: + # parameter id + # number of following pages (N) + # page id + # value + def updatePages( self, stream ): + i = [-1] + parameter = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + page = self._readstream(stream,i) + val = self._readstream(stream,i) + self.updatePage( page, parameter, val ) + + def getInstruments(self, pages): + dict = {} + for page in pages: + list = [] + for track in range(Config.NUMBER_OF_TRACKS): + list.append(self.instrumentDB.instId[self.pages[page].instruments[track]].name) + dict[page] = list[:] + return dict + + #-- private -------------------------------------------- + def _newPage( self, pid, page ): + if pid == -1 : pid = self._genId() + self.pages[pid] = page + self.noteD[pid] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ] + self.noteS[pid] = [ [] for i in range(Config.NUMBER_OF_TRACKS) ] + self.parasiteD[pid] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ] + self.parasiteS[pid] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ] + for i in range(Config.NUMBER_OF_TRACKS): + for par in self.parasiteList.keys(): + self.parasiteD[pid][i][par] = {} + self.parasiteS[pid][i][par] = [] + return pid + + def _insertPage( self, pid, after ): + if not after: at = 0 + else: at = self.tune.index(after)+1 + self.tune.insert( at, pid ) + + return at + + #def _updateBeatsBefore( self, ind ): + # if ind == 0: beats = 0 + # else: beats = self.beatsBefore[self.tune[ind-1]] + self.pages[self.tune[ind-1]].beats + # for i in range(ind, len(self.tune)): + # self.beatsBefore[self.tune[ind]] = beats + # beats += self.pages[self.tune[ind]].beats + + + + #======================================================= + # Track Functions + + def setInstrument( self, pages, track, instrumentId ): + stream = [] + for page in pages: + self.pages[page].instruments[track] = instrumentId + notes = self.getNotesByTrack( page, track ) + sub = [] + for note in notes: + sub.append( note.id ) + sub.append( instrumentId ) + if len(sub): + stream += [ page, track, PARAMETER.INSTRUMENT, len(sub)//2 ] + sub + if len(stream): + self.updateNotes( stream + [-1] ) + + def setInstrument2( self, pages, track, instrumentId ): + stream = [] + for page in pages: + #self.pages[page].instruments[track] = instrumentId + notes = self.getNotesByTrack( page, track ) + sub = [] + for note in notes: + sub.append( note.id ) + sub.append( instrumentId ) + if len(sub): + stream += [ page, track, PARAMETER.INSTRUMENT2, len(sub)//2 ] + sub + if len(stream): + self.updateNotes( stream + [-1] ) + + #======================================================= + # Note Functions + + def addNote( self, nid, page, track, cs, hint = False ): + if nid == -1: nid = self.pages[page].genId() + n = self.noteD[page][track][nid] = Note( page, track, nid, cs ) + + if not hint: at = 0 + else: at = hint[0] + while at > 0: + onset = self.noteS[page][track][at-1].cs.onset + if onset <= cs.onset: + if onset <= cs.onset: break + elif self.noteS[page][track][at-1].cs.pitch <= cs.pitch: break + at -= 1 + last = len(self.noteS[page][track]) + while at < last: + onset = self.noteS[page][track][at].cs.onset + if onset >= cs.onset: + if onset > cs.onset: break + elif self.noteS[page][track][at].cs.pitch > cs.pitch: break + at += 1 + + self.noteS[page][track].insert( at, n ) + + for par in self.parasiteList.keys(): + parasite = self.parasiteList[par]( self, par, n ) + self.parasiteD[page][track][par][nid] = parasite.attach() # give parasites the option of return something other than themselves + self.parasiteS[page][track][par].insert( at, parasite.attach() ) + + if hint: hint[0] = at + 1 # assume the next note will fall after this one + + for l in self.noteListeners: + l.notifyNoteAdd( page, track, nid ) + + return nid + + # stream format: + # page id + # track index + # number of following notes (N) + # cs pointer + # ... up to N + # page id or -1 to exit + def addNotes( self, stream ): + new = {} + i = [-1] + p = self._readstream(stream,i) + while p != -1: + if p not in new: + new[p] = [ [] for x in range(Config.NUMBER_OF_TRACKS) ] + t = self._readstream(stream,i) + N = self._readstream(stream,i) + hint = [0] + for j in range(N): + new[p][t].append( self.addNote( -1, p, t, self._readstream(stream,i), hint ) ) + p = self._readstream(stream,i) + + return new + + def deleteNote( self, page, track, id ): + ind = self.noteS[page][track].index( self.noteD[page][track][id] ) + + for par in self.parasiteList.keys(): + self.parasiteD[page][track][par][id].destroy() + self.parasiteS[page][track][par].pop(ind) + del self.parasiteD[page][track][par][id] + + self.noteS[page][track].pop(ind) + del self.noteD[page][track][id] + + for l in self.noteListeners: + l.notifyNoteDelete( page, track, id ) + + # stream format: + # page id + # track index + # number of following notes (N) + # note id + # ... up to N + # page id or -1 to exit + def deleteNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + self.deleteNote( p, t, self._readstream(stream,i) ) + p = self._readstream(stream,i) + + def deleteNotesByTrack( self, pages, tracks ): + for p in pages: + for t in tracks: + notes = self.noteS[p][t][:] + for n in notes: + self.deleteNote( p, t, n.id ) + + def duplicateNote( self, page, track, id, toPage, toTrack, offset ): + cs = self.noteD[page][track][id].cs.clone() + cs.trackId = toTrack + cs.pageId = toPage + cs.onset += offset + ticks = self.pages[toPage].ticks + if cs.onset >= ticks: return False # off the end of the page + if cs.onset + cs.duration > ticks: + cs.duration = ticks - cs.onset + + return self.addNote( -1, toPage, toTrack, cs ) + + # stream format: + # page id + # track index + # toPage id + # toTrack index + # offset + # number of following notes (N) + # note id + # ... up to N + # page id or -1 to exit + def duplicateNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + toP = self._readstream(stream,i) + toT = self._readstream(stream,i) + offset = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + self.duplicateNote( p, t, self._readstream(stream,i), toP, toT, offset ) + p = self._readstream(stream,i) + + + def updateNote( self, page, track, id, parameter, value ): + if parameter == PARAMETER.ONSET: + self.noteD[page][track][id].cs.onset = value + self._resortNote( page, track, id ) + elif parameter == PARAMETER.PITCH: + self.noteD[page][track][id].cs.pitch= value + self._resortNote( page, track, id ) + elif parameter == PARAMETER.AMPLITUDE: + self.noteD[page][track][id].cs.amplitude = value + elif parameter == PARAMETER.DURATION: + self.noteD[page][track][id].cs.duration = value + elif parameter == PARAMETER.INSTRUMENT: + self.noteD[page][track][id].cs.instrumentId = value + elif parameter == PARAMETER.PAN: + self.noteD[page][track][id].cs.pan = value + elif parameter == PARAMETER.REVERB: + self.noteD[page][track][id].cs.reverbSend = value + elif parameter == PARAMETER.ATTACK: + self.noteD[page][track][id].cs.attack = value + elif parameter == PARAMETER.DECAY: + self.noteD[page][track][id].cs.decay = value + elif parameter == PARAMETER.FILTERTYPE: + self.noteD[page][track][id].cs.filterType = value + elif parameter == PARAMETER.FILTERCUTOFF: + self.noteD[page][track][id].cs.filterCutoff = value + elif parameter == PARAMETER.INSTRUMENT2: + self.noteD[page][track][id].cs.instrumentId2 = value + + for par in self.parasiteList.keys(): + self.parasiteD[page][track][par][id].updateParameter( parameter, value ) + + for l in self.noteListeners: + l.notifyNoteUpdate( page, track, id, parameter, value ) + + # stream format: + # page id + # track index + # parameter id + # number of following notes (N) + # note id + # value + # ... up to N + # page id or -1 to exit + def updateNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + param = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + self.updateNote( p, t, self._readstream(stream,i), param, self._readstream(stream,i) ) + p = self._readstream(stream,i) + + #-- private -------------------------------------------- + def _readstream( self, stream, i ): + i[0] += 1 + return stream[i[0]] + + def _resortNote( self, page, track, id ): + ins = out = self.noteS[page][track].index(self.noteD[page][track][id]) + cs = self.noteD[page][track][id].cs + while ins > 0: # check backward + onset = self.noteS[page][track][ins-1].cs.onset + if onset <= cs.onset: + if onset <= cs.onset: break + elif self.noteS[page][track][ins-1].cs.pitch <= cs.pitch: break + ins -= 1 + if ins == out: # check forward + ins += 1 + last = len(self.noteS[page][track]) + while ins < last: + onset = self.noteS[page][track][ins].cs.onset + if onset >= cs.onset: + if onset > cs.onset: break + elif self.noteS[page][track][ins].cs.pitch > cs.pitch: break + ins += 1 + + if ins != out: # resort + if ins > out: ins -= 1 + n = self.noteS[page][track].pop( out ) + self.noteS[page][track].insert( ins, n ) + for par in self.parasiteList.keys(): + p = self.parasiteS[page][track][par].pop( out ) + self.parasiteS[page][track][par].insert( ins, p ) + + + #======================================================= + # Clipboard Functions + + # stream format: + # page id + # track index + # number of following notes (N) + # note id + # ... up to N + # page id or -1 to exit + def notesToClipboard( self, stream ): + self.clipboard = [] + self.clipboardArea = [] + i = [-1] + pages = [] + p = self._readstream(stream,i) + while p != -1: + if p not in pages: + page = [ [] for x in range(Config.NUMBER_OF_TRACKS) ] + pageArea = { "limit": [ 99999, 0 ], + "tracks": [ 0 for x in range(Config.NUMBER_OF_TRACKS) ] } + pages.append(p) + self.clipboard.append(page) + self.clipboardArea.append(pageArea) + else: + ind = pages.index(p) + page = self.clipboard[ind] + pageArea = self.clipboardArea[ind] + t = self._readstream(stream,i) + pageArea["tracks"][t] = 1 + N = self._readstream(stream,i) + for j in range(N): + cs = self.noteD[p][t][self._readstream(stream,i)].cs.clone() + if cs.onset < pageArea["limit"][0]: pageArea["limit"][0] = cs.onset + if cs.onset + cs.duration > pageArea["limit"][1]: pageArea["limit"][1] = cs.onset + cs.duration + page[t].append( cs ) + p = self._readstream(stream,i) + + return self.clipboardArea + + def tracksToClipboard( self, pages, tracks ): + self.clipboard = [] + self.clipboardOrigin = [ 0, 0 ] + self.clipboardArea = [] + for p in pages: + page = [ [] for x in range(Config.NUMBER_OF_TRACKS) ] + pageArea = { "limit": [ 0, 99999 ], + "tracks": [ 0 for x in range(Config.NUMBER_OF_TRACKS) ] } + self.clipboard.append(page) + self.clipboardArea.append(pageArea) + for t in tracks: + pageArea["tracks"][t] = 1 + for id in self.noteD[p][t]: + cs = self.noteD[p][t][id].cs.clone() + page[t].append( cs ) + + return self.clipboardArea + + # trackMap = { X: Y, W: Z, ... }; X,W are track indices, Y,Z are clipboard indices + # instrumentMap = { X: Y, W: Z, ... }; X,W are track indices, Y,Z are instrument ids + def pasteClipboard( self, pages, offset, trackMap, instrumentMap = {} ): + if not len(self.clipboard): return + + deleteStream = [] + updateStream = [] + addStream = [] + + pp = 0 + ppMax = len(self.clipboard) + for p in pages: + ticks = self.pages[p].ticks + area = self.clipboardArea[pp] + area["limit"][0] += offset + area["limit"][1] += offset + for t in trackMap.keys(): + if not area["tracks"][trackMap[t]]: continue + if instrumentMap.has_key(t): + updateInstrument = True + instrumentId = instrumentMap[t] + else: + updateInstrument = False + tdeleteStream = [] + tupdateOStream = [] + tupdateDStream = [] + taddStream = [] + # clear area + for n in self.noteS[p][t]: + start = n.cs.onset + end = start + n.cs.duration + if area["limit"][0] <= start < area["limit"][1]: start = area["limit"][1] + if area["limit"][0] < end <= area["limit"][1]: end = area["limit"][0] + if start < area["limit"][0] and end > area["limit"][1]: end = area["limit"][0] + if end <= start: + tdeleteStream.append( n.id ) + elif start != n.cs.onset: + tupdateDStream += [ n.id, end - start ] + tupdateOStream += [ n.id, start ] + elif end != start + n.cs.duration: + tupdateDStream += [ n.id, end - start ] + if len(tdeleteStream): + deleteStream += [ p, t, len(tdeleteStream) ] + tdeleteStream + if len(tupdateOStream): + updateStream += [ p, t, PARAMETER.ONSET, len(tupdateOStream)//2 ] + tupdateOStream + if len(tupdateDStream): + updateStream += [ p, t, PARAMETER.DURATION, len(tupdateDStream)//2 ] + tupdateDStream + # paste notes + for cs in self.clipboard[pp][trackMap[t]]: + newcs = cs.clone() + newcs.onset += offset + if newcs.onset >= ticks: continue + if newcs.onset + newcs.duration > ticks: + newcs.duration = ticks - newcs.onset + newcs.pageId = p + newcs.trackId = t + if updateInstrument: + newcs.instrumentId = instrumentId + # TODO update any other parameters? + taddStream.append( newcs ) + if len(taddStream): + addStream += [ p, t, len(taddStream) ] + taddStream + + pp += 1 + if pp == ppMax: pp -= ppMax + + + + if len(deleteStream): + self.deleteNotes( deleteStream + [-1] ) + if len(updateStream): + self.updateNotes( updateStream + [-1] ) + if len(addStream): + return self.addNotes( addStream + [-1] ) + + return None + + def getClipboardArea( self, ind ): + N = len(self.clipboardArea) + while ind >= N: ind -= N + return self.clipboardArea[ind] + + #======================================================= + # Listener Functions + + def addListener( self, listener, parasite = None, page = False, note = False ): + if listener in self.listeners: + return # only one listener per object + + if parasite: + self.parasiteList[listener] = parasite + self._addParasite( listener, parasite ) + + if page: self.pageListeners.append( listener ) + if note: self.noteListeners.append( listener ) + self.listeners.append( listener ) + + def deleteListener( self, listener ): + self.listeners.remove( listener ) + if listener in self.pageListeners: + self.pageListeners.remove( listener ) + if listener in self.noteListeners: + self.noteListeners.remove( listener ) + if self.parasites.has_key( listener ): + self._deleteParasite( listener ) + del self.parasiteList[listener] + + #-- private -------------------------------------------- + def _addParasite( self, listener, parasite ): + for p in self.tune: + for t in range(Config.NUMBER_OF_TRACKS): + self.parasiteD[p][t][listener] = {} + self.parasiteS[p][t][listener] = [] + for n in self.noteD[p][t].keys(): + parasite( self, listener, self.noteD[p][t][n] ) + self.parasiteD[p][t][listener][n] = parasite.attach() # give parasites the option of returning something other than themselves + self.parasiteS[p][t][listener].insert( self.noteS[p][t].index( self.noteD[p][t][n]), parasite.attach() ) + + def _deleteParasite( self, listener ): + for p in self.tune: + for t in range(Config.NUMBER_OF_TRACKS): + for n in self.notes[p][t].keys(): + self.parasiteD[p][t][listener][n].destroy() + del self.parasiteD[p][t][listener] + del self.parasiteS[p][t][listener] + + #======================================================= + # Get Functions + + def getPageCount( self ): + return len(self.pages) + + def getTune( self ): + return self.tune + + def getPage( self, page ): + return self.pages[page] + + def getPageByIndex( self, ind ): + return self.tune[ind] + + def getPageIndex( self, page ): + return self.tune.index(page) + + # Not sure if this is useful! + #def getBeatsBeforePage( self, page ): + # return self.beatsBefore[page] + + def getNote( self, page, track, id, listener = None ): + if listener: + return self.parasiteD[page][track][listener][id] + return self.noteD[page][track][id] + + def getNotesByPage( self, page, listener = None ): + notes = [] + if listener: + for i in range(Config.NUMBER_OF_TRACKS): + notes.extend( self.parasiteS[page][i][listener] ) + else: + for i in range(Config.NUMBER_OF_TRACKS): + notes.extend( self.noteS[page][i] ) + return notes + + + def getNotesByTrack( self, page, track, listener = None ): + if listener: + return self.parasiteS[page][track][listener] + else: + return self.noteS[page][track] + + def getNotes(self, listener = None ): + notes = [] + for p in self.pages: + notes.extend( self.getNotesByPage(p, listener ) ) + return notes + + + def getCSNotesByPage( self, page ): + return map( lambda n: n.cs, self.getNotesByPage( page ) ) + + def getCSNotesByTrack( self, page, track ): + return map( lambda n: n.cs, self.getNotesByTrack( page, track ) ) diff --git a/common/Util/__init__.py b/common/Util/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/common/Util/__init__.py diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/common/__init__.py -- cgit v0.9.1