Пример #1
0
class Swiat:
    def __init__(self, n, m, sc):
        self.N = n
        self.tura = 0
        self.M = m
        self.l = Logs(self)
        self.queue = Kolejka()
        self.aktualnygatunek = "Antylopa"
        self.aktualnyobrazek = pygame.image.load(self.aktualnygatunek + ".bmp")
        self.plikdozapisu = "save.txt"
        self.dozabicia = Zabij()
        self.plansza = [[None for i in range(self.M)] for j in range(self.N)]
        self.ekran = sc
        self.tlo = pygame.image.load("panelgry.png")
        self.pustepole = pygame.image.load("pusty.bmp")
        if n >= m:
            self.skalowanie = int(400 / n)
        else:
            self.skalowanie = int(400 / m)

    def zmienaktualnygatunek(self, str):
        self.aktualnygatunek = str
        self.aktualnyobrazek = pygame.image.load(self.aktualnygatunek + ".bmp")
        self.rysujSwiat()
        pygame.display.update()

    def setField(self, x, y, c):
        self.plansza[x][y] = c

    def GetField(self, x, y):
        return self.plansza[x][y]

    def getN(self):
        return self.N

    def getM(self):
        return self.M

    def CheckField(self, x, y):
        if x < self.N and x >= 0 and y < self.M and y >= 0:
            return True
        return False

    def wykonajTure(self):
        i = 0
        while i < self.queue.length():
            if self.queue.at(i).getGatunek() == "Czlowiek":
                self.dozabicia.kill()
                self.rysujSwiat()
                pygame.display.update()
                self.l.clear()
                self.l.add("Ruch czlowieka!")
                self.l.log()
            self.queue.at(i).akcja()
            i += 1
        self.dozabicia.kill()

    def rysujSwiat(self):
        self.ekran.blit(self.tlo, (0, 0))
        self.ekran.blit(self.aktualnyobrazek, (770, 42))
        x = 5
        y = 5
        for i in range(0, self.N):
            for j in range(0, self.M):
                if self.GetField(i, j) != None:
                    self.ekran.blit(
                        self.GetField(i, j).rysujorganizm(), (x, y))
                else:
                    self.ekran.blit(self.pustepole, (x, y))
                x += self.skalowanie
            x = 5
            y += self.skalowanie

    def save(self):
        file = open(self.plikdozapisu, "w")
        file.write(str(self.N))
        file.write("\n")
        file.write(str(self.M))
        file.write("\n")
        file.write(str(self.queue.length()))
        file.write("\n")
        for i in range(0, self.queue.length()):
            file.write(self.queue.at(i).getGatunek())
            file.write("\n")
            if self.queue.at(i).getGatunek() == "Czlowiek":
                file.write(str(self.queue.at(i).czasumiejetnosci))
                file.write("\n")
            file.write(str(self.queue.at(i).getX()))
            file.write("\n")
            file.write(str(self.queue.at(i).getY()))
            file.write("\n")
            file.write(str(self.queue.at(i).getSila()))
            file.write("\n")
            file.write(str(self.queue.at(i).getIni()))
            file.write("\n")
            file.write(str(self.queue.at(i).getDZ()))
            file.write("\n")
        file.close()
        self.l.clear()
        self.l.add("Pomyślnie zapisano w " + self.plikdozapisu)
        self.rysujSwiat()
        self.l.log()

    def load(self):
        self.plikdozapisu = str(self.plikdozapisu)
        self.plikdozapisu = self.plikdozapisu[25:]
        index = self.plikdozapisu.index("'")
        self.plikdozapisu = self.plikdozapisu[0:index]
        file = open(self.plikdozapisu, "r")
        self.N = int(file.readline())
        self.M = int(file.readline())
        if self.N >= self.M:
            self.skalowanie = int(400 / self.N)
        else:
            self.skalowanie = int(400 / self.M)
        i = int(file.readline())
        for k in range(0, i):
            gatunek = str(file.readline())
            gatunek = gatunek[:len(gatunek) - 1]
            if gatunek == "Czlowiek":
                ablity = int(file.readline())
            x = int(file.readline())
            y = int(file.readline())
            sila = int(file.readline())
            inicjatywa = int(file.readline())
            dlugosczycia = int(file.readline())
            if gatunek == "Czlowiek":
                pom = Czlowiek(self, x, y)
                pom.czasumiejetnosci = ablity
            if gatunek == "BSosnowskiego":
                pom = BSosnowskiego(self, x, y)
            if gatunek == "Guarana":
                pom = Guarana(self, x, y)
            if gatunek == "Mlecz":
                pom = Mlecz(self, x, y)
            if gatunek == "Trawa":
                pom = Trawa(self, x, y)
            if gatunek == "WilczeJagody":
                pom = WilczeJagody(self, x, y)
            if gatunek == "Antylopa":
                pom = Antylopa(self, x, y)
            if gatunek == "Lis":
                pom = Lis(self, x, y)
            if gatunek == "Owca":
                pom = Owca(self, x, y)
            if gatunek == "Wilk":
                pom = Wilk(self, x, y)
            if gatunek == "Zolw":
                pom = Zolw(self, x, y)
            if gatunek == "Cyberowca":
                pom = Cyberowca(self, x, y)
            pom.setSila(sila)
            pom.setIni(inicjatywa)
            pom.setDZ(dlugosczycia)
            self.queue.add(pom)
        self.l.add("Poprawnie zaladowano")
        self.l.log()
