import sys,os
abspeed=0.02
spritedir='/home/nyk/ideas/Person/'
bgdir='/home/nyk/ideas/unm/'
fade_nb=10
debug=False
sprite_scaling=1.5
waiting_turns=20
def empty_directory(d):
for f in os.listdir(d):
os.unlink('%s/%s' % (d.rstrip('/'),f))
commands=['','Portamento Up','Portamento Down','TonePortamento','Vibrato','ToneP + VolSlide','Vibra + VolSlide','Tremolo','* NOT USED *','SampleOffset','VolumeSlide','PositionJump','Set Volume','PatternBreak','Misc. Cmds','Set Speed']
def get_pattern(data,p,speed,fps):
samples,notes,effects,lastchan={},{},{},{}
tots=0
last_speed=speed
for i in range(64):
tots+=abspeed*speed
frame=int(tots*fps)
for chan in range(4):
note=''
for byte in range(4):
v=ord(data[1084+p*1024+i*16+chan*4+byte])
b=bin(v)[2:]
note+=(8-len(b))*'0'+b
sample=int(note[:4]+note[16:20],2)
note_period=int(note[4:16],2)
effect_command=int(note[20:24],2)
effect_data=int(note[24:],2)
if effect_command==15: speed=effect_data
if not sample and lastchan.has_key(chan): sample=lastchan[chan]
lastchan[chan]=sample
if speed!=last_speed:
tots-=abspeed*last_speed
tots+=abspeed*speed
frame=int(tots*fps)
last_speed=speed
samples[frame]=samples.get(frame,[])+[sample]
notes[frame]=notes.get(frame,[])+[note_period]
effects[frame]=effects.get(frame,[])+[effect_command]
samplist,notelist,efflist=[],[],[]
for i in range(max(samples.keys())):
if samples.has_key(i):
samplist.append(samples[i])
notelist.append(notes[i])
efflist.append(effects[i])
else:
samplist.append([])
notelist.append([])
efflist.append([])
return samplist,notelist,efflist,speed
def mod_lists(fn,fps=25.0):
data=open(fn).read()
for i in range(20):
if data[i]==0: break
song_name=data[:i]
song_length=ord(data[950])
patterns=map(lambda x: ord(data[952+x]),range(song_length))
num_pat=max(patterns)+1
ident=data[1080:1084]
assert ident=='M.K.'
print 'Song Name = "%s", Length = %d, Patterns = %d' % (song_name,song_length,num_pat)
sample_list,note_list,effect_list=[],[],[]
speed=6
for i,p in enumerate(patterns):
s,n,e,speed=get_pattern(data,p,speed,fps)
sample_list+=s
note_list+=n
effect_list+=e
len_tot=float(len(sample_list))/fps
len_min=len_tot/60
len_s=len_tot % 60
print 'Speed = %d, length = %d:%02d (%d frames)' % (speed,len_min,len_s,len(sample_list))
return sample_list,note_list,effect_list
def load_sprite(x):
sprite=Image.open(spritedir+x)
sz=int(sprite.size[0]/sprite_scaling),int(sprite.size[1]/sprite_scaling)
return sprite.resize(sz)
def rnd_pos(x):
x=random.random()*(mask.size[0]-mask.size[0]/2)+mask.size[0]/4
y=random.random()*(mask.size[1]-mask.size[0]/2)+mask.size[0]/4
while mask.getpixel((int(x),int(y)))[0]==255: x,y=random.random()*mask.size[0],random.random()*mask.size[1]
return [x,y]
flat=lambda z : reduce(lambda x,y:list(x)+list(y),z,[])
def limit(x,m):
if x>=m: return m-1
if x<0: return 0
return x
def bright(iimg,n,cidx):
oimg=Image.new('RGBA',iimg.size)
for x in range(iimg.size[0]):
for y in range(iimg.size[1]):
c=iimg.getpixel((x,y))
cl=list(c)
if cl[3]>0:
if cidx>-1: cl[cidx]=int(cl[cidx]+(200-cl[cidx])/fade_nb*(fade_nb-n))
oimg.putpixel((x,y),tuple(cl))
return oimg
def exe(c):
print c
os.system(c)
fn=sys.argv[1]
odir='/tmp/' + os.path.basename(fn).split('.')[0]
if os.path.isdir(odir): empty_directory(odir)
else: os.mkdir(odir)
sample_list,note_list,effect_list=mod_lists(fn)
used_samples=filter(int,sorted(list(set(flat(sample_list)))))
used_notes=sorted(list(set(flat(note_list))))
used_effects=sorted(list(set(flat(effect_list))))
if debug:
print 'Samples = %d, Notes = %d' % (len(used_samples),len(used_notes))
print 'Used effects:',', '.join(map(lambda x : '"%s"' % commands[x],filter(int,used_effects)))
sampnotes={}
for u in used_samples:
sampnotes[u]=[]
for s,n in zip(flat(sample_list),flat(note_list)):
if s==u and n not in sampnotes[u]: sampnotes[u].append(n)
import Image,random,math,ImageDraw
bgfiles=sorted(os.listdir(bgdir))
assert len(bgfiles)>=len(sample_list)
mask=Image.open('/opt/img/kg_mask.png')
spritefiles=sorted(os.listdir(spritedir))
random.shuffle(spritefiles)
channels=4
sprites=map(load_sprite,spritefiles[:channels])
spritei=channels
mx,my=mask.size[0]/2,mask.size[1]/2
positions=[[mx-50,my-50],[mx-50,my+50],[mx+50,my-50],[mx+50,my+50]]
degrees=[225,315,135,45]
speeds=[0.0]*len(sprites)
turns=[0]*len(sprites)
blinks=[0]*len(sprites)
import progressbar
pbar = progressbar.ProgressBar().start()
for i,(s,n,e) in enumerate(zip(sample_list,note_list,effect_list)):
im=Image.open(bgdir+bgfiles[i])
if debug: draw=ImageDraw.Draw(im)
for chan,(si,ni,ei) in enumerate(zip(s,n,e)):
if si: np=float(sampnotes[si].index(ni))/float(len(sampnotes[si]))
else: np=0.1
speeds[chan]=np*10
if ei and 'Vol' not in commands[ei]: blinks[chan]=fade_nb*2
if np>0.8 and not turns[chan]: degrees[chan]+=1
if np<0.2 and not turns[chan]: degrees[chan]-=1
for n,spr in enumerate(sprites):
positions[n][0]+=math.sin(math.radians(degrees[n]))*speeds[n]
positions[n][1]+=math.cos(math.radians(degrees[n]))*speeds[n]
p=limit(int(positions[n][0]),mask.size[0]),limit(int(positions[n][1]),mask.size[1])
if (p[0]<spr.size[0] or p[0]>mask.size[0]-spr.size[0]) and not turns[n]:
degrees[n]=360-degrees[n]
turns[n]=waiting_turns
if (p[1]<spr.size[1] or p[1]>mask.size[1]-spr.size[1]) and not turns[n]:
degrees[n]=180-degrees[n]
turns[n]=waiting_turns
if p[1]>mask.size[1]-spr.size[1]:
sprites[n]=load_sprite(spritefiles[spritei])
spritei+=1
if spritei>=len(spritefiles): spritei=0
if mask.getpixel(p)[0]==255 and not turns[n] and speeds[n]>0:
degrees[n]+=180
turns[n]=waiting_turns
if turns[n]:
if degrees[n]>360: degrees[n]=degrees[n]-360
if degrees[n]<0: degrees[n]=degrees[n]+360
turns[n]-=1
if blinks[n]:
if blinks[n]>fade_nb: fade_pos=blinks[n]-fade_nb
else: fade_pos=fade_nb-blinks[n]
spr=bright(spr,fade_pos,2)
blinks[n]-=1
im.paste(spr,p,spr)
if debug: draw.text(p,'%d' % degrees[n],fill='red')
im.save('%s/image%05d.jpg' % (odir,i))
pbar.update(int(float(i)/float(len(sample_list))*100))
pbar.finish()
afn='/tmp/'+fn.lower().replace('mod','avi')
afn2=fn.lower().replace('mod','avi')
wfn='/tmp/'+fn.lower().replace('mod','wav')
mfn='/tmp/'+fn.lower().replace('mod','mp3')
assert fn!=afn2
exe('mencoder mf://%s/*.jpg -ovc x264 -x264encopts crf=20 -o %s -mf fps=25' % (odir,afn) )
if not os.path.isfile(wfn): exe('timidity %s -Ow -o %s' % (fn,wfn))
if not os.path.isfile(mfn): exe('lame %s %s' % (wfn,mfn))
if os.path.isfile(afn2): os.unlink(afn2)
exe('ffmpeg -i %s -i %s -vcodec copy %s -acodec copy -newaudio' % (afn,mfn,afn2))