Пример #1
0
 def load_tablature(self, filename):
     '''Unpickle tab from a file'''
     try:
         if os.path.isfile(filename):
             infile = open(filename, 'rb')
             self.tab = pickle.load(infile)
             infile.close()
         else:
             self.tab = Tablature()
         self.file_name = filename
         self.set_term_title(filename + ' - VITABS')
         self.st = '{0} ({1} bars, tuning: {2})'.format(
             filename, len(self.tab.bars),
             music.tuning_str(getattr(self.tab, 'tuning', music.standard_E)))
     except:
         self.st = 'Error: Can\'t open the specified file'
Пример #2
0
    def __init__(self, stdscr, tab = Tablature()):
        self.root = stdscr
        self.tab = tab
        self.nmap = {}
        self.motion_commands = {}
        self.commands = {}

        self.player = Player()
Пример #3
0
def analyze(file):
    # Feature extraction example
    import numpy as np
    import librosa

    # Load the example clip
    y, sr = librosa.load(file)

    # Set the hop length; at 22050 Hz, 512 samples ~= 23ms
    hop_length = 512

    # Separate harmonics and percussives into two waveforms
    y_harmonic, y_percussive = librosa.effects.hpss(y)

    y_harmonic = abs(y_harmonic)
    y_percussive = abs(y_percussive)

    # FIXME: this should be sampling rate dependant
    max_filter_win_size = 1000
    y_harmonic = maximum_filter(y_harmonic, max_filter_win_size)
    y_harmonic = gaussian_filter1d(y_harmonic, 1000)

    y_percussive = maximum_filter(y_percussive, max_filter_win_size)
    y_percussive = gaussian_filter1d(y_percussive, 1000)

    # normalize
    amax = np.amax(y_harmonic)
    y_harmonic = y_harmonic / amax

    amax = np.amax(y_percussive)
    y_percussive = y_percussive / amax

    y_total = y_percussive + y_harmonic

    #max_positions = argrelextrema(y_total, np.greater)[0]
    max_positions, _ = find_peaks(y_total, distance=150, prominence=0.1)

    # open file with wavfile

    previous_result = [0] * 84

    model = tf.keras.models.load_model("model")
    tablature = Tablature()

    limits = np.zeros(y_total.shape[0])

    for pulse in range(0, len(max_positions) - 1):
        time_from = max_positions[pulse]
        time_to = max_positions[pulse + 1]

        diff = time_to - time_from
        DIFF_PERCENTAGE = 0.8
        C = np.abs(
            librosa.cqt(y[time_from:time_from + int(diff * DIFF_PERCENTAGE)] /
                        float(65535),
                        sr=sr,
                        norm=0,
                        filter_scale=3))

        from_print = (1 / sr) * time_from
        to_print = (1 / sr) * (time_from + diff * DIFF_PERCENTAGE)

        limits[int(from_print * sr)] = 1
        limits[int(to_print * sr)] = 1

        # get info
        print("note " + str(pulse) + " from: " + str(from_print) + "s to: " +
              str(to_print) + "s")
        wavfile.write("temp/" + str(pulse) + ".wav", sr,
                      y[time_from:int(time_from + diff * DIFF_PERCENTAGE)])

        # add all samples
        result = np.sum(C, axis=1)

        amax = np.amax(result)
        result = result / amax

        new_result = result - previous_result
        previous_result = result

        # normalize
        amax = np.amax(new_result)

        # ignore silence pulses
        if amax < 0.01:
            continue

        new_result = new_result / amax

        # predict values
        prediction = model.predict(np.array([new_result]))

        new_result = np.where(prediction[0] == np.amax(prediction[0]))
        note_name = new_result[0]

        tablature.addNotes(note_name + noteutils.NoteUtils.offsetGuitar)
        """if cur_time != previous_time:
            previous_time = cur_time//3
            tablature.addTime(str(cur_time))"""

    # plot
    time = (1 / sr) * len(y_total)
    xf = np.linspace(0.0, time, len(y_total))
    plt.plot(xf, y_total)
    plt.plot(xf, limits)
    plt.plot(xf, y)
    plt.plot(max_positions / sr, y_total[max_positions], "x")
    plt.show()

    tablature.print()
