Пример #1
0
def test_subsongs():
    mod = load_file(TEST_PATH / 'beast2-ingame-st.mod')
    subsongs = list(linearize_subsongs(mod, 1))
    orders = [o for (o, _) in subsongs]
    assert len(orders) == 6
    assert orders[0] == [0, 1, 2, 3, 1, 2, 3]
    assert orders[2] == [14]

    mod = load_file(TEST_PATH / 'satanic.mod')
    subsongs = list(linearize_subsongs(mod, 1))
    orders = [o for (o, _) in subsongs]
    assert len(orders) == 114

    mod = load_file(TEST_PATH / 'entity.mod')
    subsongs = list(linearize_subsongs(mod, 1))
    assert len(subsongs) == 2
Пример #2
0
def main():
    args = docopt(__doc__, version='MIDI file generator 1.0')
    SP.enabled = args['--verbose']

    # Parse
    mod_file = args['<mod>']
    programs = parse_programs(args['--programs'])

    mod_file = Path(mod_file)
    midi_mapping = args['--midi-mapping']
    if midi_mapping != 'auto':
        with open(midi_mapping, 'r') as f:
            midi_mapping = load(f)
        midi_mapping = {int(k): v for (k, v) in midi_mapping.items()}

    mod = load_file(mod_file)
    subsongs = linearize_subsongs(mod, 1)
    volumes = [header.volume for header in mod.sample_headers]
    for idx, (_, rows) in enumerate(subsongs):
        notes = rows_to_mod_notes(rows, volumes)
        if midi_mapping == 'auto':
            props = sample_props(mod, notes)
            samples = [(sample_idx, props.is_percussive, props.note_duration)
                       for (sample_idx, props) in props.items()]
            midi_mapping = assign_instruments(samples, programs)
        midi_file = 'test-%02d.mid' % idx
        notes_to_midi_file(notes, midi_file, midi_mapping)
Пример #3
0
def percussive_samples(mod):
    subsongs = linearize_subsongs(mod, 1)
    rows = flatten(r for (_, r) in subsongs)
    volumes = [header.volume for header in mod.sample_headers]
    notes = list(rows_to_mod_notes(rows, volumes))
    return {sample for (sample, p) in sample_props(mod, notes).items()
            if p.is_percussive}
Пример #4
0
def mod_file_to_codes_w_progress(i, n, file_path, code_type):
    SP.header('[ %4d / %4d ] PARSING %s' % (i, n, file_path))
    try:
        mod = load_file(file_path)
    except UnsupportedModule as e:
        SP.print('Unsupported module format.')
        SP.leave()
        err_arg = e.args[0] if e.args else e.__class__.__name__
        yield False, 0, (ERR_PARSE_ERROR, err_arg)
        return

    code_mod = CODE_MODULES[code_type]
    subsongs = list(linearize_subsongs(mod, 1))
    volumes = [header.volume for header in mod.sample_headers]
    parsed_subsongs = []
    for idx, (order, rows) in enumerate(subsongs):
        SP.header('SUBSONG %d' % idx)
        notes = rows_to_mod_notes(rows, volumes)
        percussion = guess_percussive_instruments(mod, notes)
        if notes:
            fmt = '%d rows, %d ms/row, percussion %s, %d notes'
            args = (len(rows), notes[0].time_ms,
                    set(percussion), len(notes))
            SP.print(fmt % args)

        err = training_error(notes, percussion)
        if err:
            yield False, idx, err
        else:
            pitches = {n.pitch_idx for n in notes
                       if n.sample_idx not in percussion}
            min_pitch = min(pitches, default = 0)

            # Subtract min pitch
            for n in notes:
                n.pitch_idx -= min_pitch
            code = list(code_mod.to_code(notes, percussion))
            if code_mod.is_transposable():
                codes = code_mod.code_transpositions(code)
            else:
                codes = [code]
            fmt = '%d transpositions of length %d'
            SP.print(fmt % (len(codes), len(code)))
            yield True, idx, codes
        SP.leave()
    SP.leave()
Пример #5
0
def main():
    args = docopt(__doc__, version='MIDI file generator 1.0')
    SP.enabled = args['--verbose']
    file_path = args['<mod>']

    mod = load_file(file_path)
    rows = list(linearize_subsongs(mod, 1))[0][1]
    n_rows = len(rows)
    sample_headers = mod.sample_headers
    volumes = [header.volume for header in mod.sample_headers]
    notes = rows_to_mod_notes(rows, volumes)
    props = sample_props(mod, notes)
    mel_notes = {n for n in notes if not props[n.sample_idx].is_percussive}
    perc_notes = {n for n in notes if props[n.sample_idx].is_percussive}
    pitches = {n.pitch_idx for n in mel_notes}
    n_unique_mel_notes = len(pitches)
    pitch_range = max(pitches) - min(pitches)
    header = [
        '#', 'MC freq', 'Notes', 'Uniq', 'PCs', 'Longest rep', 'Size', 'Dur',
        'Repeat pct', 'Max ringout', 'Perc?'
    ]
    row_fmt = [
        '%2d', '%.2f', '%3d', '%2d', '%2d', '%3d', '%5d', '%2d', '%.2f',
        '%.2f', lambda x: 'T' if x else 'F'
    ]

    # Make a table
    rows = [(sample, ) + p for (sample, p) in props.items()]
    print_term_table(row_fmt, rows, header, 'rrrrrrrrrrc')

    n_chords, n_diss_chords = dissonant_chords(mel_notes)
    diss_frac = n_diss_chords / n_chords if n_chords else 0.0

    header = ['Item', 'Value']
    rows = [['Rows', n_rows], ['Melodic notes',
                               len(mel_notes)],
            ['Percussive notes', len(perc_notes)],
            ['Unique melodic notes', n_unique_mel_notes],
            ['Pitch range', pitch_range], ['Chords', n_chords],
            ['Chord dissonance', '%.2f' % diss_frac]]
    print_term_table(['%s', '%s'], rows, ['Key', 'Value'], 'lr')
Пример #6
0
def convert_to_midi(code_type, mod_file):
    code_mod = CODE_MODULES[code_type]
    mod = load_file(mod_file)
    subsongs = linearize_subsongs(mod, 1)
    volumes = [header.volume for header in mod.sample_headers]
    for idx, (_, rows) in enumerate(subsongs):
        notes = rows_to_mod_notes(rows, volumes)
        percussion = guess_percussive_instruments(mod, notes)
        pitches = {n.pitch_idx for n in notes
                   if n.sample_idx not in percussion}
        min_pitch = min(pitches, default = 0)
        for n in notes:
            n.pitch_idx -= min_pitch
        code = list(code_mod.to_code(notes, percussion))

        fmt = '%d notes, %d rows, %d tokens, %d ms/row, percussion %s'
        args = (len(notes), len(rows), len(code),
                notes[0].time_ms if notes else - 1, set(percussion))
        SP.print(fmt % args)

        row_time = code_mod.estimate_row_time(code)
        notes = code_mod.to_notes(code, row_time)
        fname = Path('test-%02d.mid' % idx)
        notes_to_audio_file(notes, fname, CODE_MIDI_MAPPING, False)
Пример #7
0
def test_pattern_jump():
    mod = load_file(TEST_PATH / 'wax-rmx.mod')
    subsongs = list(linearize_subsongs(mod, 1))
    assert len(subsongs) == 1
    assert len(subsongs[0][1]) == 2640