mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-14 15:29:27 +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 START2 !(input3 & 0x20)
|
||||||
#define VSYNC (input1 & 0x8)
|
#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 main();
|
||||||
void gsinit();
|
void gsinit();
|
||||||
|
@ -61,16 +80,8 @@ __asm
|
||||||
__endasm;
|
__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;
|
static word lfsr = 1;
|
||||||
word rand() {
|
word rand() {
|
||||||
unsigned lsb = lfsr & 1; /* Get LSB (i.e., the output bit). */
|
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() {
|
void main() {
|
||||||
palette = 0;
|
palette = 0;
|
||||||
|
|
|
@ -113,7 +113,7 @@ const char* const AY8910MASKS[14] = {
|
||||||
"11111111", " 1111",
|
"11111111", " 1111",
|
||||||
" 11111",
|
" 11111",
|
||||||
" cbaCBA",
|
" cbaCBA",
|
||||||
" E11111", " E11111", " E11111",
|
" E1111", " E1111", " E1111",
|
||||||
"11111111", " 1111",
|
"11111111", " 1111",
|
||||||
" CALH"
|
" CALH"
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ var VICDUAL_PRESETS = [
|
||||||
{id:'soundtest.c', name:'Sound Test'},
|
{id:'soundtest.c', name:'Sound Test'},
|
||||||
{id:'snake1.c', name:'Snake Game (Prototype)'},
|
{id:'snake1.c', name:'Snake Game (Prototype)'},
|
||||||
{id:'snake2.c', name:'Snake Game (Full)'},
|
{id:'snake2.c', name:'Snake Game (Full)'},
|
||||||
|
{id:'music.c', name:'Music Example'},
|
||||||
];
|
];
|
||||||
|
|
||||||
var VicDualPlatform = function(mainElement) {
|
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