Пример #4
0
class Editor:
    screen_initiated = False
    cursor_prev_bar_x = 2
    insert_duration = Fraction('1/4')
    st = ''
    file_name = None
    terminate = False
    visible_meta = 'meter'
    continuous_playback = False
    yanked_bar = None
    string = 0

    def __init__(self, stdscr, tab = Tablature()):
        self.root = stdscr
        self.tab = tab
        self.nmap = {}
        self.motion_commands = {}
        self.commands = {}

        self.player = Player()

    def init_screen(self):
        screen_height, screen_width = self.root.getmaxyx()
        self.stdscr = curses.newwin(screen_height - 1, 0, 0, 0)
        self.stdscr.keypad(1)

        if self.file_name:
            self.set_term_title(self.file_name + ' - VITABS')
        else:
            self.set_term_title('[unnamed] - VITABS')

        self.status_line = curses.newwin(0, 0, screen_height - 1, 0)
        self.status_line.scrollok(False)

        self.first_visible_bar = self.tab.cursor_bar
        self.redraw_view()
        self.cy = 2
        self.move_cursor()
        curses.doupdate()

        self.screen_initiated = True
    
    def make_motion_cmd(self, f):
        '''Turn a motion command into a normal mode command'''
        def motion_wrap(ed, num):
            m = f(ed, num)
            if m is not None:
                ed.make_motion(f(ed, num))
        motion_wrap.__name__ = f.__name__
        motion_wrap.__doc__ = f.__doc__
        motion_wrap.nosidefx = True
        return motion_wrap

    def mark_changed(self):
        if not getattr(self.tab, 'changed', False):
            if self.file_name:
                self.set_term_title(self.file_name + ' + - VITABS')
            else:
                self.set_term_title('[unnamed] + - VITABS')
        self.tab.changed = True

    def register_handlers(self, module):
        '''Add commands defined in the module'''
        for f in module.__dict__.itervalues():
            if hasattr(f, 'normal_keys'):
                if getattr(f, 'motion_command', False):
                    for k in f.normal_keys:
                        self.nmap[k] = self.make_motion_cmd(f)
                        self.motion_commands[k] = f
                else:
                    for k in f.normal_keys:
                        self.nmap[k] = f
            if hasattr(f, 'handles_command'):
                self.commands[f.handles_command] = f
            
    def load_tablature(self, filename):
        '''Unpickle tab from a file'''
        try:
            if os.path.isfile(filename):
                infile = open(filename, 'rb')
                self.tab = pickle.load(infile)
                infile.close()
            else:
                self.tab = Tablature()
            self.file_name = filename
            self.set_term_title(filename + ' - VITABS')
            self.st = '{0} ({1} bars, tuning: {2})'.format(
                filename, len(self.tab.bars),
                music.tuning_str(getattr(self.tab, 'tuning', music.standard_E)))
        except:
            self.st = 'Error: Can\'t open the specified file'

    def save_tablature(self, filename):
        '''Pickle tab to a file'''
        if hasattr(self.tab, 'changed'):
            self.tab.changed = False
            delattr(self.tab, 'changed')
        try:
            outfile = open(filename, 'wb')
            pickle.dump(self.tab, outfile)
            outfile.close()
            self.file_name = filename
        except:
            self.st = 'Error: Can\'t save'
        self.set_term_title(filename + ' - VITABS')
    
    def set_term_title(self, text):
        '''Atempt to change virtual terminal window title'''
        import sys
        try:
            term = os.environ['TERM'] 
            if 'xterm' in term or 'rxvt' in term:
                sys.stdout.write('\033]0;' + text + '\007')
                sys.stdout.flush()
        except:
            pass

    def draw_bar(self, y, x, bar):
        '''Render a single bar at specified position'''
        stdscr = self.stdscr
        screen_width = self.stdscr.getmaxyx()[1]
        stdscr.vline(y, x - 1, curses.ACS_VLINE, 6)
        gcd = bar.gcd()
        total_width = bar.total_width(gcd)
        for i in range(6):
            stdscr.hline(y + i, x, curses.ACS_HLINE, total_width)
        x += 1
        for chord in bar.chords:
            for i in chord.strings.keys():
                if x < screen_width:
                    stdscr.addstr(y+i, x, str(chord.strings[i]), curses.A_BOLD)
            # should it really be here?
            if self.visible_meta == 'length':
                dstr = music.len_str(chord.duration)
                if x + len(dstr) < screen_width:
                    stdscr.addstr(y - 1, x, dstr)
            width = int(chord.duration / gcd)
            x = x + width*2 + 1
        if x + 1 < screen_width:
            stdscr.vline(y, x + 1, curses.ACS_VLINE, 6)
        return x + 2

    def draw_bar_meta(self, y, x, bar, prev_bar, index):
        '''Print additional bar info at specified position'''
        if self.visible_meta == 'meter':
            if (prev_bar == None 
                    or bar.sig_num != prev_bar.sig_num 
                    or bar.sig_den != prev_bar.sig_den):
                self.stdscr.addstr(
                    y, x, 
                    str(bar.sig_num) + '/' + str(bar.sig_den))
        elif self.visible_meta == 'number':
            self.stdscr.addstr(y, x, str(index))
        elif self.visible_meta == 'label':
            if hasattr(bar, 'label'):
                self.stdscr.addstr(y, x, bar.label)

    def draw_tab(self, t):
        '''Render the whole tablature'''
        x = 2
        y = 1
        prev_bar = None
        screen_height, screen_width = self.stdscr.getmaxyx()
        for i, tbar in enumerate(t.bars[self.first_visible_bar - 1 : ]):
            bar_width = tbar.total_width(tbar.gcd())
            if x + bar_width >= screen_width and x != 2:
                x = 2
                y += 8
            if y + 8 > screen_height:
                break
            self.draw_bar_meta(y, x, tbar, prev_bar, self.first_visible_bar + i)
            x = self.draw_bar(y + 1, x, tbar)
            self.last_visible_bar = i + self.first_visible_bar

            prev_bar = tbar
    
    def redraw_view(self):
        '''Redraw tab window'''
        self.stdscr.erase()
        self.draw_tab(self.tab) # merge theese functions?
        self.stdscr.noutrefresh()
    
    def term_resized(self):
        '''Called when the terminal window is resized, updates window sizes'''
        height, width = self.root.getmaxyx()
        self.status_line.mvwin(height - 1, 0)
        self.stdscr.resize(height - 1, width)
        self.redraw_view()
        self.move_cursor()
    
    def redraw_status(self):
        '''Update status bar'''
        width = self.status_line.getmaxyx()[1]
        self.status_line.erase()
        # general purpose status line
        self.status_line.addstr(0, 0, self.st)
        # position indicator
        self.status_line.addstr(
            0, width - 8, 
            '{0},{1}'.format(self.tab.cursor_bar, self.tab.cursor_chord))
        # note length indicator
        self.status_line.addstr(
            0, width - 16,
            str(self.tab.get_cursor_chord().duration))
        # meter incomplete indicator
        cb = self.tab.get_cursor_bar()
        if cb.real_duration() != cb.required_duration():
            self.status_line.addstr(0, width - 18, 'M')
        self.status_line.noutrefresh()

    def pager(self, lines):
        '''Display a list of lines in a paged fashion'''
        self.root.scrollok(True)
        self.root.clear()
        i = 0
        h = self.root.getmaxyx()[0]
        for line in lines:
            self.root.addstr(i, 1, line)
            i += 1
            if i == h - 1:
                self.root.addstr(i, 0, '<Space> NEXT PAGE')
                self.root.refresh()
                while self.get_char() != ord(' '): pass
                self.root.clear()
                i = 0
        self.root.addstr(h - 1, 0, '<Space> CONTINUE')
        while self.get_char(self.root) != ord(' '): pass
        self.root.scrollok(False)
        self.root.clear()
        self.redraw_view()

    def move_cursor(self, new_bar=None, new_chord=None, cache_lengths=False):
        '''Set new cursor position'''
        if not new_bar: new_bar = self.tab.cursor_bar
        if not new_chord: new_chord = self.tab.cursor_chord
        if not cache_lengths: self.cursor_prev_bar_x = None

        # make sure the cursor stays inside the visible bar range
        if new_bar < self.first_visible_bar or new_bar > self.last_visible_bar:
            self.first_visible_bar = new_bar
            self.redraw_view()

        newbar_i = self.tab.bars[new_bar - 1]
        
        # calculate the width of preceeding bars
        screen_height, screen_width = self.stdscr.getmaxyx()
        if new_bar != self.tab.cursor_bar or self.cursor_prev_bar_x == None:
            self.cursor_prev_bar_x = 2
            self.cy = 2
            if new_bar > self.first_visible_bar:
                for b in self.tab.bars[self.first_visible_bar - 1 : new_bar - 1]:
                    barw = b.total_width(b.gcd()) + 1

                    self.cursor_prev_bar_x += barw

                    if (self.cursor_prev_bar_x > screen_width and
                            self.cursor_prev_bar_x != 2 + barw):
                        self.cursor_prev_bar_x = 2 + barw
                        self.cy += 8

                # should the cursor bar be wrapped?
                newbar_w = newbar_i.total_width(newbar_i.gcd()) + 1
                if newbar_w + self.cursor_prev_bar_x > screen_width:
                    self.cursor_prev_bar_x = 2
                    self.cy += 8

        # width of preceeding chords
        offset = 1
        gcd = newbar_i.gcd()
        for c in newbar_i.chords[:new_chord - 1]:
            offset += int(c.duration / gcd)*2 + 1

        self.tab.cursor_bar = new_bar
        self.tab.cursor_chord = new_chord
        self.cx = self.cursor_prev_bar_x + offset
    
    def make_motion(self, pos):
        self.move_cursor(pos[0], 1 if pos[1] is None else pos[1],
                         cache_lengths=True)

    def go_left(self, num=1):
        '''Returns position pair [num] chords left from the cursor'''
        if self.tab.cursor_chord <= num:
            if self.tab.cursor_bar > 1:
                return (self.tab.cursor_bar - 1, 
                        len(self.tab.bars[self.tab.cursor_bar - 2].chords))
            else:
                return (1, 1)
        else:
            return (self.tab.cursor_bar, self.tab.cursor_chord - num)

    def move_cursor_left(self):
        self.make_motion(self.go_left())

    def go_right(self, num=1):
        '''Returns position pair [num] chords right from the cursor'''
        if self.tab.cursor_chord + num > len(self.tab.get_cursor_bar().chords):
            if self.tab.cursor_bar < len(self.tab.bars):
                return (self.tab.cursor_bar + 1, 1)
            else:
                return self.tab.last_position()
        else:
            return (self.tab.cursor_bar, self.tab.cursor_chord + num)

    def move_cursor_right(self):
        self.make_motion(self.go_right())
    
    def play_range(self, fro, to):
        def redraw_playback_status():
            self.st = 'Playing... <CTRL-C> to abort'
            self.redraw_status()
            curses.setsyx(self.cy - 1, self.cx)
            curses.doupdate()

        def move_to_beginning():
            self.move_cursor(fro[0], fro[1])
            redraw_playback_status()
            return True

        def update_playback_status():
            self.move_cursor_right()
            redraw_playback_status()
            return True

        p = self.player
        p.before_repeat = move_to_beginning
        p.post_play_chord = update_playback_status
        p.set_instrument(getattr(self.tab, 'instrument', 24))
        p.play(ChordRange(self.tab, fro, to), self.continuous_playback)
        self.st = ''

    def get_char(self, parent=None):
        '''Get a character from terminal, handling things like terminal
        resize'''
        if parent is None:
            parent = self.stdscr
        c = parent.getch()
        if c == curses.KEY_RESIZE:
            self.term_resized()
        return c

    def insert_mode(self, free_motion=False):
        '''Switch to insert mode and listen for keys'''
        if free_motion:
            self.st = '-- REPLACE --'
        else:
            self.st = '-- INSERT --'

        self.redraw_view()

        insert_beg = self.tab.cursor_position()
        insert_end = insert_beg

        while True:
            self.redraw_status()
            curses.setsyx(self.cy + self.string, self.cx)
            curses.doupdate()

            c = self.get_char()
            if c == 27: # ESCAPE
                self.st = ''
                break

            elif ord('0') <= c <= ord('9'):
                curch = self.tab.get_cursor_chord()
                string = self.string
                if string in curch.strings and curch.strings[string].fret < 10:
                    st_dec = curch.strings[string].fret * 10 
                    curch.strings[string].fret = st_dec + c - ord('0')
                else:
                    curch.strings[string] = Fret(c - ord('0'))
                self.redraw_view()
            elif c == curses.KEY_DC or c == ord('x'):
                if self.string in self.tab.get_cursor_chord().strings:
                    del self.tab.get_cursor_chord().strings[self.string]
                    self.redraw_view()

            elif c == curses.KEY_UP or c == ord('k'):
                self.string = max(self.string - 1, 0)
            elif c == curses.KEY_DOWN or c == ord('j'):
                self.string = min(self.string + 1, 5)
            elif c == ord('E'): self.string = 5
            elif c == ord('A'): self.string = 4
            elif c == ord('D'): self.string = 3
            elif c == ord('G'): self.string = 2
            elif c == ord('B'): self.string = 1
            elif c == ord('e'): self.string = 0

            elif c == ord(' '):
                # TODO: don't repeat yourself...
                self.tab.get_cursor_bar().chords.insert(
                        self.tab.cursor_chord, 
                        Chord(self.insert_duration))
                self.redraw_view()
                self.move_cursor_right()
                self.move_cursor()
                insert_end = (insert_end[0], insert_end[1] + 1)

            elif (c == curses.KEY_RIGHT or c == ord('l')) and not free_motion:
                right = (self.tab.cursor_bar, self.tab.cursor_chord + 1)
                if right > insert_end:
                    self.tab.get_cursor_bar().chords.insert(
                            self.tab.cursor_chord, 
                            Chord(self.insert_duration))
                    self.redraw_view()
                    insert_end = right
                self.make_motion(right)
                self.move_cursor()

            elif (c == curses.KEY_LEFT or c == ord('h')) and not free_motion:
                left = self.go_left()
                if left >= insert_beg:
                    self.make_motion(left)

            elif c == curses.KEY_RIGHT or c == ord('l') and free_motion:
                self.move_cursor_right()

            elif (c == curses.KEY_LEFT or c == ord('h')) and free_motion:
                self.move_cursor_left()

            try:
                # try to find a symbol in key -> symbol dict
                sym = symbols.keys[c]
                fr = self.tab.get_cursor_chord().strings[self.string]
                if sym in fr.symbols:
                    fr.symbols.remove(sym)
                else:
                    fr.symbols.append(sym)
                self.redraw_view()
            except KeyError:
                pass

    def exec_command(self, args, apply_to=None):
        cmd = args[0]
        try:
            if apply_to is not None:
                try:
                    self.commands[cmd](self, args, apply_to=apply_to)
                except TypeError:
                    self.st = 'Command does not accept range'
            else:
                self.commands[cmd](self, args)
        except KeyError:
            self.st = 'Invalid command'

    def command_mode(self):
        '''Read a command'''
        import sys
        curses.echo()
        self.status_line.erase()
        self.status_line.addstr(0, 0, ":")
        try:
            line = self.status_line.getstr(0, 1) 
        except KeyboardInterrupt:
            line = ''
        words = line.split(' ')
        cmd = words[0]
        curses.noecho()
        if cmd:
            try:
                self.exec_command(words)
            except:
                exc = sys.exc_info()
                self.st = "Exception: " + str(exc[0].__name__) + ": " + \
                str(exc[1])
        self.redraw_view()

    def _is_number(self, char):
        return (ord('0') <= char <= ord('9'))
    
    def _parse_numeric_arg(self, c, num_arg):
        if num_arg:
            num_arg = num_arg * 10 + c - ord('0')
        elif c != ord('0'):
            num_arg = c - ord('0')
        return num_arg

    def expect_range(self, num=None, whole_bar_cmd=None):
        '''Get a motion command and return a range from cursor position to
           motion'''
        num_motion = None
        c = self.get_char()
        while self._is_number(c) and (c != ord('0') or num_motion):
            num_motion = self._parse_numeric_arg(c, num_motion)
            c = self.get_char()
        if num_motion and num: total_num = num * num_motion
        elif num_motion: total_num = num_motion
        else: total_num = num

        cur = self.tab.cursor_position()
        if whole_bar_cmd and c == whole_bar_cmd:
            return ChordRange(self.tab,
                    (cur[0], 1),
                    (cur[0], None))
        try:
            dest = self.motion_commands[c](self, total_num)
            if dest:
                if dest > cur:
                    return ChordRange(self.tab, cur, dest)
                else:
                    return ChordRange(self.tab, dest, cur)
        except KeyError:
            return None

    def normal_mode(self):
        '''Enter normal mode, returns on quit'''
        num_arg = None
        t = self.tab
        
        while True:
            if self.terminate:
                break

            self.redraw_status()
            self.st = ''
            curses.setsyx(self.cy - 1, self.cx)
            curses.doupdate()
            # TODO: accept multi-char commands
            try:
                c = self.get_char()

                if c in self.nmap:
                    cmd = self.nmap[c]
                    cmd(self, num_arg)
                    if not (getattr(cmd, 'nosidefx', False)):
                        self.mark_changed()
                        self.redraw_view()

                if self._is_number(c):
                    num_arg = self._parse_numeric_arg(c, num_arg)
                    if num_arg: self.st = str(num_arg)
                else:
                    # reset after a command
                    num_arg = None

                if c == 27: # ESCAPE
                    self.st = ''

            except KeyboardInterrupt:
                self.st = 'Use :q<Enter> to quit'