Advertisement
Guest User

Untitled

a guest
Jul 23rd, 2022
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.68 KB | None | 0 0
  1. #!/usr/bin/python3.9
  2. # -*- coding: utf-8 -*-
  3. # vim: set fileencoding=utf-8 :
  4.  
  5. """
  6. Converts a 0cc-json-export into bytebeat code.
  7. No effects. Very basic volume and square control (hardcoded for each channel)
  8. Uses the engine found in BotB Entry 17669
  9. Author: @kleeder
  10. Created: 2022-07-22
  11. """
  12.  
  13. import json
  14. import math
  15.  
  16. with open("music.json", 'r') as f:
  17.     occ_export = json.load(f)
  18.  
  19. song_data = occ_export['songs'][0]
  20.  
  21.  
  22. class Module:
  23.     rows = song_data['rows']
  24.     speed = song_data['speed'] * 2
  25.     tempo = song_data['tempo']
  26.     frames = song_data['frames']
  27.     channels = song_data['tracks'][0:4]
  28.  
  29.  
  30. class Bytebeat:
  31.     frequencies = [
  32.         261.63,
  33.         277.18,
  34.         293.66,
  35.         311.13,
  36.         329.63,
  37.         349.23,
  38.         369.99,
  39.         392.00,
  40.         415.30,
  41.         440.00,
  42.         466.16,
  43.         493.88,
  44.     ]
  45.     noise_frequencies = [
  46.         440.0,
  47.         879.9,
  48.         1761.6,
  49.         2348.8,
  50.         3523.2,
  51.         4709.9,
  52.         7046.4,
  53.         8860.3,
  54.         11186.1,
  55.         13982.6,
  56.         18643.5,
  57.         27965.2,
  58.         55930.4,
  59.         111860.8,
  60.         223721.6,
  61.         447443.3,
  62.     ]
  63.     channel_pulses = [
  64.         50,
  65.         25,
  66.         "''",
  67.         "floor(t * (chan4_freq * 440 / SAMP_RATE) / 440)"
  68.     ]
  69.     channel_amps = [
  70.         25,
  71.         20,
  72.         50,
  73.         20
  74.     ]
  75.     channel_mix = [
  76.         "chan1_amp * 2 * (floor(chan1_freq * t / SAMP_RATE * 256) % 256 <= chan1_pulse * 256 / 100) - chan1_amp",
  77.         "chan2_amp * 2 * (floor(chan2_freq * t / SAMP_RATE * 256) % 256 <= chan2_pulse * 256 / 100) - chan2_amp",
  78.         "chan3_amp / 128 * (17*abs(min(16-t*chan3_freq*32/SAMP_RATE%32|0,15))) - chan3_amp",
  79.         "(chan4_amp / 128) * (floor(65536 * sin(chan4_pulse*chan4_pulse)) & 255) - chan4_amp"
  80.     ]
  81.  
  82.  
  83. def frame_lookup(frame_number, patterns):
  84.     for pattern in patterns:
  85.         if pattern['index'] == frame_number:
  86.             return pattern
  87.  
  88.     else:
  89.         return {
  90.             "notes": []
  91.         }
  92.  
  93.  
  94. def get_note_frequency(note_value, is_tri, is_noise):
  95.     if is_noise:
  96.         return Bytebeat.noise_frequencies[note_value - 16]
  97.     oct_dropdown = 3
  98.     if is_tri:
  99.         oct_dropdown = 4
  100.     octave = math.floor(note_value / 12) - oct_dropdown
  101.     which_note = note_value % 12
  102.     byte_freq = Bytebeat.frequencies[which_note]
  103.     if octave < 0:
  104.         return byte_freq / (2 ** abs(octave))
  105.     return byte_freq * (2 ** abs(octave))
  106.  
  107.  
  108. def convert_channel(channel, ch_index):
  109.     global_tick = 0
  110.     open_note = False
  111.     chan_data = f"chan{ch_index + 1}_freq =\n"
  112.     note_start = [
  113.         "ERROR",
  114.         "ERROR",
  115.         False
  116.     ]
  117.  
  118.     # C * (tick >= 0 && tick < 12)
  119.     for frame in channel['frame_list']:
  120.         pattern = frame_lookup(frame, channel['patterns'])
  121.         chan_data += f"// pattern {frame}\n"
  122.         for note in pattern['notes']:
  123.             note_row = note['row']
  124.             byte_tick = global_tick + note_row * Module.speed
  125.             if note['note']['kind'] == 'note':
  126.                 note_value = note['note']['value']
  127.  
  128.                 if open_note:
  129.                     chan_data += f"{note_start[0]} * (tick >= {note_start[1]} && tick < {byte_tick}) +"
  130.                 note_freq = get_note_frequency(note_value, (ch_index == 2), (ch_index == 3))
  131.  
  132.                 note_start = [
  133.                     note_freq,
  134.                     byte_tick,
  135.                 ]
  136.                 open_note = True
  137.             elif note['note']['kind'] == 'halt':
  138.                 if open_note:
  139.                     chan_data += f"{note_start[0]} * (tick >= {note_start[1]} && tick < {byte_tick}) +"
  140.  
  141.                 open_note = False
  142.         global_tick += Module.rows * Module.speed
  143.  
  144.     if open_note:
  145.         chan_data += f"{note_start[0]} * (tick >= {note_start[1]} && tick < {global_tick}),\n"
  146.     if chan_data.endswith("+") or chan_data.endswith("*"):
  147.         chan_data = chan_data[:-1] + ","
  148.     chan_data += f"\nchan{ch_index + 1}_amp = {Bytebeat.channel_amps[ch_index]},\nchan{ch_index + 1}_pulse = {Bytebeat.channel_pulses[ch_index]},\n"
  149.     return chan_data
  150.  
  151.  
  152. bytebeat_file = f"""SAMP_RATE = 11025,\n
  153. BPM = {Module.tempo},\n
  154. beat = BPM * (t / SAMP_RATE) / 60,\n
  155. tick = floor(beat * 48) % {Module.frames * Module.rows * Module.speed},\n\n
  156. """
  157.  
  158. byte_channel_data = []
  159. for i, channel in enumerate(Module.channels):
  160.     byte_channel_data.append(convert_channel(channel, i))
  161.  
  162. for byte_chan_data in byte_channel_data:
  163.     bytebeat_file += byte_chan_data
  164.  
  165. ch_mixing = "128 + " + "\n+ ".join(Bytebeat.channel_mix)
  166.  
  167. bytebeat_file += ch_mixing
  168.  
  169. with open("byte_convert.js", "w") as f:
  170.     f.write(bytebeat_file)
  171.     f.close()
  172.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement