def run(stdscr): onsets = [] parses = None mode = BEGIN while True: stdscr.clear() if mode == BEGIN: stdscr.addstr(3, 3, 'Tap space to begin and add an onset, hit b to begin and not add an onset, hit q to stop') elif mode == STARTED: stdscr.addstr(3, 3, 'Running. Tap space to add onsets, q to stop') elif mode == ENDED: stdscr.addstr(3, 3, 'Done. Onsets: {0}'.format(onsets)) stdscr.refresh() c = stdscr.getch() if c == ord('b'): if mode == BEGIN: mode = STARTED start = datetime.now() if c == ord(' '): if mode == BEGIN: mode = STARTED start = datetime.now() onsets.append(0) elif mode == STARTED: diff = datetime.now() - start onsets.append(diff.total_seconds()) if c == ord('r'): mode = BEGIN onsets = [] parses = None if c == ord('q'): if mode == BEGIN or mode == ENDED: break else: diff = datetime.now() - start onsets.append(diff.total_seconds()) mode = ENDED if c == ord('p') and mode == ENDED: if parses == None: cgui.alert(stdscr, 'Parsing', block=False) corpus = annotations.corpus('explicitswing') rhythmmodel = pcfg.train(corpus) expressionmodel = expression.additive_noise(0.1) parser = StochasticParser(corpus, expressionModel=expressionmodel, rhythmModel=rhythmmodel) parses = parser.parse_onsets(onsets) if len(parses) > 0: choice = cgui.menu(stdscr, '{0} parses'.format(len(parses)), ['View analysis', 'View score', 'View all']) if choice == -1: continue elif choice == 0: parse = parses[cgui.menu(stdscr, 'parse?', ['Prior: {0}, likelihood {1}, posterior {2}. Depth {3}'.format(p.prior, p.likelihood, p.posterior, p.depth) for p in parses])] parse.view() elif choice == 1: barlevel = cgui.prompt(stdscr, 'Barlevel?') parse = parses[cgui.menu(stdscr, 'parse?', ['Prior: {0}, likelihood {1}, posterior {2}. Depth {3}'.format(p.prior, p.likelihood, p.posterior, p.depth) for p in parses])] parse.score(barlevel=int(barlevel)) elif choice == 2: latex.view_symbols(parses, scale=False) else: cgui.alert(stdscr, 'No parses')
def save(self, collection=None, name=None, force=False): """Save the annotation to the corpus""" if not collection: choice = cgui.menu(self.stdscr, 'Collection', annotationcorpus.collections() + ['Add new collection']) if choice == -1: return elif choice == len(annotationcorpus.collections()): collection = cgui.prompt(self.stdscr, 'Collection?') else: collection = annotationcorpus.collections()[choice] if not name: name = self.name metadata = {'beatdiv':self.meter.beatdiv, 'beatspb':self.meter.beatspb, 'offset':self.offset, 'bpm':self.bpm, 'name':self.name} if annotationcorpus.exists(collection, name) and not force: if cgui.menu(self.stdscr, 'Item exists, overwrite?', ['No', 'Yes']) != 1: return annotationcorpus.save(collection, name, metadata, sorted(self.annotations, key=lambda x: x[0]), self.notelist, self.midifile)
def choose_book(index, results, stdscr=None): if stdscr: song = results[cgui.menu(stdscr, "Select a song", results)] else: song = results[commandline.menu("Select a song", results)] locations = zip(books, index[song]) bookhits = [] for book in books: if index[song][book]: bookhits.append(book) if stdscr: book = bookhits[cgui.menu(stdscr, "Select a book", bookhits)] else: book = bookhits[commandline.menu("Select a book", bookhits)] return (song, book)
def graphics(self, stdscr): self.stdscr = stdscr self.my, self.mx = self.stdscr.getmaxyx() self.height = 4 self.width = self.mx - 30 self.posy = int(self.my / 2.0 - self.height / 2.0) self.posx = int(self.mx / 2.0 - self.width / 2.0) self.midipad = curses.newpad(self.height, 1) self.annotationpad = curses.newpad(self.height, 1) self.com_buffer = curses.newwin(1, self.width, self.posy, self.posx) self.buf = '' self.stdscr.refresh() if annotationcorpus.exists('autosave', 'autosave'): if cgui.menu(self.stdscr, 'Annotator quit unexpectedly. Restore last session?', ['Yes', 'No']) == 0: self.load('autosave', 'autosave') while True: exp = re.compile(r'(?P<repetitions>[0-9]+)?(?P<action>[iqpsx ])$|:(?P<command>set |play|stop|pause|save|strip|subtract|q|load|score|restore)(?P<arg1>resolution|correction|beatsperbar|beatdiv)?(?P<arg2> (-)?[0-9]+)?\n$') if self.mode == self.INSERT: exp = re.compile(r'(?P<command>[ wsrge]|t(?P<arg>[0-9]+))$') # Check if the buffer contains a command m = exp.match(self.buf) if m: if not self.execute(m): break self.buf = '' self.updateScr(self.stdscr) c = self.stdscr.getch() if c == curses.ERR: continue self.status = '' if c == 27: # or c == curses.KEY_BACKSPACE: if self.mode == self.INSERT: self.mode = self.ANNOTATING self.status = 'Leaving insert mode' # Empty buffer self.buf = '' elif c == curses.KEY_BACKSPACE: # Empty buffer self.buf = '' elif c == curses.KEY_LEFT: self.curs_left() elif c == curses.KEY_RIGHT: self.curs_right() elif c == curses.KEY_UP and self.mode != self.INSERT: if self.mode == self.ANNOTATING: self.mode = self.PLAYING elif c == curses.KEY_DOWN and self.mode != self.INSERT: if self.mode == self.PLAYING: self.mode = self.ANNOTATING else: if c in range(32, 128) + [10]: self.buf += chr(c)
def load(self, collection=None, name=None): if not collection: collection = annotationcorpus.collections()[\ cgui.menu(self.stdscr, 'Choose collection', annotationcorpus.collections())] results = annotationcorpus.load(collection, name) if results: self.annotations = [] self.midifile = annotationcorpus.load_midifile(collection, name) for result in results: self.annotations += result.annotation metadata = results[0].metadata self.notelist = results[0].notes self.bpm = metadata['bpm'] self.offset = metadata['offset'] self.name = metadata['name'] self.meter = meter.Meter(metadata['beatspb'], metadata['beatdiv']) self.refreshAnnotation = True self.refreshMidi = True return True return False
def gui(self, stdscr): seq = self.seq my, mx = stdscr.getmaxyx() #curses.mousemask(curses.BUTTON1_CLICKED) curses.halfdelay(1) y = x = 0 scale = 10 time = 0.0 tracks = None length = None viewpos = 0 trackview = None viewwidth = mx - 9 - 3 maxscale = 100 speed = 1.0 following = True name = None while True: if not seq.running: pass #break stdscr.clear() curses.curs_set(0) stdscr.addstr(0, 0, '[(p)lay/pause] [(s)top] [choose (f)ile] [choose (t)rack] [choose (o)utput] [((j/k) slower/faster)] [toggle (F)ollowing]') stdscr.addstr(1, 0, '[(e)dit alignment] [e(x)port track]') if seq.midifile: stdscr.addstr(4, 3, 'File: {0}'.format(seq.midifile.name)) stdscr.addstr(5, 3, 'Midi Device: {0}'.format(seq.device_info())) stdscr.addstr(6, 3, 'Midi Track: {0}'.format(seq.currenttrack)) stdscr.addstr(7, 3, 'Player status: {0}'.format(seq.status)) stdscr.refresh() time = self.seq.time # Load tracks once the midifile is loaded if self.seq.midifile and name is not self.seq.midifile.name: name = self.seq.midifile.name tracks = [] for t in self.seq.midifile.values(): if len(t) > 0: tracks.append(t) tracks = sorted(tracks, key=lambda x: x.n) length = max([t.length() for t in tracks]) length /= 1000000.0 trackview = None # Paint the trackview if tracks and not trackview: trackview = \ cgui.trackview(stdscr, tracks, ypos=9, xpos=3, width=viewwidth, scale=scale) viewpos = 0 if tracks and trackview: if seq.mode == seq.PLAYING and following: if time*scale < round(viewwidth/2.0): viewpos = 0 elif time*scale > length*scale - round(viewwidth/2.0): viewpos = length*scale - viewwidth else: viewpos = time*scale - round(viewwidth/2.0) cgui.updatetracks(stdscr, viewpos, time, trackview, ypos=9, xpos=3, width=mx-10, scale=scale, currenttrack=seq.currenttrack) stdscr.addstr(len(tracks)+1+9, 3, 'Time: {0:.2f}\tTime left: {1:.2f}\tTrackview scale: {2}\tSpeed: {3}' .format(time, length-time, scale, speed)) c = stdscr.getch() # if c == curses.KEY_MOUSE: # (id, x, y, z, state) = curses.getmouse() # if state == curses.BUTTON1_CLICKED: # pass # if c == ord('q'): self.seq.control(self.seq.QUIT, None) break elif c == ord('F'): if following: following = False else: following = True elif c == ord('s'): seq.control(seq.STOP, None) elif c == ord('N'): seq.control(seq.NEXTNOTE, None) elif c == ord('p'): if seq.mode == seq.PLAYING: seq.control(seq.PAUSE, None) else: seq.control(seq.PLAY, None) elif c == ord('t'): if seq.midifile: choice = cgui.menu(stdscr, 'Choose a track',\ ['Track #{0}\tName: {1}\tNumber of notes: {2}'.format(\ seq.midifile[t].n, seq.midifile[t].name, len(seq.midifile[t]))\ for t in seq.tracklist()]) if choice < 0: continue seq.control(seq.LOADTRACK, seq.tracklist()[choice]) elif c == ord('j'): if speed > 0: speed -= 0.01 seq.control(seq.SETSPEED, speed) elif c == ord('k'): speed += 0.01 seq.control(seq.SETSPEED, speed) elif c == ord('h'): seq.control(seq.SETTIME, seq.time-1) elif c == ord('l'): seq.control(seq.SETTIME, seq.time+1) elif c == ord('f'): level = 1 midifile = None while level > 0: if level == 1: choice = cgui.menu(stdscr, 'Choose collection', midicorpus.collections()) if choice == -1: level -= 1 continue else: level += 1 collection = midicorpus.collections()[choice] elif level == 2: choice = cgui.menu(stdscr, 'Choose song', midicorpus.songs(collection=collection)) if choice == -1: level -= 1 continue else: level += 1 song = midicorpus.songs(collection=collection)[choice] elif level == 3: choice = cgui.menu(stdscr, 'Choose version', midicorpus.versions(song, collection=collection)) if choice == -1: level -= 1 continue else: level += 1 version = midicorpus.versions(song, collection=collection)[choice] elif level == 4: singletrack = False track = 0 if len(midicorpus.tracks(song, version, collection=collection)) > 0: singletrack = True choice = cgui.menu(stdscr, 'Choose track', midicorpus.tracks(song, version, collection=collection)) if choice == -1: level -= 1 continue track = midicorpus.tracks(song, version, collection=collection)[choice] else: level += 1 midifile = midicorpus.load(song, version, track, singletrack, collection=collection) break if not midifile: continue cgui.alert(stdscr, 'Loading file', block=False) seq.control(seq.LOADFILE, midifile) time = 0 elif c == ord('o'): choice = cgui.menu(stdscr, 'Choose a midi device', seq.devicelist()) if choice < 0: continue seq.control(seq.SETOUTPUT, choice) elif c == curses.KEY_LEFT: if viewpos > 0: viewpos -= 1 elif c == curses.KEY_RIGHT: if viewpos < length*scale - viewwidth: viewpos += 1 elif c == curses.KEY_UP: if scale < maxscale and trackview: scale += 1 trackview = \ cgui.trackview(stdscr, tracks, ypos=9, xpos=3, width=viewwidth, scale=scale) elif c == curses.KEY_DOWN: if scale > 1 and trackview: scale -= 1 trackview = \ cgui.trackview(stdscr, tracks, ypos=9, xpos=3, width=viewwidth, scale=scale) curses.endwin()
def execute(self, match): props = match.groupdict() if self.mode == self.INSERT: self.refreshAnnotation = True if props['command'] == ' ' or props['command'] == 'r': for (quarters, midipos, pitch, type) in self.annotations: if self.cursor == self.quarters2units(quarters) and not type in [Annotation.GRACE, Annotation.ERROR]: index = self.annotations.index((quarters, midipos, pitch, type)) del self.annotations[index] self.midipos = midipos if props['command'] == ' ': return True else: break if props['command'] == ' ': self.addNote() self.midipos += 1 self.seq.control(self.seq.STOP, None) self.seq.control(self.seq.SETEVENTS, self.midifile.nonemptytrack().toEvents(self.midipos-1, self.midipos)) self.seq.control(self.seq.PLAY, True) elif props['command'] == 'r': # Add rest self.addNote(type=Annotation.REST) elif props['command'] == 'g': # Add gracenote self.addNote(type=Annotation.GRACE) self.midipos += 1 elif props['command'] == 'e': # Add end marker self.addNote(type=Annotation.END) elif props['command'] == 'w': # Add end marker self.addNote(type=Annotation.SWUNG) self.midipos += 1 elif props['command'] == 's': # Skip and mark as error self.addNote(type=Annotation.ERROR) self.midipos += 1 elif re.match('t[0-9]+$', props['command']): # Add a triplet division = int(props['arg']) if not division: cgui.alert(self.stdscr, 'Enter a beatdivision: t<beatdivision>', block=True) return True allowed = [math.pow(2, p) for p in range(int(math.log(1/float(self.resolution))/math.log(2)-1))] if not division in allowed: cgui.alert(self.stdscr, 'Beatdivision {0} is invalid.\n'.format(division) +\ 'Either the resolution doesn\'t allow it (try :set resolution <division>)\n' +\ 'or it\'s not a power of two.\n' +\ 'allowed divisions: {0}'.format(allowed), block=True) return True pattern = cgui.prompt(self.stdscr, 'Enter notes for a triplet with duration 1/{0}'.format(division), length=3) exp = re.compile('([ nr])([ nr])([ nr])$') m = exp.match(pattern) if m: self.refreshAnnotation = True for g in range(3): position = self.units2quarters(self.cursor) + self.notelength2quarters(g*(1/float(division))/3.0) if m.group(g+1) == 'n': self.addNote(position=position) self.midipos += 1 elif m.group(g+1) == 'r': self.addNote(type=Annotation.REST, position=position) elif m.group(g+1) == ' ': pass self.cursor += self.notelength2units(1/float(division)) else: cgui.alert(self.stdscr, 'Couldn\'t parse input.', block=True) elif props['action']: if props['action'] == 'q': return False elif props['action'] == 'i': self.mode = self.INSERT self.status = 'Entering insert mode' elif props['action'] == 'r': self.mode = self.INSERT self.status = 'Entering insert mode' elif props['action'] == 'p': if self.mode == self.PLAYING: self.seq.control(self.seq.STOP, None) self.seq.control(self.seq.LOADFILE, self.midifile) self.seq.control(self.seq.SETEVENTS, self.midifile.nonemptytrack().toEvents(self.midipos)) self.seq.control(self.seq.PLAY, True) elif self.mode == self.ANNOTATING: mid = generator.annotations2midi([(quarters, pitch, type) for (quarters, midipos, pitch, type) in self.annotations], meter=self.meter, bpm=self.bpm) if mid.nonemptytrack(): self.seq.control(self.seq.STOP, None) self.seq.control(self.seq.LOADFILE, mid) self.seq.control(self.seq.SETEVENTS, mid.nonemptytrack().toEvents(self.notepos)) self.seq.control(self.seq.PLAY, True) self.status = 'Playing' elif props['action'] == 's': self.seq.control(self.seq.STOP, None) elif props['action'] == 'x' and self.mode == self.ANNOTATING: if len(self.annotations) > 0: del self.annotations[self.notepos] self.refreshAnnotation = True elif props['action'] == 's' and self.mode == self.PLAYING: pass else: if props['command'] == 'set ': if props['arg1'] == 'correction': self.viewcorrection = int(props['arg2']) self.refreshMidi = True self.status = 'Transposing {0} semitone(s)'.format(props['arg2']) elif props['arg1'] == 'beatdiv': self.meter.beatdiv = int(props['arg2']) self.refreshAnnotation = True self.status = 'Changed beatdivision' elif props['arg1'] == 'beatsperbar': self.meter.beatspb = int(props['arg2']) self.refreshAnnotation = True self.status = 'Changed beats per bar' elif props['arg1'] == 'resolution': if not self.setresolution(1/float(props['arg2'])): cgui.alert(self.stdscr, 'Invalid resolution.') self.refreshMidi = True self.refreshAnnotation = True elif props['command'] == 'restore': if cgui.menu(self.stdscr, 'Restore last session?', ['No', 'Yes']) == 1: if not self.load('autosave', 'lastsession'): cgui.alert(self.stdscr, 'No session found') elif props['command'] == 'save': self.save() elif props['command'] == 'strip': self.strip() elif props['command'] == 'subtract': self.subtract() elif props['command'] == 'score': (name, version, track, singletrack) = midi.parsename(self.name) index = rbsearch.load_file() hits = rbsearch.find(index, name.replace('_', ' ')) if len(hits) > 0: (song, book) = rbsearch.choose_book(index, hits, stdscr=self.stdscr) rbsearch.view(song, book) elif props['command'] == 'load': if not self.load(name=self.name): cgui.alert(self.stdscr, 'No annotations found!') elif props['command'] == 'q': return False return True