示例#1
0
def main():
    parser = argparse.ArgumentParser(description="Reorder MIDI tracks.")
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('midi_out_file', help='midi filename to export')
    parser.add_argument('track_order',
                        type=int,
                        nargs='*',
                        help='new order (first track is track 1)')

    args = parser.parse_args()

    if not (args.midi_in_file.lower().endswith(r'.mid')
            or args.midi_in_file.lower().endswith(r'.midi')):
        parser.error(r'Expecting input filename that ends in ".mid"')
    if not os.path.exists(args.midi_in_file):
        parser.error('Cannot find "%s"' % args.midi_in_file)

    song = midi.MIDI().to_chirp(args.midi_in_file)

    if max(args.track_order) > len(song.tracks):
        raise ChiptuneSAKValueError(
            "Illegal track specified: only %d tracks in song" %
            len(song.tracks))

    old_tracks = copy.deepcopy(song.tracks)

    new_tracks = [old_tracks[it - 1] for it in args.track_order]
    song.tracks = new_tracks

    print("New track order:")
    print("\n".join(t.name for t in song.tracks))

    print("Writing to midi file %s" % args.midi_out_file)
    midi.MIDI().to_file(song, args.midi_out_file)
示例#2
0
def main():
    parser = argparse.ArgumentParser(
        description="Convert song into Lilypond score")
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('out_folder', help='output folder')
    parser.add_argument('-a',
                        '--autosort',
                        action="store_true",
                        help='automatically sort staves by average note')

    args = parser.parse_args()

    lp = Lilypond()

    if args.autosort:
        lp.set_options(autosort="True")

    print("Reading %s" % args.midi_in_file)
    chirp_song = midi.MIDI().to_chirp(args.midi_in_file,
                                      quantization='auto',
                                      polyphony=False)

    print('Converting to measures...')
    mchirp_song = chirp_song.to_mchirp()
    print('Generating lilypond...')
    ly_name = args.midi_in_file.replace('.mid', '.ly')
    lp.to_file(mchirp_song, ly_name)

    subprocess.call('lilypond -o %s %s' % (args.out_folder, ly_name),
                    shell=True)

    print("\ndone")
示例#3
0
def main():
    parser = argparse.ArgumentParser(description="Explode MIDI track.")
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('midi_out_file', help='midi filename to export')
    track_group = parser.add_mutually_exclusive_group(required=False)
    track_group.add_argument('-n',
                             '--tracknumber',
                             type=int,
                             help='track number (first track is 1)')
    track_group.add_argument('-t', '--trackname', type=str, help='track name')

    args = parser.parse_args()

    if not (args.midi_in_file.lower().endswith(r'.mid')
            or args.midi_in_file.lower().endswith(r'.midi')):
        parser.error(r'Expecting input filename that ends in ".mid"')
    if not os.path.exists(args.midi_in_file):
        parser.error('Cannot find "%s"' % args.midi_in_file)

    song = midi.MIDI().to_chirp(args.midi_in_file)

    if args.tracknumber:
        print("Exploding track number %d" % args.tracknumber)
        song.explode_polyphony(args.tracknumber - 1)
    elif args.trackname:
        it = next(
            (it
             for it, t in enumerate(song.tracks) if t.name == args.trackname),
            None)
        if it is None:
            print("Track %s not found" % args.trackname)
            exit(1)
        else:
            print("Exploding track %s" % song.tracks[it].name)
            song.explode_polyphony(it)
    else:
        print("No track specified")
        exit(1)

    midi.MIDI().to_file(song, args.midi_out_file)
示例#4
0
def main():
    parser = argparse.ArgumentParser(description="Convert a GoatTracker2 sng file to a midi file.")
    parser.add_argument('sng_in_file', help='sng filename to import')
    parser.add_argument('midi_out_file', help='midi filename to export')
    parser.add_argument(
        '-s', '--subtune_number', type=int, default=0, help='subtune number (default: 0)'
    )

    args = parser.parse_args()

    rchirp_song = goat_tracker.GoatTracker().to_rchirp(args.sng_in_file, subtune=args.subtune_number)

    """
    cvs_filename = '%s.csv' % (args.sng_in_file.split('.')[0])
    with open(cvs_filename, 'w') as out_file:
        out_file.write(rchirp_song.note_time_data_str())
    """

    chirp_song = rchirp_song.to_chirp()

    # TODO:  Allow time signature to be set here?
    midi.MIDI().to_file(chirp_song, args.midi_out_file)

    print("\ndone")