Пример #2
0
class Resource(BaseNamespace, BroadcastMixin):
    def initialize(self):
        # Called after socketio has initialized the namespace.
        self.history = []
        self.parsed_seqs_notes = {}
        self.unit_dur = 60/92.0

        self.ngram = retrieve_NGram()
        self.nn = retrieve_SkipGramNN()

        assert self.ngram.syms == self.nn.syms

        self.previous_sym = None
        self.previous_sym_ind = None
        self.n_suggestions = 5
        self.n_similar = 2

        self.suggestions = SuggestionList(self)
        self.suggestions_above = SuggestionList(self)

        self.config = get_configs()
        self.corpus = self.config['corpus']
        print '...corpus', self.corpus

        if self.config['use_letternames']:
            self.symbol_type = 'letter'
        else:
            self.symbol_type = 'roman'

        # need to correct some roman numerals
        print '# of syms: %d' % len(self.ngram.syms)
        self.syms = []
        for sym in self.ngram.syms:
            formatted_sym, valid = self.format_sym(sym)
            self.syms.append(formatted_sym)

        # print 'F#m in syms?', 'F#m' in self.syms

        # need to update the "spelling" of roman numerals in nn and ngram
        self.nn.syms = self.syms
        self.ngram.syms = self.syms

        self._rn2letter, self._letter2rn = self.load_rn2letter_dict()

        self.experiment_type = EXPERIMENT_TYPE

        self.logs = Logs(EXPERIMENT_TYPE, EXPERIMENT_TYPE_STRS)


    @property
    def model(self):
        return self.ngram

    def load_rn2letter_dict(self):
        fname = 'rn2letter-rock.pkl'
        fpath = os.path.join(PKL_FOLDER, fname)
        with open(fpath, 'rb') as p:
            rn2letter = pickle.load(p)
        letter2rn = {}

        # TODO: is this a bug? should be letter2rn[val] = key
        for key, val in rn2letter.iteritems():
            letter2rn[key] = val
        return rn2letter, letter2rn

    def on_inputSave(self, text):
        # log add automatically saves upon seeing type "save"
        self.logs.add("save", text)

    def on_clear(self, text):
        self.logs.add("clear", text)
        self.clear_suggestions()

    def on_rating(self, value, ind, text):
        try:
            ind = int(ind)
        except ValueError:
            print 'WARNING: index to save entry wanted, but received', ind

        if len(text) > 0:
            self.logs.add_rating(value, 'save', text, ind)
        logs = self.logs.save
        for log in logs:
            print log

    def on_setPlaybackSpeed(self, speed):
        print '...playbackspeed',
        self.unit_dur = 60.0 / speed
        print self.unit_dur

    def disconnect(self, *a, **kw):
        super(Resource, self).disconnect(*a, **kw)

    def on_ping(self, param):
        print '---on_ping', param
        self.emit('pong', param)

    def on_requestData(self):
        # data = [{'x':10, 'y':15, 'label':'I'},
        #         {'x':10, 'y':15, 'label':'IV'}]
        fname = 'w1-RN.pkl'
        with open(fname, 'rb') as p:
            vertices = pickle.load(p)
            labels = pickle.load(p)

        # vertices = [[100, 150], [200, 150]]
        # labels = ['I', 'IV']
        max_x = np.max(vertices[:, 0])
        min_x = np.min(vertices[:, 0])
        x_range = max_x - min_x

        max_y = np.max(vertices[:, 1])
        min_y = np.min(vertices[:, 1])
        y_range = max_y - min_y

        width = 750.0
        margin = 30
        scale = (width-2*margin)/y_range
        if x_range > y_range:
            scale = (width-2*margin)/x_range

        vertices[:, 0] -= min_x
        vertices[:, 1] -= min_y
        vertices *= scale
        vertices[:, 0] += margin
        vertices[:, 1] += margin

        vertices = map(list, vertices)
        self.emit('dataLabels', vertices, labels)

    def parse_seq_as_notes(self, text):
        text = text.strip()
        if text in self.parsed_seqs_notes.keys():
            chords = self.parsed_seqs_notes[text]
            return chords
        parts, raw_parts = self.parse_seq_as_syms(text)
        chords = self.make_note_seqs(parts)
        self.parsed_seqs_notes[text] = chords
        return chords

    def parse_seq_as_syms(self, text):
        text = text.strip()
        parts = text.split(' ')
        parts = [part for part in parts if len(part) > 0]
        parts, raw_parts = self.format_seq(parts)
        return parts, raw_parts

    def make_note_seqs(self, seq):
        print 'make_note_seqs', seq
        note_seq = []
        for sym in seq:
            notes = self.make_notes(sym)
            if len(note_seq) == 0:
                note_seq.append(notes)
                continue
            if len(notes) == 0:
                note_seq.append([])
                continue
            if len(note_seq[-1]) == 0:
                diff = 0
            else:
                diff = np.min(notes) - np.min(note_seq[-1])
            # print '...make_note_seqs', sym, notes, diff, diff % 12
            if np.abs(diff) > 6:
                # i.e. C4 to B4 => C4 to B3
                # i.e. C4 to B5 => C4 to B3
                shift_int = (np.abs(diff) / 12) * 12
                # prefer going down
                # TODO: temp disable
                if np.abs(diff) % 12 > 6 and diff > 0:
                    shift_int += 12
                    # shift_int -= 12

                direction = np.sign(diff) * -1
                notes = np.asarray(notes) + shift_int * direction
                print 'notes[0]', notes[0]
                if notes[0] <= 55:
                    notes += 12
                note_seq.append(list(notes))
            else:
                note_seq.append(notes)
            # print 'shifted', notes

        print 'playSeq, chords'
        for i, ch in enumerate(note_seq):
            print seq[i],
            for p in ch:
                print pitch.Pitch(p).nameWithOctave,
            print ch
        print

        return note_seq

    def make_notes(self, sym):
        sym = sym.strip()
        if sym in MAKE_NOTE_EXCEPTIONS:
            # look up in dictionary
            letter = self.rn2letter(sym)
            print 'due to roman not giving right pitches', sym, letter
            sym = letter
        chord_sym = sym2chord(sym)
        midi = []
        if chord_sym is not None:
            midi = [pch.midi for pch in chord_sym.pitches]
            # take away duplicates
            midi = list(set(midi))
            midi = sorted(midi)
            # double the tonic on top
            if sym in ['I', 'I7', 'i']:
                doubled_note = midi[0]
                midi.append(doubled_note+12)
                print 'doubled tonic on top', midi
            elif len(midi) > 4 and '11' in sym:
                # 11th creates half step with 3rd if major (not checking if major here)
                # shift 11th up an octave to make
                print midi
                # 1,3,11,5,7,9
                reduced_midi = midi[:2] + [midi[3]] + [midi[2]+12]
                midi = reduced_midi
            elif len(midi) > 4:
                reduced_midi = midi[:3] + [midi[-1]]
                midi = reduced_midi
        return midi

    def make_log_tags_for_playback(self, text, ind, author, suggestPanel):
        tags = {}
        print 'make_log_tags_for_playback', author
        if author == 'machine':
            item = self.retrieve_suggestion_item_at(ind, text, suggestPanel)
            if item is None:
                print 'WARNNG: can not retrieve suggestion item', ind, text
                assert False
            tags['suggestion_item'] = item
        tags['author'] = author
        return tags

    def on_playSeq(self, text, pos, author, item_ind, suggestPanel=None, loop=False):
        print '...on_playSeq, loop', loop
        if author == 'dont_log':
            pass
        elif loop:
            tags = self.make_log_tags_for_playback(text, item_ind, author, suggestPanel)
            self.logs.add("loop", text, tags)
        else:
            tags = self.make_log_tags_for_playback(text, item_ind, author, suggestPanel)
            self.logs.add("play", text, tags)

        print '---on_parseSeq:', text
        chords = self.parse_seq_as_notes(text)
        durs = [self.unit_dur] * len(chords)
        if loop:
            chords = chords[:] + chords[:]
            durs = durs[:-1] + [durs[-1]+self.unit_dur*0.2] + durs[:]

        # durs = [self.unit_dur] * len(chords)
        print chords
        print durs

        self.emit('playSeq', chords, durs)

    def on_playSubseq(self, chord_changes, author, item_ind, suggestPanel, log=True):
        print '...on_playSubseq', chord_changes, author, item_ind, log, suggestPanel
        print chord_changes
        syms = []
        inds = []
        # assuming continuous
        for i, chord_change in enumerate(chord_changes):
            if chord_change[1]:
                syms.append(chord_change[0])
                inds.append(i)
        print len(inds), inds
        if len(inds) > 1:
            p_ind = inds[0]
            for ind in inds[1:]:
                if ind - p_ind != 1:
                    print 'WARNING: Changes not continuous'
                p_ind = ind

        # TODO: a bit of a hack here
        all_chords = [ch[0] for ch in chord_changes]

        # add to log
        text = ' '.join(all_chords)
        if log:
            tags = self.make_log_tags_for_playback(text, item_ind, author, suggestPanel)
            self.logs.add("play bold", text, tags)

        # need context to determine octave
        all_notes = self.parse_seq_as_notes(text)
        notes = [all_notes[ind] for ind in inds]
        durs = [self.unit_dur] * len(syms)
        self.emit('playSeq', notes, durs)

    def get_similar_chords(self, sym, topn):
        similars = self.nn.most_similar(sym, topn=topn)
        if similars is None:
            return
        sims = [s[0] for s in similars]
        return sims

    def on_startSeqs(self):
        if self.experiment_type == MANUAL:
            return
        # want diverse sequences
        print '---on_startSeqs'
        # TODO: fixed start seqs
        if self.symbol_type == 'roman':
            # sims = [[u'I', u'vi'], ['V', 'vi'], ['IV', 'ii'], [u'V7/IV', u'IV'], [u'i', u'i'], [u'i', u'V6']]
            sims = [[u'i'], [u'I'], ['V'], ['iv'], ['IV'], ['V7/IV']]
        else:
            # sims = [['a', 'C64'], ['G', 'C'], ['A', 'gb'], ['C', 'F'], ['G', 'e'], ['Bb', 'Ab'], ['E', 'G']]
            # sims = [['C'], ['F'], ['G'], ['Am'], ['Cm'], ['B-'], ['A-']]
            sims = [['Cm'], ['B-'], ['F/C'], ['G7'], ['Dm7'], ['Cmaj7'], ['F#dim']]

        self.clear_suggestions()
        for s in sims:
            self.suggestions.add(SuggestionItem(s, range(len(s)), 'start'))
        seqs, inds = self.suggestions.get_seqs_inds()
        # print seqs
        # print inds
        self.emit('updateChordSuggestions', seqs, inds)

    def rn2letter(self, sym):
        if sym in ROMAN2LETTER_RELABELS:
            formatted_sym = ROMAN2LETTER_RELABELS[sym]
        elif sym in self._rn2letter:
            formatted_sym = self._rn2letter[sym]
            print 'rn2letter retrieved', sym, formatted_sym
        else:
            formatted_sym = roman2letter(sym)
        return formatted_sym

    def letter(self, sym):
        if is_roman_numeral(sym):
            return self.rn2letter(sym)
        else:
            return sym

    def roman(self, sym):
        if sym is None:
            return None
        if is_roman_numeral(sym):
            return sym
        else:
            return self.letter2rn(sym)

    def letter2rn(self, sym):
        if sym in LETTER2ROMAN_RELABELS:
            formatted_sym = LETTER2ROMAN_RELABELS[sym]
        elif sym in self._letter2rn:
            formatted_sym = self._letter2rn[sym]
            print 'letter2rn retrieved', formatted_sym
        else:
            formatted_sym = letter2roman(sym)
            if formatted_sym is None:
                return ''
            # print 'created', formatted_sym

        return formatted_sym

    def back_to_roman(self, sym):
        is_roman = is_roman_numeral(sym)
        if not is_roman:
            sym = self.letter2rn(sym)
        return sym

    def format_seq(self, seq):
        local_seq = []
        local_original_seq = []
        for sym in seq:
            formatted_sym, valid = self.format_sym(sym)
            if valid:
                local_seq.append(formatted_sym)
            else:
                local_seq.append('')
            local_original_seq.append(formatted_sym)

        return local_seq, local_original_seq

    def format_sym(self, sym):
        is_roman = is_roman_numeral(sym)
        formatted_sym = sym
        # print 'format_sym', sym, is_roman

        if is_roman and self.symbol_type != 'roman':
            formatted_sym = self.rn2letter(sym)
        elif not is_roman and self.symbol_type == 'roman':
            formatted_sym = self.letter2rn(sym)

        if formatted_sym is not None:
            if self.symbol_type == 'roman':
                for k, v in ROMAN_PARTIAL_RELABELS.iteritems():
                    if k in formatted_sym:
                        formatted_sym = formatted_sym.replace(k, v)
            else:
                for k, v in LETTER_PARTIAL_RELABELS_FOR_USER.iteritems():
                    if k in formatted_sym:
                        formatted_sym = formatted_sym.replace(k, v)

        # check = sym2chord(sym)
        # if check is None:
        #     return None
        if formatted_sym == '':
            return sym, False
        return formatted_sym, True

    def generate_subs_from_context(self, sym_ind, original_seq, factor=1):
        print '...generate_subs_from_context', sym_ind, original_seq
        # factor: factor*n_similar # of suggestions
        original_sym = original_seq[sym_ind]
        subs = []
        # sorted_syms = None
        # if 0 < sym_ind < len(original_seq):
        if sym_ind - 1 < 0:
            before_sym = None
        else:
            before_sym = original_seq[sym_ind - 1]
            if before_sym not in self.syms:
                before_sym = None
        if sym_ind + 1 >= len(original_seq):
            after_sym = None
        else:
            after_sym = original_seq[sym_ind + 1]
            if after_sym not in self.syms:
                after_sym = None
        sorted_probs, sorted_syms = \
            simple_foward_backward_gap_dist(self.model, before_sym, after_sym)
        n_subs = factor*self.n_similar
        if sorted_syms is not None:
            subs = sorted_syms[:n_subs]
        if original_sym in subs:
            subs.remove(original_sym)
            subs.append(sorted_syms[n_subs])
        print '...subs', subs
        return sorted_syms, subs

    def make_single_sub_suggestion_items(self, sym_ind, original_seq, subs, sorted_syms, return_tags_only=False):
        # original_sym = original_seq[sym_ind]
        suggestion_items = []
        tags_list = []
        for i, ss in enumerate(subs):
            sub_seq = original_seq[:sym_ind] + [ss] + original_seq[sym_ind + 1:]
            print 'subseq', sub_seq
            tags = {}
            if i < self.n_similar:
                tags['source'] = 'subs'
            else:
                tags['source'] = 'sim'

            if sorted_syms is not None:
                tags['context_rank'] = sorted_syms.index(ss)
            tags_list.append(tags)
            item = SuggestionItem(sub_seq, [sym_ind], 'sub_ripple', tags)
            suggestion_items.append(item)

        # original_sym, valid = self.format_sym(original_sym)
        # if valid:
        #     subs.insert(0, original_sym)
        if return_tags_only:
            return tags_list
        return suggestion_items

    def generate_singleton_subs(self, sym_ind, original_seq, factor=1):
        original_sym = original_seq[sym_ind]
        print '...generate substitutions based on similarity'
        # generate substitutions based on similarity
        sims = self.get_similar_chords(original_sym, self.n_similar)
        print sims
        # generate substitutions based on context
        sorted_syms, subs = self.generate_subs_from_context(sym_ind, original_seq, factor=factor)
        # collect all the single changes
        if subs is None:
            subs = sims
        elif sims is not None:
            subs.extend(sims)
        # subs first, sims next
        return sorted_syms, subs

    def on_generateAlternatives(self, text, pos):
        if self.experiment_type == MANUAL:
            return
        print '--- --- generate_alternative --- ---', text, pos
        print self.symbol_type
        original_seq, raw_original_seq = self.parse_seq_as_syms(text)
        print 'original_seq', original_seq
        if len(original_seq) == 0 or original_seq == ['']:
            self.previous_sym = None
            self.previous_sym_ind = None
            self.clear_suggestions()
            self.emit('updateChordSuggestions', [], [], [])
            self.on_startSeqs()
            return

        original_sym, sym_ind = self.get_sym_at_pos(text, pos, return_ind=True)
        print original_sym, sym_ind

        if sym_ind is None or original_seq[sym_ind] != original_sym:
            print 'WARNING: symbol not matching'
            return
        print 'formatted sym', original_sym, sym_ind
        print 'previous', self.previous_sym, self.previous_sym_ind

        # self.roman(self.previous_sym) == self.roman(original_sym)
        if self.previous_sym is not None and self.previous_sym_ind == sym_ind \
                and self.previous_sym == original_sym:
            return
        else:
            self.previous_sym = original_sym
            self.previous_sym_ind = sym_ind
            self.clear_suggestions()

        if original_sym is None or not len(original_sym):
            print 'WARNING: no symbol at this position'
            # if sym in middle, then use context to ripple in to generate suggestions
            sorted_syms, subs = self.generate_subs_from_context(sym_ind, raw_original_seq, 4)
            if sorted_syms is not None:
                suggestion_items = self.make_single_sub_suggestion_items(sym_ind, raw_original_seq, subs, sorted_syms)
                self.clear_suggestions()
                self.suggestions_above.add(suggestion_items)
                seqs, inds = self.suggestions_above.get_seqs_inds()

                print '...generateAlternatives, # of items', self.suggestions_above.num_items
                self.emit('updateChordSuggestionsAbove', seqs, inds, self.suggestions_above.types)
            return

        # generate nexts
        print '...generate_nexts', raw_original_seq
        print original_seq, original_sym
        if sym_ind == len(original_seq) - 1:
            ss, sinds = self.generate_next(original_sym, sym_ind, raw_original_seq)
            if ss is None:
                print 'WARNING: no next chords for ', original_seq[sym_ind]
                return
            for i, s in enumerate(ss):
                self.suggestions.add(SuggestionItem(s, sinds[i], 'next'))
                print s, sinds[i]

        if self.experiment_type == RIPPLE:
            suggestion_items = self.generate_side_ripples(sym_ind, original_seq)
            self.suggestions.add(suggestion_items)

        sorted_syms, subs = self.generate_singleton_subs(sym_ind, raw_original_seq)
        suggestion_items = self.make_single_sub_suggestion_items(sym_ind, raw_original_seq, subs, sorted_syms)

        if subs is None:
            seqs, inds = self.suggestions_above.get_seqs_inds()
            self.emit('updateChordSuggestionsAbove', seqs, inds)
            return

        # generate ripples for the single changes
        print '...subs', subs
        if self.experiment_type == RIPPLE:
            seq_subs, seq_inds = self.generate_ripples(raw_original_seq, sym_ind, subs)
        else:
            seq_subs = None

        if seq_subs is None:
            # add what we have so far
            self.suggestions_above.add(suggestion_items)
        else:
            # the first one is for the current text
            # check if it is the same as current text
            # same_as_original_seq = True
            # ss = seq_subs.pop(0)
            # inds = seq_inds.pop(0)
            # for s, ind in zip(ss, inds):
            #     if len(original_seq) < ind and original_seq[ind] != s:
            #         same_as_original_seq = False
            # rippled_plus_original_items = []
            # if not same_as_original_seq:
            #     tags = {'source': 'user'}
            #     rippled_plus_original_items.append(SuggestionItem(ss, inds, 'sub_ripple', tags))

            rippled_plus_original_items = []
            assert len(suggestion_items) == len(seq_subs)
            # interleave the two
            print '...interleaving subs and their ripples'
            for i, item in enumerate(suggestion_items):
                rippled_plus_original_items.append(item)
                ripple_item = SuggestionItem(seq_subs[i], seq_inds[i], 'sub_ripple', item.tags)
                rippled_plus_original_items.append(ripple_item)
            self.suggestions_above.add(rippled_plus_original_items)

        # if EXPERIMENT_TYPE != BASELINE_SINGLETON and sym_ind == len(original_seq) - 1:
        #     ss, sinds = self.generate_continuations(original_sym, sym_ind, original_seq)
        #     self.suggestions_above.add_seqs_inds(ss, sinds, 'till_end')

        seqs, inds = self.suggestions.get_seqs_inds()
        print '...generateAlternatives, # of items', self.suggestions.num_items
        self.emit('updateChordSuggestions', seqs, inds, self.suggestions.types)

        seqs, inds = self.suggestions_above.get_seqs_inds()
        print '...generateAlternatives, # of above items', self.suggestions_above.num_items
        print seqs
        print inds
        self.emit('updateChordSuggestionsAbove', seqs, inds, self.suggestions.types)

    def generate_continuations(self, sym, ind, original_seq):
        postfix_len = 4
        seqs = []
        seq_inds = []
        for i in range(2, postfix_len):
            fixed = {ind:sym}
            fixed[ind+i] = 'I'
            seq, inds = \
                shortest_path(self.model, fixed, ind, original_seq)
            seqs.append(seq)
            seq_inds.append(inds)
        return seqs, seq_inds

    def generate_next(self, sym, seq_ind, original_seq):
        trans = self.model.trans
        syms = self.syms
        if sym not in syms:
            return None, None
        sym_ind = syms.index(sym)
        n_conts = self.n_suggestions
        inds = np.argsort(-trans[sym_ind, :])[:n_conts]
        # unformatted_syms = [syms[ind] for ind in inds]
        # print 'unformatted_syms', unformatted_syms
        # formatted_subs = []
        # for ind in inds:
        #     formatted_sub = self.format_sym(syms[ind])
        #     print syms[ind], formatted_sub
        #     if formatted_sub is not None:
        #         formatted_subs.append(ind)
        # print len(inds), len(formatted_subs)
        # subs = [original_seq[:] + [formatted_subs[i]] for i in range(len(inds))]

        subs = [original_seq[:] + [syms[ind]] for ind in inds]
        # print 'generate_next', subs
        return subs, [[seq_ind+1]]*n_conts

    def generate_ripples(self, original_seq, sym_ind, sims, win_max=2):
        print '...generate_ripples', sims
        seq_subs = []
        seq_inds = []
        for win in range(1, win_max):
            ub = sym_ind + win
            lb = sym_ind - win

            # allow one extra seq step
            lb_out_bound = lb < -1
            ub_out_bound = ub > len(original_seq)

            # supposedly already second time out of bound
            if lb_out_bound or ub_out_bound:
                break

            if lb < 0:
                lb = 0
            if ub > len(original_seq):
                ub = len(original_seq)

            for j, s in enumerate(sims):
                fixed = {}
                if ub < len(original_seq):
                    fixed[ub] = original_seq[ub]
                else:
                    fixed[ub] = []

                fixed[lb] = original_seq[lb]
                # may override lb or ub
                fixed[sym_ind] = s
                print fixed

                sub_seq, sym_inds = \
                    shortest_path(self.model, fixed, sym_ind, original_seq)

                seq_subs.append(sub_seq)
                seq_inds.append(list(sym_inds))
        for seq in seq_subs:
            print seq
        return seq_subs, seq_inds

    def generate_side_ripples(self, sym_ind, original_seq, factor=1, win_max=2):
        print '...generate_side_ripples', sym_ind, 'len(original_seq)', len(original_seq)
        # because if factor not equal to one for now will cause the sub, sim attribution to be incorrect
        # even though the context rank will probably reveal which one it is, and is more important anyway
        assert factor == 1
        # original_sym = original_seq[sym_ind]
        # left side
        left_subs = None
        if sym_ind > 0:
            left_ind = sym_ind - 1
            left_sorted_syms, left_subs = self.generate_singleton_subs(left_ind, original_seq, factor)

        # right side
        right_subs = None
        if sym_ind < len(original_seq) - 1:
            right_ind = sym_ind + 1
            right_sorted_syms, right_subs = self.generate_singleton_subs(right_ind, original_seq, factor)

        if left_subs is None and right_subs is None:
            print 'no side ripple yet'
            return []

        print 'left subs', left_subs
        print 'right subs', right_subs
        seqs = []
        inds = []

        n = 0
        # choose the smaller non-zero one
        if left_subs is None:
            left_n = 0
        else:
            left_n = len(left_subs)
            n = left_n
        if right_subs is None:
            right_n = 0
        else:
            right_n = len(right_subs)
            n = right_n

        if right_n > left_n > 0:
            n = left_n

        print left_n, right_n, n
        for i in range(n):
            if left_subs is not None and right_subs is not None:
                seq = original_seq[:left_ind] + [left_subs[i]] + \
                    [original_seq[sym_ind]] + [right_subs[i]]
                if right_ind + 1 < len(original_seq):
                    seq += original_seq[right_ind+1:]
                inds.append([left_ind, right_ind])

            elif left_subs is None:
                seq = original_seq[:right_ind] + [right_subs[i]]
                if right_ind + 1 < len(original_seq):
                    seq += original_seq[right_ind+1:]
                inds.append([right_ind])

            elif right_subs is None:
                seq = original_seq[:left_ind] + [left_subs[i]] + \
                    original_seq[sym_ind:]
                inds.append([left_ind])
            else:
                assert False, 'ERROR: case not considered'

            seqs.append(seq)

        tags = None
        if left_subs is not None:
            tags = self.make_single_sub_suggestion_items(left_ind, original_seq,
                                                         left_subs, left_sorted_syms,
                                                         return_tags_only=True)
        right_tags = None
        if right_subs is not None:
            right_tags = self.make_single_sub_suggestion_items(right_ind, original_seq,
                                                               right_subs, right_sorted_syms,
                                                               return_tags_only=True)

        # merge the tags
        if tags is not None and right_tags is not None:
            for i in range(n):
                key = 'context_rank'
                tag = tags[i]
                other_tag = right_tags[i]
                if key in tags and key in other_tag:
                    tag[key] = [tag[key], other_tag[key]]
                elif key in other_tag:
                    tag[key] = [other_tag[key]]
                print 'tags'
        elif tags is None:
            tags = right_tags

        suggestion_items = []
        for i, seq in enumerate(seqs):
            print 'seq:', seq
            print 'inds', inds[i]
            print tags[i]
            item = SuggestionItem(seq, inds[i], 'side_ripple', tags[i])
            suggestion_items.append(item)

        return suggestion_items

    # TODO: more side options
    def generate_sides(self, original_seq, sym_ind, sims, win_max=2):
        seq_subs = []
        seq_inds = []
        for win in range(1, win_max):
            ub = sym_ind + win
            lb = sym_ind - win

            # allow one extra seq step
            lb_out_bound = lb < -1
            ub_out_bound = ub > len(original_seq)

            # supposedly already second time out of bound
            if lb_out_bound or ub_out_bound:
                break

            if lb < 0:
                lb = 0
            if ub > len(original_seq):
                ub = len(original_seq)

            for j, s in enumerate(sims):
                fixed = {}
                if ub < len(original_seq):
                    fixed[ub] = original_seq[ub]
                else:
                    fixed[ub] = []

                fixed[lb] = original_seq[lb]
                # may override lb or ub
                fixed[sym_ind] = s
                print fixed

                sub_seq, sym_inds = \
                    shortest_path(self.model, fixed, sym_ind, original_seq)

                seq_subs.append(sub_seq)
                seq_inds.append(list(sym_inds))
        return seq_subs, seq_inds

    def generate_more_change_alternatives(self, text, pos):
        sym, sym_ind = self.get_sym_at_pos(text, pos, return_ind=True)
        if sym is None:
            return
        original_seq, raw_original_seq = self.parse_seq_as_syms(text)
        win_max = 2

        sims = self.get_similar_chords(sym, 3)
        sims.insert(0, sym)
        if sims is None:
            return
        return self.generate_ripples(raw_original_seq, sym_ind, sims, win_max)

    def clear_suggestions(self):
        self.previous_sym = None
        self.previous_sym_ind = None
        self.suggestions.clear()
        self.suggestions_above.clear()
        self.emit('updateChordSuggestions', [], [], [])
        self.emit('updateChordSuggestionsAbove', [], [], [])
        return

    def retrieve_suggestion_item_at(self, ind, text, suggestPanel):
        if 'above' in suggestPanel:
            item = self.suggestions_above.retrieve_item_at(ind, text.strip())
        else:
            item = self.suggestions.retrieve_item_at(ind, text.strip())
        return item

    def on_textChange(self, text, pos, kind, author, ind, suggestPanel=None, play=True):
        print '--- on_textChange ---', text, pos, author, play, ind, suggestPanel
        if len(text.strip()) == 0:
            return
        tags = {}
        if kind == 'use' and author == 'machine':
            assert suggestPanel is not None
            item = self.retrieve_suggestion_item_at(ind, text, suggestPanel)
            tags['suggestion_item'] = item
            tags['suggestion_list'] = deepcopy(self.suggestions.items)
        elif kind == 'use' and author == 'user':
            self.clear_suggestions()

        tags['author'] = author
        self.logs.add(kind, text, tags)
        self.history.append(text)
        if text is None or not isinstance(text, unicode):
            print "WARNING: chord symbol not valid", text
            return
        sym, sym_ind = self.get_sym_before_pos(text, pos, return_ind=True)
        print 'sym to play?', sym, sym_ind, play
        note_seqs = self.parse_seq_as_notes(text)
        print 'len(note_seqs)', len(note_seqs)
        notes = note_seqs[sym_ind]
        # print 'notes', notes
        if play:
            self.emit("playNotes", notes)

        if len(notes) > 0:
            text = self.remove_extra_spacing(text)
            text_previous = ''
            if len(self.history) > 1:
                text_previous = self.remove_extra_spacing(self.history[-2])
            self.emit("updateHistory", text_previous, text, )

            self.on_generateAlternatives(text, pos)

    @staticmethod
    def remove_extra_spacing(text):
        parts = text.strip().split(' ')
        non_space_parts = []
        for part in parts:
            non_space_part = part.strip()
            if len(non_space_part) > 0:
                non_space_parts.append(non_space_part)
        return ' '.join(non_space_parts)

    # get the symbol index when given the left side pos of the sym
    def get_sym_ind_from_left(self, text, left):
        count_syms_before = 0
        p_is_space = False
        for i in range(left+1):
            if text[i] == ' ' and i and not p_is_space:
                count_syms_before += 1
                p_is_space = True
            elif text[i] != ' ':
                p_is_space = False
        if not left:
            count_syms_before = 0
        return count_syms_before

    # for alternatives, get symbol that's currently being edited
    def get_sym_at_pos(self, text, pos, return_ind=False):
        # for alternatives, get symbol that's currently being edited
        # symbol that is to the left of the cursor
        if len(text) == 0:
            return None, None
        right = pos-1
        if right < 0:
            right = 0
        right_iter_start = right
        if len(text)-1 <= right_iter_start:
            right_iter_start = len(text)-1
        for i in range(right_iter_start, len(text)):
            if text[i] == ' ':
                right = i
                break
        if pos > 0:
            left = pos-2
            if left < 0:
                left = 0
        else:
            left = 0
        left_iter_start = pos - 2
        if len(text)-1 <= left_iter_start:
            left_iter_start = len(text) - 1
        for i in range(left_iter_start, -1, -1):
            if text[i] == ' ':
                left = i + 1
                break
        # TODO: hacky
        if i == 0:
            left = 0

        sym_ind = self.get_sym_ind_from_left(text, left)

        sym = text[left:right+1]
        sym = sym.strip()
        if len(sym) == 0:
            return None, None
        if self.symbol_type == 'roman':
            sym = self.back_to_roman(sym)
        if return_ind is True:
            return sym, sym_ind
        else:
            return sym

    # for playback, gets symbol before the space
    def get_sym_before_pos(self, text, pos, return_ind=False):
        # minus 1 because the caret pos is to right of a space
        right = pos - 1
        print 'right', right,
        right_char_ind = right
        for i in range(right, -1, -1):
            if text[i] != ' ':
                right_char_ind = i
                break
        right = right_char_ind
        print right
        # find the left boundary
        left = 0
        for i in range(right, -1, -1):
            if text[i] == ' ':
                left = i
                break
        print 'left', left
        sym_ind = self.get_sym_ind_from_left(text, left)
        print 'sym_ind', sym_ind
        sym = text[left:right+1]
        sym = sym.strip()
        # sym = self.back_to_roman(sym)

        if return_ind is True:
            return sym, sym_ind
        else:
            return sym