def load_roms(self): binary = self.load_rom(constants.project_to_absolute_path('res/c64kernal.bin'), 8192) if binary is not None: self.rom_kernal = binary self.has_kernal = True binary = self.load_rom(constants.project_to_absolute_path('res/c64basic.bin'), 8192) if binary is not None: self.rom_basic = binary self.has_basic = True binary = self.load_rom(constants.project_to_absolute_path('res/c64char.bin'), 4096) if binary is not None: self.rom_char = binary self.has_char = True
def create_output_files(write_csv=True, write_midi=True): for entry in to_extract: (subtune, desc, seconds, starting_key, time_sig_top, time_sig_bottom) = entry sid = chiptunesak.SID() sid.set_options( sid_in_filename=sid_filename, subtune=subtune, tuning=448.93, # above code figured this out vibrato_cents_margin=10, seconds=seconds, gcf_row_reduce=True, ) filename_no_ext = 'examples/data/motl/motl_%s' % desc.replace(" ", "_") sid_dump = sid.capture() # noqa: F841 if write_csv: print("writing %s.csv" % filename_no_ext) sid.to_csv_file( project_to_absolute_path('%s.csv' % filename_no_ext)) if write_midi: print("writing %s.mid" % filename_no_ext) rchirp_song = sid.to_rchirp(sid_filename) play_calls_per_quarter = 32 # can see this in the csv output # milliframes_per_quarter will determine the QPM/BPM chirp_song = \ rchirp_song.to_chirp(milliframes_per_quarter=play_calls_per_quarter * 1000) chirp_song.set_key_signature(starting_key) chirp_song.set_time_signature(time_sig_top, time_sig_bottom) chiptunesak.MIDI().to_file( chirp_song, project_to_absolute_path('%s.mid' % filename_no_ext))
def manage_resources(resources): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0' } for resource in resources: local_path = project_to_absolute_path(resource.local_path) # pathlib would have been better (can build full dir paths), but didn't work for me # pathlib.Path(local_path).parent.mkdir(parents=True, exist_ok=True) # TODO: this approach assumes parent dir exists. Make a nested directory creator routine # and use it here. os.makedirs(local_path, exist_ok=True) local_file = os.path.normpath( os.path.join(local_path, resource.local_name)) if os.path.exists(local_file): if not os.path.isfile(local_file): raise Exception('Error: not expecting "%s" to not be a file' % (local_file)) if SKIP_IF_EXISTS: print('"%s" exists, skipping' % (local_file)) continue else: print('Will overwrite "%s"' % (local_file)) print("%s -> %s" % (resource.remote_url, local_file)) try: response = requests.get(resource.remote_url, headers=headers) except requests.exceptions.RequestException as e: print('Unable to download file at "%s", due to exception "%s"' % (resource.remote_url, e)) continue content = response.content if b'<html' in content.lower()[0:200]: print( 'Warning: "%s" might merely be HTML saying the equivalent of "no botz allowed"' % (local_file)) with open(local_file, 'wb') as out_file: out_file.write(content) time.sleep(uniform(1.5, 2.8)) # be friendly to web sites print()
# Script to make sid header histograms for all the sids in an HVSC zip file import operator import zipfile import itertools from chiptunesak.constants import project_to_absolute_path from chiptunesak import sid # Assumes that the file is not double zipped HVSC_LOG = project_to_absolute_path('res/HVSC72.zip') histograms_categories = [ 'magic_id', 'version', 'data_offset', 'load_address', 'init_address', 'play_address', 'num_subtunes', 'start_song', 'speed', 'compute sid player', 'flag_1', 'clock', 'sid_model', 'sid2_model', 'sid3_model', 'start_page', 'page_length', 'sid2_address', 'sid3_address', 'sid_count', 'init_sets_irq', 'init_no_irq', 'contains_basic' ] histograms = {category: {} for category in histograms_categories} def update_hist(category, value): global histograms if value not in histograms[category]: histograms[category][value] = 0 histograms[category][value] += 1 with zipfile.ZipFile(HVSC_LOG, 'r') as hvsc_zip: sid_files = [
import chiptunesak from chiptunesak.constants import project_to_absolute_path """ This minimal example imports a GoatTracker song and exports as MIDI. """ # Set up input and output paths output_folder = str(project_to_absolute_path('examples\\data\\BWV_147')) + '\\' input_folder = output_folder input_file = str(project_to_absolute_path(input_folder + 'BWV_147_Bleibet.sng')) output_midi_file = str(project_to_absolute_path(output_folder + 'BWV_147_Bleibet.mid')) # Read in the song using the GoatTracker I/O class print(f'Reading and converting {input_file}') rchirp_song = chiptunesak.GoatTracker().to_rchirp(input_file, arch='PAL-C64') # The song has a ritard at the end that will mess up the algorithm finding the beat, so eliminate it. print(f'Removing the ritard at the end of the song') rchirp_song.remove_tempo_changes() # Turn the song into a ChirpSong object print(f'Converting from RChirp to Chirp') chirp_song = rchirp_song.to_chirp() # We know the key signature and the time signature for the piece so set them (not required for playback) print(f'Setting time and key signatures') chirp_song.set_key_signature('G') chirp_song.set_time_signature(3, 8)
import copy import chiptunesak from chiptunesak.constants import project_to_absolute_path """ This example processes a MIDI file captured from Secret of Monkey Island to a GoatTracker song. It shows the steps needed for this conversion: 1. Scale and adjust the note data to correspond to musical notes and durations 2. Split a track with chords into 3 separate tracks 3. Assign GoatTracker instruments to the voices 4. Export the 5-track to a stereo GoatTracker .sng file """ input_file = str( project_to_absolute_path( 'examples/data/lechuck/MonkeyIsland_LechuckTheme.mid')) output_midi_file = str( project_to_absolute_path('examples/data/lechuck/LeChuck.mid')) output_gt_file = str( project_to_absolute_path('examples/data/lechuck/LeChuck.sng')) chirp_song = chiptunesak.MIDI().to_chirp(input_file) print(f'Original song:') print(f'#tracks = {len(chirp_song.tracks)}') print(f' ppq = {chirp_song.metadata.ppq}') print(f' tempo = {chirp_song.metadata.qpm} qpm') print('Track names:') print('\n'.join(f'{i+1}: {t.name}' for i, t in enumerate(chirp_song.tracks))) print()
# Make a SID for Jim Happel's last-minute CRX demo... import chiptunesak from chiptunesak.constants import project_to_absolute_path input_mid_file = project_to_absolute_path( 'examples\\data\\c128\\BWV_784_16th_to_8th.mid') output_gt_file = project_to_absolute_path('examples\\temp\\BWV_784.sng') chirp_song = chiptunesak.MIDI().to_chirp(input_mid_file, quantization='8', polyphony=False) rchirp_song = chiptunesak.RChirpSong(chirp_song) gt = chiptunesak.GoatTracker() # TODO: Dang, end_with_repeat=True seems to create an unplayable song. Fix this # after CRX # gt.to_file(rchirp_song, output_gt_file, arch='NTSC-C64', end_with_repeat=True) gt.to_file(rchirp_song, output_gt_file, arch='NTSC-C64')
1. Remove unused tracks, reorder and rename tracks to use 2. Consolidate two tracks into one, changing instruments partway through 3. Scale, move and adjust the note data to correspond to musical notes and durations 4. Set minimum note lengths, quantize the song, and remove polyphony 5. Truncate the captured song to a reasonable stopping point 6. Convert the ChirpSong to an MChirpSong 7. Use the Lilypond I/O object to write lilypond markup for the piece 8. Convert the ChirpSong to an RChirpSong 9. Assign GoatTracker instruments to the voices 10. Find repeated loops and compress the song 11. Export the GoatTracker .sng file """ output_folder = str( project_to_absolute_path('examples\\data\\mercantile')) + '\\' input_folder = output_folder input_file = str( project_to_absolute_path(input_folder + 'betrayalKrondorMercantile.mid')) output_midi_file = str( project_to_absolute_path(output_folder + 'mercantile.mid')) output_ly_file = str(project_to_absolute_path(output_folder + 'mercantile.ly')) output_gt_file = str(project_to_absolute_path(output_folder + 'mercantile.sng')) # Read in the original MIDI to Chirp chirp_song = chiptunesak.MIDI().to_chirp(input_file) # First thing, we rename the song chirp_song.metadata.name = "Betrayal at Krondor - Mercantile Theme" chirp_song.metadata.composer = "Jan Paul Moorhead"
import chiptunesak from chiptunesak.constants import project_to_absolute_path """ This example shows how to convert a 3-voice song to C128 Basic: 1. Import the song to chirp format from a MIDI file, quantizing the notes to the nearest 32nd note 2. Since C128 BASIC cannot do notes shorter than a 16th note, perform a metric modulation to double note lengths 3. Convert the song to mchirp format 3. Save the BASIC as source 4. Save the BASIC as a prg file """ output_folder = str(project_to_absolute_path('examples\\data\\C128')) + '\\' input_folder = str(project_to_absolute_path('examples\\data\\common')) + '\\' input_mid_file = input_folder + 'BWV_799.mid' output_bas_file = output_folder + 'BWV_799.bas' output_prg_file = output_folder + 'BWV_799.prg' # Read in the MIDI song and quantize chirp_song = chiptunesak.MIDI().to_chirp(input_mid_file, quantization='32', polyphony=False) # When imported, the shortest note is a 32nd note, which is too fast for C128 BASIC. # Perform a metric modulation by making every note length value twice as long, but # increasing the tempo by the same factor so it sounds the same. Now the shortest # note will be a 16th note which the C128 BASIC can play. print('Modulating music...') chirp_song.modulate(2, 1)
import os import subprocess import chiptunesak from chiptunesak.constants import project_to_absolute_path from chiptunesak.sid import SID """ This example shows how to do metric modulation to remove triplets """ file_name = 'Skyfox' output_folder = str(project_to_absolute_path('examples/data/triplets/')) input_folder = str(project_to_absolute_path('examples/data/sid/')) input_sid_file = os.path.join(input_folder, file_name + '.sid') output_mid_file = os.path.join(output_folder, file_name + '.mid') output_mod_mid_file = os.path.join(output_folder, file_name + '_mod.mid') output_ly_file = os.path.join(output_folder, file_name + '.ly') output_ly_file_mod = os.path.join(output_folder, file_name + '_mod.ly') sid = SID() # Skyfox SID playback continues to repeat, 100 secs is enough rchirp_song = sid.to_rchirp(input_sid_file, seconds=100) # CSV shows 24 plays calls per quarter note # 24 = 2^3 * 3; the factor of 3 is necessary for all the division-by-three rhythms play_calls_per_quarter = 24 chirp_song = rchirp_song.to_chirp( milliframes_per_quarter=play_calls_per_quarter * 1000) # the song is in the key of G and 4/4 time chirp_song.set_key_signature('G')
import chiptunesak from chiptunesak.constants import project_to_absolute_path """ This example shows how to process a clip of a song into a PNG file using Lilypond using the following steps: 1. Import the song to chirp format from a MIDI file, quantizing the notes to the nearest 16th note 2. Convert the song to mchirp format 3. Select the measures for the clip 4. Save the lilypond source 5. Run the lilypond converter from within python to generate the PNG file. """ output_folder = str( project_to_absolute_path('examples\\data\\lilypond')) + '\\' input_folder = output_folder input_file = input_folder + 'BWV_775.mid' output_ly_file = output_folder + 'BWV_775.ly' # Read in the MIDI song and quantize chirp_song = chiptunesak.MIDI().to_chirp(input_file, quantization='16', polyphony=False) # Convert to mchirp mchirp_song = chirp_song.to_mchirp() # Create the LilyPond I/O object lp = chiptunesak.Lilypond() # Set the format to do a clip and set the measures to the clip we want lp.set_options(format='clip', measures=mchirp_song.tracks[0].measures[3:8])
def __str__(self): return "Ultima IV Music file {}, number of songs: {}".format( self.name, self.num_songs) def import_song_to_chirp(self, song_no): """ Open and import a MIDI file into the ChirpSong representation. :param input_filename: Ultima IV filename. """ song = Ultima4Song(song_no, self) return song.chirp_song # Open the Ultima IV music file musicPath = constants.project_to_absolute_path('examples/data/appleii_u4/') f_songinfo = os.path.join(musicPath, 'u4songs.csv') with open(f_songinfo, 'r') as csvfile: csvreader = csv.reader(csvfile, delimiter=",") rows = [] for row in csvreader: rows.append(row) info = [dict(zip(rows[0], row)) for row in rows[1:]] for song in info: print("Extracting song:", song['title']) music = Ultima4Music(os.path.join(musicPath, song['fname'])) print("Number of songs in file: ", music.num_songs) chirp_song = music.import_song_to_chirp(int(song['songno']) - 1) midi_song = MIDI() output_filename = \
# Export subtunes from Master of the Lamps for processing in Sibelius. # Used to create this: https://www.youtube.com/watch?v=HH9sVayG0oQ # # Notes: # - 3 genies * 7 pieces * (1 tunnel level + 1 music level) + final tunnel = 43 levels # odd numbers are tunnels, even numbers are genies # 1st genie: level 1-14, 2nd genie: level 15-28, 3rd genie: level 29-42, # final tunnel: level 43 # - The starting key signatures (below) don't match the PAL subtunes this parses, instead # they should match the NTSC game footage (https://csdb.dk/release/?id=164839) that I # captured for the YouTube video that I'm going to match this music up with. import chiptunesak from chiptunesak.constants import project_to_absolute_path sid_filename = project_to_absolute_path( 'examples/data/sid/Master_of_the_Lamps_PAL.sid') # list of subtunes to extract to_extract = [ # repeats every 1024 frames [10, "getting on carpet", 21, 'Ebm', 2, 4], [7, "carpet liftoff", 9, 'Em', 2, 4], [9, "fell off carpet", 9, 'Am', 4, 4], [8, "finished level", 12, 'C', 2, 4], [11, "game won", 23, 'C', 4, 4], # level 1, 15, 29 [0, "tunnel 1", 77, 'C', 4, 4], # level 3, 17, 31 [1, "tunnel 2", 86, 'Cm', 4, 4],
import subprocess import chiptunesak from chiptunesak.constants import project_to_absolute_path """ This example shows how to process a song into PDF file using Lilypond using the following steps: 1. Import the song to chirp format from a MIDI file, quantizing the notes to the nearest 32nd note 2. Convert the song to mchirp format 3. Save the lilypond source 4. Run the lilypond converter from within python to generate the PDF file. """ output_folder = str( project_to_absolute_path('examples\\data\\lilypond')) + '\\' input_folder = str(project_to_absolute_path('examples\\data\\common')) + '\\' input_mid_file = input_folder + 'BWV_799.mid' output_ly_file = output_folder + 'BWV_799.ly' # Read in the MIDI song and quantize chirp_song = chiptunesak.MIDI().to_chirp(input_mid_file, quantization='32', polyphony=False) # It's in A minor, 3/8 time chirp_song.set_key_signature('Am') chirp_song.set_time_signature(3, 8) # Convert to mchirp mchirp_song = chirp_song.to_mchirp()