示例#5
0
def main():
    parser = argparse.ArgumentParser(
        description="Perform transformations on MIDI files.",
        epilog="Operations are performed in the order given in this help."
    )
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('midi_out_file', help='midi filename to export')
    parser.add_argument('-n', '--name', type=str, help='set name of song')
    parser.add_argument('-z', '--removekeyswitchnotes', action="store_true", help='remove keyswitch notes')
    parser.add_argument('-s', '--scaleticks', type=float, help='scale ticks')
    parser.add_argument('-x', '--moveticks', type=str, help='move ticks lXXXX for left and rXXXX for right')
    parser.add_argument('-p', '--ppq', type=int, help='set ppq')
    parser.add_argument('-m', '--modulate', type=str, help='modulate by n/d')
    parser.add_argument('-t', '--transpose', type=str,
                        help='transpose by semitones (uXX or dXX for up or down XX semitones)')
    quant_group = parser.add_mutually_exclusive_group(required=False)
    quant_group.add_argument('-a', '--quantizeauto', action="store_true", help='Auto-quantize')
    quant_group.add_argument('-q', '--quantizenote', type=str, help='quantize to a note value')
    quant_group.add_argument('-c', '--quantizeticks', type=int, help='quantize to ticks')
    parser.add_argument('-r', '--removepolyphony', action="store_true", help='remove polyphony')
    parser.add_argument('-b', '--qpm', type=int, help='set qpm')
    parser.add_argument('-j', '--timesignature', type=str, help='set time signature e.g. 3/4')
    parser.add_argument('-k', '--keysignature', type=str, help='set key signature, e.g. D, F#m')

    args = parser.parse_args()

    if not (args.midi_in_file.lower().endswith(r'.mid') or args.midi_in_file.lower().endswith(r'.midi')):
        parser.error(r'Expecting input filename that ends in ".mid"')
    if not path.exists(args.midi_in_file):
        parser.error('Cannot find "%s"' % args.midi_in_file)

    song = midi.MIDI().to_chirp(args.midi_in_file)

    # Print stats
    print('%d notes' % (sum(len(t.notes) for t in song.tracks)))
    print('PPQ = %d' % (song.metadata.ppq))
    q_state = "" if song.is_quantized() else "not"
    p_state = "" if song.is_polyphonic() else "not"
    print("Input midi is %s quantized and is %s polyphonic" % (q_state, p_state))

    if args.name:
        print("Renaming song to %s" % args.name)
        song.metadata.name = args.name

    if args.removekeyswitchnotes:
        print("Removing control notes...")
        song.remove_keyswitches()

    if args.scaleticks:
        print("Scaling by %lf" % args.scaleticks)
        song.scale_ticks(args.scaleticks)

    if args.moveticks:
        move = args.moveticks
        if move[0] == 'l':
            v = -int(move[1:])
        elif move[0].isdigit():
            v = int(move)
        else:
            v = int(move[1:])
        print("Moving by %d" % v)
        song.move_ticks(v)

    if args.ppq:
        print("setting ppq to %d" % args.ppq)
        song.metadata.ppq = args.ppq

    if args.modulate:
        num, denom = (int(n) for n in args.modulate.split('/'))
        print("modulating by %d/%d" % (num, denom))
        song.modulate(num, denom)

    if args.transpose:
        if args.transpose[0] == 'd':
            transpose = -int(args.transpose[1:])
        else:
            transpose = int(args.transpose[1:])
        print("transposing by %d" % transpose)
        song.transpose(transpose)

    if args.quantizenote:
        print("Quantizing...")
        print("to note value %s" % args.quantizenote)
        song.quantize_from_note_name(args.quantizenote)
    elif args.quantizeticks:
        print("Quantizing...")
        print('to %d ticks' % args.quantizeticks)
        song.quantize(args.quantizeticks, args.quantizeticks)
    elif args.quantizeauto:
        print("Quantizing...")
        qticks_n, qticks_d = song.estimate_quantization()
        print('to estimated quantization: %d, %d  ticks' % (qticks_n, qticks_d))
        song.quantize(qticks_n, qticks_d)

    if args.removepolyphony:
        print("Eliminating polyphony...")
        song.remove_polyphony()

    if args.qpm:
        print("Setting qpm to %d" % args.qpm)
        song.set_qpm(args.qpm)

    if args.timesignature:
        num, denom = (int(x) for x in args.timesignature.split('/'))
        if num > 0 and denom > 0:
            print('Setting time signature to %d/%d' % (num, denom))
            song.set_time_signature(num, denom)

    if args.keysignature:
        print('Setting key signature to %s' % args.keysignature)
        song.set_key_signature(args.keysignature)

    q_state = "" if song.is_quantized() else "not"
    p_state = "" if song.is_polyphonic() else "not"
    print("Output ChirpSong is %s quantized and %s polyphonic" % (q_state, p_state))

    print("Exporting to MIDI...")

    midi.MIDI().to_file(song, args.midi_out_file)
示例#6
0
def main():
    parser = argparse.ArgumentParser(
        description="Convert a midi file into a C128 BASIC program.")
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('basic_out_file', help='filename to export')
    parser.add_argument('-t',
                        '--type',
                        choices=['bas', 'prg'],
                        help='basic output file type (default: prg)')
    parser.add_argument('-i',
                        '--instruments',
                        nargs=3,
                        help="instrument names (3 required)")
    parser.add_argument('-a',
                        '--arch',
                        default=constants.DEFAULT_ARCH,
                        help="architecture (NTSC or PAL)")

    args = parser.parse_args()

    if not (args.midi_in_file.lower().endswith(r'.mid')
            or args.midi_in_file.lower().endswith(r'.midi')):
        parser.error(r'Expecting input filename that ends in ".mid"')
    if not os.path.exists(args.midi_in_file):
        parser.error('Cannot find "%s"' % args.midi_in_file)

    # midi -> mchirp
    song = midi.MIDI().to_chirp(args.midi_in_file)

    song.remove_keyswitches(8)
    song.quantize_from_note_name('16')
    # transformMidi.py -q 32 -k Am ..\test\BWV_799.mid ..\test\BWV_799_q.mid
    # song.quantize_from_note_name('32')
    song.remove_polyphony()

    c128_basic.trim_note_lengths(song)

    if len(song.metadata.name) == 0:
        song.metadata.name = args.midi_in_file.split(os.sep)[-1].lower()

    # chirp -> mchirp
    mchirp_song = song.to_mchirp()

    # mchirp -> C128 Basic

    instruments = ('piano', 'piano', 'piano')
    if args.instruments:
        instruments = (i.lower() for i in args.instruments)

    # if -t flag not used, look at output filename extension
    if args.type is None:
        if args.basic_out_file.lower().endswith('.bas'):
            args.type = 'bas'
        else:
            args.type = 'prg'

    basic_converter = c128_basic.C128Basic()
    basic_converter.set_options(arch=args.arch,
                                format=args.type,
                                instruments=instruments)
    basic_converter.to_file(mchirp_song, args.basic_out_file)

    print("\ndone")
示例#7
0
def main():
    parser = argparse.ArgumentParser(
        description="Fit best PPQ value for MIDI files.")
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('midi_out_file', help='midi filename to export')
    parser.add_argument('-p',
                        '--ppq',
                        type=int,
                        default=DEFAULT_MIDI_PPQN,
                        nargs='?',
                        help='preferred PPQ (default = DEFAULT_MIDI_PPQN)')
    parser.add_argument('-m',
                        '--minnote',
                        type=str,
                        default='16',
                        nargs='?',
                        help='minimum interval name (default = 16)')
    parser.add_argument('-s',
                        '--scalefactor',
                        type=float,
                        help='estimated scale factor')
    parser.add_argument('-o',
                        '--offset',
                        type=int,
                        help='estimated offset in original ticks')

    args = parser.parse_args()

    desired_ppq = args.ppq
    desired_q = desired_ppq * DURATION_STR[args.minnote]

    print("Reading file %s" % args.midi_in_file)
    song = midi.MIDI().to_chirp(args.midi_in_file)
    notes = [n for t in song.tracks for n in t.notes]
    f_min = round(desired_ppq / song.metadata.ppq / 2, 3)
    f_max = f_min * 8.
    if args.scalefactor:
        f_min = args.scalefactor * .9
        f_max = args.scalefactor * 1.1
    else:
        if f_min < 1.:
            f_min = 1.
        if f_max > 12.:
            f_max = 12.
    f_step = .01
    offset_est = min(n.start_time for n in notes)
    if args.offset:
        offset_est = args.offset
    last_min_e = 1.e9

    get_best_f = functools.partial(find_best_f, notes, desired_q)
    get_best_offset = functools.partial(find_best_offset, notes, desired_q,
                                        offset_est - 20, offset_est + 20)

    print('Finding initial parameters...')
    # Do wide-range search for best scale factor and offset.
    best_f, min_e = get_best_f(f_min, f_max, f_step, offset_est)
    best_offset, min_e = get_best_offset(best_f)

    # Now refine the scale factor and offset iteratively until they converge
    print('Refining...')
    while min_e < last_min_e:
        last_min_e = min_e
        f_step /= 10.
        f_min = best_f - (f_step * 200)
        if (f_min < 1.0):
            f_min = 1.0
        f_max = f_min + (f_step * 200)
        best_f, min_e = get_best_f(f_min, f_max, f_step, best_offset)
        best_offset, min_e = get_best_offset(best_f)

    # Average error in new ticks
    tick_error = min_e / len(notes) * best_f
    print(
        "scale_factor = %.7lf, offset = %d, total error = %.1lf ticks (%.2lf ticks/note for ppq = %d)"
        % (best_f, best_offset, min_e, tick_error, desired_ppq))

    song.move_ticks(-best_offset)
    song.scale_ticks(best_f)
    song.metadata.ppq = desired_ppq
    # song.quantize_from_note_name('16')
    print("Writing file %s" % args.midi_out_file)
    midi.MIDI().to_file(song, args.midi_out_file)

    print("\ndone")
示例#8
0
def main():
    parser = argparse.ArgumentParser(
        description="Perform operations on individual tracks of MIDI files.",
        epilog="Operations are performed in the order given in this help.")
    parser.add_argument('midi_in_file', help='midi filename to import')
    parser.add_argument('midi_out_file', help='midi filename to export')
    parser.add_argument('track',
                        type=str,
                        help='Track either number (starting with 1) or name')
    parser.add_argument('-n', '--name', type=str, help='Set track name')
    quant_group = parser.add_mutually_exclusive_group(required=False)
    quant_group.add_argument('-a',
                             '--quantizeauto',
                             action="store_true",
                             help='Auto-quantize')
    quant_group.add_argument('-q',
                             '--quantizenote',
                             type=str,
                             help='quantize to a note value')
    quant_group.add_argument('-c',
                             '--quantizeticks',
                             type=int,
                             help='quantize to ticks')
    quant_group.add_argument('-d',
                             '--quantizelongticks',
                             type=int,
                             help='quantize long notes to ticks')
    parser.add_argument('-m',
                        '--merge',
                        type=int,
                        help='merge track (max num ticks to merge)')
    short_note_group = parser.add_mutually_exclusive_group(required=False)
    short_note_group.add_argument('-r',
                                  '--removeshort',
                                  type=int,
                                  help='remove notes <= tick value')
    short_note_group.add_argument(
        '-l',
        '--setminnotelen',
        type=int,
        help='set the minimum note length to tick value,'
        ' possibly consuming portion of '
        'following note')

    args = parser.parse_args()

    if not (args.midi_in_file.lower().endswith(r'.mid')
            or args.midi_in_file.lower().endswith(r'.midi')):
        parser.error('Expecting input filename that ends in ".mid"')
    if not path.exists(args.midi_in_file):
        parser.error('Cannot find "%s"' % args.midi_in_file)

    song = midi.MIDI().to_chirp(args.midi_in_file)

    track = None
    if all(t.isdigit() for t in args.track):
        track = song.tracks[int(args.track) - 1]
        print("Editing track %d" % int(args.track))
    else:
        for t in song.tracks:
            if t.name == args.track:
                track = t
        if track is None:
            print('Track %s not found. Available tracks:')
            for t in song.tracks:
                print(t.name)
            parser.error('No track named %s found.' % args.track)

    if args.name:
        track.name = args.name

    if args.quantizelongticks:
        print("Quantizing long notes to %d" % args.quantizelongticks)
        track.quantize_long(args.quantizelongticks)

    if args.removeshort:
        print("Removing notes under %d ticks" % args.removeshort)
        track.remove_short_notes(args.removeshort)

    if args.setminnotelen:
        print("Setting minimum note length to %d" % args.setminnotelen)
        track.set_min_note_len(args.setminnotelen)

    if args.merge:
        print("Merging notes under %d" % args.merge)
        track.merge_notes(args.merge)

    print("Exporting to MIDI...")
    midi.MIDI().to_file(song, args.midi_out_file)