mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-25 03:34:05 +00:00
working on music tools
This commit is contained in:
parent
4a5c6f86ed
commit
49cc581b2b
224
presets/vicdual/music.c
Normal file
224
presets/vicdual/music.c
Normal file
File diff suppressed because one or more lines are too long
@ -27,7 +27,26 @@ byte __at (0xe800) tileram[256][8];
|
||||
#define START2 !(input3 & 0x20)
|
||||
#define VSYNC (input1 & 0x8)
|
||||
|
||||
// GAME CODE
|
||||
inline void set8910(byte reg, byte data) {
|
||||
ay8910_reg = reg;
|
||||
ay8910_data = data;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
AY_PITCH_A_LO, AY_PITCH_A_HI,
|
||||
AY_PITCH_B_LO, AY_PITCH_B_HI,
|
||||
AY_PITCH_C_LO, AY_PITCH_C_HI,
|
||||
AY_NOISE_PERIOD,
|
||||
AY_ENABLE,
|
||||
AY_ENV_VOL_A,
|
||||
AY_ENV_VOL_B,
|
||||
AY_ENV_VOL_C,
|
||||
AY_ENV_PERI_LO, AY_ENV_PERI_HI,
|
||||
AY_ENV_SHAPE
|
||||
} AY8910Register;
|
||||
|
||||
|
||||
// STARTUP CODE
|
||||
|
||||
void main();
|
||||
void gsinit();
|
||||
@ -61,16 +80,8 @@ __asm
|
||||
__endasm;
|
||||
}
|
||||
|
||||
// SOUND CODE
|
||||
// PLATFORM CODE
|
||||
|
||||
inline void set8910(byte reg, byte data) {
|
||||
ay8910_reg = reg;
|
||||
ay8910_data = data;
|
||||
}
|
||||
|
||||
////////
|
||||
|
||||
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs
|
||||
static word lfsr = 1;
|
||||
word rand() {
|
||||
unsigned lsb = lfsr & 1; /* Get LSB (i.e., the output bit). */
|
||||
@ -132,7 +143,7 @@ void draw_box(byte x, byte y, byte x2, byte y2, const char* chars) {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GAME CODE
|
||||
|
||||
void main() {
|
||||
palette = 0;
|
||||
|
@ -113,7 +113,7 @@ const char* const AY8910MASKS[14] = {
|
||||
"11111111", " 1111",
|
||||
" 11111",
|
||||
" cbaCBA",
|
||||
" E11111", " E11111", " E11111",
|
||||
" E1111", " E1111", " E1111",
|
||||
"11111111", " 1111",
|
||||
" CALH"
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ var VICDUAL_PRESETS = [
|
||||
{id:'soundtest.c', name:'Sound Test'},
|
||||
{id:'snake1.c', name:'Snake Game (Prototype)'},
|
||||
{id:'snake2.c', name:'Snake Game (Full)'},
|
||||
{id:'music.c', name:'Music Example'},
|
||||
];
|
||||
|
||||
var VicDualPlatform = function(mainElement) {
|
||||
|
BIN
tools/entertainer.mid
Normal file
BIN
tools/entertainer.mid
Normal file
Binary file not shown.
156
tools/midi2song.py
Normal file
156
tools/midi2song.py
Normal file
@ -0,0 +1,156 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys, string, math
|
||||
import mido
|
||||
|
||||
min_note = 21
|
||||
max_note = 21+63
|
||||
max_voices = 3
|
||||
one_voice_per_channel = 1
|
||||
tempo = 48
|
||||
compress = 0
|
||||
transpose = 0
|
||||
|
||||
fn = sys.argv[1]
|
||||
|
||||
mid = mido.MidiFile(fn)
|
||||
|
||||
def hex2(n):
|
||||
return '0x%02x'%n
|
||||
|
||||
def only_notes(s):
|
||||
for ch in s:
|
||||
if ord(ch) == 0xff:
|
||||
return False
|
||||
return True
|
||||
|
||||
def find_common_substrings(s):
|
||||
results = {}
|
||||
for l in range(64, 6, -1):
|
||||
for i in range(0,len(s)-l*2):
|
||||
match = s[i:i+l]
|
||||
if not only_notes(match):
|
||||
continue
|
||||
count = 0
|
||||
j = i+l
|
||||
while j < len(s):
|
||||
p = s.find(match, j)
|
||||
if p > 0:
|
||||
count += 1
|
||||
j = p+l
|
||||
else:
|
||||
break
|
||||
if count:
|
||||
n = count*(l-1)-1
|
||||
if not results.get(i) or n > results[i][0]:
|
||||
results[i] = (n,l)
|
||||
return results
|
||||
|
||||
def get_best_substring(ss):
|
||||
best = (0,0,0)
|
||||
for k,v in ss.items():
|
||||
if v[0] > best[2]:
|
||||
best = (k,v[1],v[0])
|
||||
return best
|
||||
|
||||
def offset2str(ofs):
|
||||
return chr(ofs & 0xff) + chr((ofs >> 8) & 0xff)
|
||||
#return chr(0xc0 | (ofs & 0x3f)) + chr(0xc0 | ((ofs >> 6) & 0x3f))
|
||||
|
||||
g_code = 0xc1
|
||||
g_subs = []
|
||||
|
||||
def replace_substrings(s, bss):
|
||||
global g_code
|
||||
i,l,n = bss
|
||||
count = (n+1)/(l-1)
|
||||
match = s[i:i+l]
|
||||
print i,l,n,count,repr(match)
|
||||
r = s[0:i]
|
||||
while i<len(s):
|
||||
r += chr(g_code)
|
||||
p = s.find(match,i+l)
|
||||
if p < 0:
|
||||
p = len(s)
|
||||
r += s[i+l:p]
|
||||
i = p
|
||||
g_subs.append(match + chr(0xff))
|
||||
g_code += 1
|
||||
print len(s), len(r)+n+l
|
||||
assert len(s) == len(r)+n+l
|
||||
return r
|
||||
|
||||
def channels_for_track(track):
|
||||
channels = set()
|
||||
for msg in track:
|
||||
if msg.type == 'note_on':
|
||||
channels.add(msg.channel)
|
||||
return list(channels)
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print mid
|
||||
print mid.length, 'seconds'
|
||||
for i, track in enumerate(mid.tracks):
|
||||
print('Track {}: {} ({}) {}'.format(i, track.name, len(track), channels_for_track(track)))
|
||||
#for msg in track:
|
||||
# print(msg)
|
||||
else:
|
||||
if len(sys.argv) > 3:
|
||||
transpose = int(sys.argv[3])
|
||||
gtime = 0
|
||||
curtime = 0
|
||||
nnotes = 0
|
||||
nvoices = 0
|
||||
curchans = 0
|
||||
channels = [int(x) for x in string.split(sys.argv[2], ',')]
|
||||
print
|
||||
print "// %s %s" % (mid, channels)
|
||||
output = []
|
||||
for msg in mid:
|
||||
gtime += msg.time * tempo
|
||||
if msg.type == 'note_on' and msg.channel in channels:
|
||||
note = msg.note + transpose
|
||||
vel = msg.velocity
|
||||
t = int(math.ceil(gtime))
|
||||
if vel > 0:
|
||||
while curtime < t:
|
||||
dt = min(63, t-curtime)
|
||||
curtime += dt
|
||||
if nnotes > 0:
|
||||
nvoices = 0
|
||||
curchans = 0
|
||||
output.append(dt+128)
|
||||
if note >= min_note and note <= max_note and nvoices < max_voices:
|
||||
if not (one_voice_per_channel and (curchans & (1<<msg.channel))):
|
||||
n = note - min_note
|
||||
output.append(n)
|
||||
nnotes += 1
|
||||
nvoices += 1
|
||||
curchans |= 1<<msg.channel
|
||||
output.append(0xff)
|
||||
print string.join([hex2(x) for x in output], ',')
|
||||
if compress:
|
||||
# replace common substrings
|
||||
bout = ''.join(chr(i) for i in output)
|
||||
for iter in range(0,32):
|
||||
ss = find_common_substrings(bout)
|
||||
bss = get_best_substring(ss)
|
||||
print bss
|
||||
if bss[1] == 0:
|
||||
break
|
||||
bout = replace_substrings(bout, bss)
|
||||
print repr(bout)
|
||||
# build substring table
|
||||
ofs = (len(g_subs)+1)*2
|
||||
zout = offset2str(ofs)
|
||||
ofs += len(bout)
|
||||
for ss in g_subs:
|
||||
ofs += len(ss)
|
||||
zout += offset2str(ofs)
|
||||
# output strings
|
||||
zout += bout
|
||||
for ss in g_subs:
|
||||
zout += ss
|
||||
# print output
|
||||
print string.join([hex2(ord(x)) for x in zout], ',')
|
||||
print "// compressed %d -> %d bytes" % (len(output), len(zout))
|
34
tools/mknotes.py
Normal file
34
tools/mknotes.py
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys, math
|
||||
|
||||
test_notes = int(sys.argv[1]) or 49
|
||||
final_notes = int(sys.argv[2]) or 64
|
||||
|
||||
basehz = 3579545/32.0
|
||||
|
||||
results = []
|
||||
|
||||
for a440 in range(4300,4500):
|
||||
error = 0
|
||||
for note in range(1,test_notes):
|
||||
notehz = a440 / 10.0 * math.pow(2.0, (note - 49) / 12.0);
|
||||
period = int(round(basehz / notehz))
|
||||
tonehz = basehz / period
|
||||
error += abs(notehz-tonehz)
|
||||
#print a440,note,notehz,notehz-tonehz,period
|
||||
#if a440 == 4405:
|
||||
# print '%d,%f,%d' % (note,tonehz-notehz,period)
|
||||
results.append((error, a440))
|
||||
|
||||
results.sort()
|
||||
best_error, best_a440 = results[0]
|
||||
best_a440 /= 10.0
|
||||
print '//', best_a440, best_error, test_notes
|
||||
|
||||
print "const int note_table[%d] = {" % final_notes
|
||||
for note in range(0,final_notes):
|
||||
notehz = best_a440 * math.pow(2.0, (note - 49) / 12.0);
|
||||
period = int(round(basehz / notehz))
|
||||
print '%d,' % period,
|
||||
print "};"
|
71
tools/svg2vector.py
Executable file
71
tools/svg2vector.py
Executable file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
from xml.dom import minidom
|
||||
|
||||
def textToFloat(name):
|
||||
if name:
|
||||
return float(name)
|
||||
else:
|
||||
return None
|
||||
|
||||
def textToColor(name,opacity):
|
||||
if name is None or not name[0] == '#':
|
||||
return None
|
||||
color = int(name[1:],16) << 8
|
||||
if opacity:
|
||||
print 'opacity',opacity
|
||||
return color
|
||||
|
||||
svg_file = sys.argv[1]
|
||||
|
||||
doc = minidom.parse(svg_file) # parseString also exists
|
||||
groups = doc.getElementsByTagName('g')
|
||||
for grp in groups:
|
||||
groupID = grp.getAttribute('id')
|
||||
paths = grp.getElementsByTagName('path')
|
||||
print groupID,paths
|
||||
if len(paths):
|
||||
for path in paths:
|
||||
shape = {}
|
||||
d = path.getAttribute('d')
|
||||
styleAttrs = {}
|
||||
style = path.getAttribute('style')
|
||||
sarr = style.split(';')
|
||||
for s in sarr:
|
||||
nvarr = s.split(':', 2)
|
||||
if len(nvarr) == 2:
|
||||
styleAttrs[nvarr[0]] = nvarr[1]
|
||||
print path,d,styleAttrs
|
||||
shape['pts'] = []
|
||||
shape['currentColor'] = textToColor(styleAttrs.get("stroke"), styleAttrs.get("stroke-opacity"))
|
||||
shape['currentWidth'] = textToFloat(styleAttrs.get("strokeWidth"))
|
||||
hidden = "none" == styleAttrs.get("display")
|
||||
cmds = d.split(' ')
|
||||
x = 0
|
||||
y = 0
|
||||
i0 = 0
|
||||
start = True
|
||||
for cmd in cmds:
|
||||
print cmd
|
||||
ch = cmd[0]
|
||||
if ch == 'm' or ch == 'l':
|
||||
start = True
|
||||
elif ch == 'z':
|
||||
origin = shape['pts'][i0]
|
||||
shape.lineTo(origin.x, origin.y)
|
||||
elif ch == 'c':
|
||||
skip = True
|
||||
else:
|
||||
xy = cmd.split(',')
|
||||
print xy
|
||||
x += float(xy[0])
|
||||
y -= float(xy[1])
|
||||
if start or hidden:
|
||||
shape.moveTo(x,y)
|
||||
else:
|
||||
shape.lineTo(x,y)
|
||||
start = False
|
||||
|
||||
|
||||
doc.unlink()
|
Loading…
Reference in New Issue
Block a user