Advertisement
kill0u

autotracker.py

Dec 20th, 2012
403
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 20.64 KB | None | 0 0
  1. #!/usr/bin/env python --
  2. # -*- coding: utf-8 -*-
  3. #
  4. #    Autotracker-C - the even more ultimate audio experience
  5. #    (C) Ben "GreaseMonkey" Russell, 2010
  6. #
  7. #    This program is free software: you can redistribute it and/or modify
  8. #    it under the terms of the GNU General Public License as published by
  9. #    the Free Software Foundation, either version 3 of the License, or
  10. #    (at your option) any later version.
  11. #
  12. #    This program is distributed in the hope that it will be useful,
  13. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. #    GNU General Public License for more details.
  16. #
  17. #    You should have received a copy of the GNU General Public License
  18. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19.  
  20. import sys, struct, random
  21. import math
  22.  
  23. # we'll do major as our base this time
  24. # if random.random() < 0.4: # minor
  25.     # BASE_SCALE = [ 0, 2, 3, 5, 7, 8, 10 ]
  26.     # BASE_ISCALE = [ 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6 ]
  27. # else: # major
  28. BASE_SCALE = [ 0, 2, 4, 5, 7, 9, 11 ]
  29. BASE_ISCALE = [ 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 ]
  30. BASE_ICHORDS = [[] for i in xrange(12)]
  31. BASE_IBASS = [[] for i in xrange(12)]
  32. for i in [0,3,4,5]:
  33.     for j in xrange(3):
  34.         BASE_IBASS[BASE_SCALE[(i+j*2)%7]].append(BASE_SCALE[i])
  35. for i in xrange(6):
  36.     for j in xrange(3):
  37.         BASE_ICHORDS[BASE_SCALE[(i+j*2)%7]].append(BASE_SCALE[i])
  38.  
  39. # strategies we can use
  40. STRAT_CHORUS = 1
  41. STRAT_VERSE = 2
  42. STRAT_PRECHORUS = 3
  43. STRAT_INTERLUDE = 4 # minor <-> major pretty much
  44. STRAT_INTRO = 5 # must be done after doing the chorus
  45. STRAT_OUTRO = 6
  46.  
  47. STRAT_FLAGS_RELATE = 0x100 # attempt to relate to reference
  48. STRAT_FLAGS_START = 0x200 # start with base note
  49. STRAT_FLAGS_END = 0x400 # end with base note and cadence
  50. STRAT_FLAGS_TRANSITION = 0x800 # aim for base note after end and cadence
  51. STRAT_FLAGS_COPY = 0x1000 # just copy the pattern data
  52. STRAT_FLAGS_COMPOTRANSPOSE = 0x2000 # DON'T DO ANYTHING ELSE
  53. STRAT_FLAGS_FADEOUT = 0x4000
  54.  
  55. # stages are 16 rows long
  56. # there are 4 stages per 64-row pattern
  57. # stretches are 1 64-row pattern long
  58.  
  59. def relnote(n, a):
  60.     kn = BASE_ISCALE[(n+120)%12] + (n/12)*7
  61.     kn += a
  62.     return BASE_SCALE[(kn+70)%7] + (kn/7)*12
  63.  
  64. def nearinate(ln, n):
  65.     if n >= 12:
  66.         if abs(n-12-ln) < abs(n-ln):
  67.             return n-12
  68.         else:
  69.             return n
  70.     else:
  71.         if abs(n+12-ln) < abs(n-ln):
  72.             return n+12
  73.         else:
  74.             return n
  75.    
  76. class Depender:
  77.     def __init__(self, sng, parent):
  78.         self.parent = parent
  79.         self.sng = sng
  80.         if parent:
  81.             parent.add_dep(self)
  82.         self.deps = []
  83.    
  84.     # add dependency
  85.     def add_dep(self, dep):
  86.         self.deps.append(dep)
  87.    
  88.     # delegate stuff to your children
  89.     def delegate(self):
  90.         for d in self.deps:
  91.             d.delegate()
  92.             d.depend()
  93.    
  94.     # delegate spice to your children
  95.     # this does the actual pattern writing
  96.     def delegate_spice(self, pat, stage):
  97.         for d in self.deps:
  98.             d.delegate_spice(pat, stage)
  99.        
  100.         self.spice(pat, stage)
  101.    
  102.     ###################################
  103.     # v OVERRIDE THESE METHODS HERE v #
  104.     ###################################
  105.    
  106.     # make your mind up on the matter
  107.     def initiate(self):
  108.         pass
  109.    
  110.     # make children repeat strat
  111.     def repeat(self):
  112.         pass
  113.    
  114.     # depend on parent decision
  115.     def depend(self):
  116.         pass
  117.    
  118.     # make your piece awesome
  119.     def spice(self, pat, stage):
  120.         pass
  121.  
  122. class LeadInstrument(Depender):
  123.     def __init__(self, sng, parent):
  124.         Depender.__init__(self, sng, parent)
  125.         self.ch = sng.alloc(1, True)
  126.         self.last_note = 0
  127.         self.steal_note = False
  128.    
  129.     def spice(self, pat, stage):
  130.         n1 = self.sng.lead_data[stage]
  131.         n2 = self.sng.lead_data[stage+1] if stage+1 < len(self.sng.lead_data) else n1
  132.         base = (stage&3)*16
  133.         if (pat.strat&255) == STRAT_OUTRO:
  134.             if (pat.strat & STRAT_FLAGS_START) and base == 0:
  135.                 pat.data[base][self.ch][0] = 254 # note cut
  136.            
  137.             return
  138.         elif (pat.strat&255) == STRAT_INTRO:
  139.             return
  140.        
  141.         if not self.steal_note:
  142.             pat.data[base][self.ch][0] = n1+60
  143.             pat.data[base][self.ch][1] = 1
  144.        
  145.         last_steal = self.steal_note
  146.        
  147.         self.steal_note = random.random() < 0.6
  148.        
  149.         #print n1,n2
  150.        
  151.         stealstep = random.randint(1,2)
  152.         if pat.strat & STRAT_FLAGS_END and (stage & 3) == 3:
  153.             self.steal_note = False
  154.         else:
  155.             noteseq = []
  156.            
  157.             r = random.randint(1,1)
  158.             if r == 1: # steping two notes just before n2
  159.                 # pick either one for equal
  160.                 if n2 > n1 or (n2 == n1 and random.random() < 0.5):
  161.                     noteseq.append(relnote(n2,-2))
  162.                     noteseq.append(relnote(n2,-1))
  163.                 else:
  164.                     noteseq.append(relnote(n2,2))
  165.                     noteseq.append(relnote(n2,1))
  166.        
  167.             beatseq = []
  168.             lbs = 2
  169.             tgt = 8-len(noteseq)
  170.             if last_steal:
  171.                 tgt -= stealstep
  172.            
  173.             for i in xrange(len(noteseq)):
  174.                 lbs = random.randint(lbs,tgt)
  175.                 tgt += 1
  176.                 beatseq.append(2*lbs)
  177.            
  178.             for i in xrange(len(noteseq)):
  179.                 pat.data[base+beatseq[i]][self.ch][0] = noteseq[i]+60
  180.                 pat.data[base+beatseq[i]][self.ch][1] = 1
  181.        
  182.         if self.steal_note:
  183.            
  184.             pat.data[base+16-stealstep*2][self.ch][0] = n2+60
  185.             pat.data[base+16-stealstep*2][self.ch][1] = 1
  186.        
  187.    
  188.     def initiate(self):
  189.         note = 0
  190.        
  191.         if (self.sng.strat&255) == STRAT_INTERLUDE:
  192.             note = BASE_SCALE[4] if (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3 else BASE_SCALE[5]
  193.         elif (self.sng.strat&255) == STRAT_PRECHORUS:
  194.             note = BASE_SCALE[4] if (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3 else BASE_SCALE[1]
  195.        
  196.         if (self.sng.strat & STRAT_FLAGS_START) and self.sng.stretch_pos == 0:
  197.             self.last_note = 0
  198.             pass
  199.         elif (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3:
  200.             pass
  201.         elif (self.sng.strat & STRAT_FLAGS_RELATE) and self.sng.ref != -1:
  202.             note = random.choice(BASE_ICHORDS[(self.sng.bass_data[self.sng.ref*4+self.sng.stretch_pos]+120)%12])
  203.         else:
  204.             note = random.choice(BASE_SCALE[:-1]) # don't use 7 here
  205.        
  206.         note = nearinate(self.last_note,note)
  207.        
  208.         self.sng.lead_data.append(note)
  209.         self.last_note = note
  210.         self.delegate()
  211.  
  212. class BassInstrument(Depender):
  213.     def __init__(self, sng, parent):
  214.         Depender.__init__(self, sng, parent)
  215.         self.ch = sng.alloc(1, True)
  216.         self.last_note = 0
  217.    
  218.     def spice(self, pat, stage):
  219.         n = self.sng.bass_data[stage]
  220.         base = (stage&3)*16
  221.        
  222.         for i in xrange(0,16,2):
  223.             pat.data[base+i][self.ch][0] = n+60
  224.             pat.data[base+i][self.ch][1] = 2
  225.    
  226.     def depend(self):
  227.         note = 0
  228.        
  229.         if (self.sng.strat&255) == STRAT_INTERLUDE:
  230.             note = BASE_SCALE[4] if (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3 else BASE_SCALE[5]
  231.         elif (self.sng.strat&255) == STRAT_PRECHORUS:
  232.             note = BASE_SCALE[4] if (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3 else BASE_SCALE[1]
  233.        
  234.         if (self.sng.strat & STRAT_FLAGS_START) and self.sng.stretch_pos == 0:
  235.             pass
  236.         elif (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3:
  237.             pass
  238.         elif (self.sng.strat & STRAT_FLAGS_RELATE):
  239.             note = self.sng.bass_data[self.sng.ref*4+self.sng.stretch_pos]
  240.         else:
  241.             note = random.choice(BASE_IBASS[self.sng.lead_data[-1]%12])
  242.        
  243.         if (self.sng.strat&255) == STRAT_PRECHORUS and note == 0:
  244.             note = BASE_SCALE[5]
  245.        
  246.         note = nearinate(12,note+12)-12
  247.         self.sng.bass_data.append(note)
  248.         self.last_note = note
  249.    
  250.     def initiate(self):
  251.         raise Exception("BassInstrument not meant to initiate")
  252.  
  253. class DrumInstrument(Depender):
  254.     def __init__(self, sng, parent):
  255.         Depender.__init__(self, sng, parent)
  256.         self.ch = sng.alloc(3, False)
  257.         self.skip_kick = False
  258.    
  259.     def spice(self, pat, stage):
  260.         rbase = (stage&3)*16
  261.        
  262.         note = 60
  263.        
  264.         for q in xrange(2):
  265.             base = rbase + q*8
  266.             prekick = (random.random() < 0.5)
  267.            
  268.             if not self.skip_kick:
  269.                 pat.data[base][self.ch+1][0] = note
  270.                 pat.data[base][self.ch+1][1] = 4
  271.            
  272.             if prekick:
  273.                 pat.data[base+2][self.ch+1][0] = note
  274.                 pat.data[base+2][self.ch+1][1] = 4
  275.            
  276.             pat.data[base+4][self.ch+2][0] = note
  277.             pat.data[base+4][self.ch+2][1] = 5
  278.            
  279.             for i in xrange(0,8,2):
  280.                 pat.data[base+i][self.ch][0] = note
  281.                 pat.data[base+i][self.ch][1] = 3
  282.                 if i%4 == 2:
  283.                     pat.data[base+i][self.ch][2] = 30
  284.            
  285.             if (pat.strat & STRAT_FLAGS_START) and base == 0:
  286.                 prekick = False
  287.             elif (pat.strat & STRAT_FLAGS_END) and base == 56:
  288.                 self.skip_kick = False
  289.                 for i in xrange(2,8,2):
  290.                     pat.data[base+i][self.ch+2][0] = note
  291.                     pat.data[base+i][self.ch+2][1] = 5
  292.             elif (pat.strat & STRAT_FLAGS_RELATE): # blatant copy
  293.                 opat = self.sng.refpat(pat.ref)
  294.                 for i in xrange(8):
  295.                     for j in xrange(3):
  296.                         pat.data[base+i][self.ch+j] = opat.data[base+i][self.ch+j][:]
  297.                 continue
  298.            
  299.             self.skip_kick = (random.random() < 0.4)
  300.            
  301.             if self.skip_kick:
  302.                 pat.data[base+6][self.ch+1][0] = note
  303.                 pat.data[base+6][self.ch+1][1] = 4
  304.    
  305.     def depend(self):
  306.         # drums only happen in spice
  307.         pass
  308.    
  309.     def initiate(self):
  310.         raise Exception("DrumInstrument not meant to initiate")
  311.  
  312. class ChordInstrument(Depender):
  313.     def __init__(self, sng, parent):
  314.         Depender.__init__(self, sng, parent)
  315.         self.ch = sng.alloc(3, True)
  316.         self.last_note = None
  317.    
  318.     def spice(self, pat, stage):
  319.         nb = (self.sng.bass_data[stage]+120)%12
  320.         n1 = (self.sng.chord_data[stage]+120)%12
  321.         n2 = (relnote(n1,2)+120)%12
  322.         n3 = (relnote(n1,4)+120)%12
  323.         base = (stage&3)*16
  324.        
  325.         if base == 0:
  326.             self.last_note = None
  327.        
  328.         if n1 != self.last_note:
  329.             pat.data[base][self.ch][0] = nb+60
  330.             pat.data[base][self.ch][1] = 6
  331.            
  332.             if n2 == nb:
  333.                 pat.data[base][self.ch][0] = n2+60
  334.                 pat.data[base][self.ch][1] = 6
  335.                 pat.data[base][self.ch+1][0] = n3+60
  336.                 pat.data[base][self.ch+1][1] = 6
  337.                 pat.data[base][self.ch+2][0] = n1+60+12
  338.                 pat.data[base][self.ch+2][1] = 6
  339.             elif n3 == nb:
  340.                 pat.data[base][self.ch][0] = n3+60
  341.                 pat.data[base][self.ch][1] = 6
  342.                 pat.data[base][self.ch+1][0] = n1+60+12
  343.                 pat.data[base][self.ch+1][1] = 6
  344.                 pat.data[base][self.ch+2][0] = n2+60+12
  345.                 pat.data[base][self.ch+2][1] = 6
  346.             else:
  347.                 pat.data[base][self.ch][0] = n1+60
  348.                 pat.data[base][self.ch][1] = 6
  349.                 pat.data[base][self.ch+1][0] = n2+60
  350.                 pat.data[base][self.ch+1][1] = 6
  351.                 pat.data[base][self.ch+2][0] = n3+60
  352.                 pat.data[base][self.ch+2][1] = 6
  353.        
  354.         self.last_note = n1
  355.    
  356.     def depend(self):
  357.         note = 0
  358.        
  359.         if (self.sng.strat&255) == STRAT_INTERLUDE:
  360.             note = 7 if (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3 else 9
  361.         elif (self.sng.strat&255) == STRAT_PRECHORUS:
  362.             note = 7 if (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3 else 2
  363.        
  364.         if (self.sng.strat & STRAT_FLAGS_START) and self.sng.stretch_pos == 0:
  365.             pass
  366.         elif (self.sng.strat & STRAT_FLAGS_END) and self.sng.stretch_pos == 3:
  367.             pass
  368.         elif (self.sng.strat & STRAT_FLAGS_RELATE):
  369.             note = self.sng.chord_data[self.sng.ref*4+self.sng.stretch_pos]
  370.         else:
  371.             note = random.choice(BASE_ICHORDS[self.sng.bass_data[-1]%12])
  372.        
  373.         if (self.sng.strat&255) == STRAT_PRECHORUS and note == 0:
  374.             note = 9
  375.        
  376.         note = nearinate(12,note+12)-12
  377.        
  378.         self.sng.chord_data.append(note)
  379.    
  380.     def initiate(self):
  381.         raise Exception("ChordInstrument not meant to initiate")
  382.  
  383. class FadeoutEffect(Depender):
  384.     def __init__(self, sng, parent):
  385.         Depender.__init__(self, sng, parent)
  386.         self.ch = sng.alloc(1, False)
  387.         self.fade_len = 0
  388.    
  389.     def spice(self, pat, stage):
  390.         base = (stage&3)*16
  391.         if (pat.strat&255) == STRAT_INTRO and (pat.strat & STRAT_FLAGS_START) and (stage&3) == 0:
  392.             pat.data[0][self.ch][3] = 22
  393.             pat.data[0][self.ch][4] = 0x80
  394.         elif (pat.strat&255) == STRAT_OUTRO:
  395.             for i in xrange(0,16,self.sng.stretch_len/2):
  396.                 pat.data[base+i][self.ch][3] = 23
  397.                 pat.data[base+i][self.ch][4] = 0xF1
  398.    
  399.     def depend(self):
  400.         if (self.sng.strat&255) == STRAT_OUTRO:
  401.             self.fade_len = self.sng.stretch_len
  402.        
  403.    
  404.     def initiate(self):
  405.         raise Exception("FadeoutEffect not meant to initiate")
  406.  
  407. class SwingEffect(Depender):
  408.     def __init__(self, sng, parent):
  409.         Depender.__init__(self, sng, parent)
  410.         self.ch = sng.alloc(1, False)
  411.         self.fade_len = 0
  412.         self.swing_str = random.randint(-1,2)
  413.         if self.swing_str <= 0:
  414.             self.swing_str = 0
  415.    
  416.     def spice(self, pat, stage):
  417.         base = (stage&3)*16
  418.         if self.swing_str == 1:
  419.             for i in xrange(0,16,4):
  420.                 pat.data[base+i][self.ch][3] = 1
  421.                 pat.data[base+i][self.ch][4] = 5
  422.                 pat.data[base+i+2][self.ch][3] = 1
  423.                 pat.data[base+i+2][self.ch][4] = 3
  424.         elif self.swing_str == 2:
  425.             for i in xrange(0,16,4):
  426.                 pat.data[base+i][self.ch][3] = 1
  427.                 pat.data[base+i][self.ch][4] = 6
  428.                 pat.data[base+i+2][self.ch][3] = 1
  429.                 pat.data[base+i+2][self.ch][4] = 3
  430.    
  431.     def depend(self):
  432.         pass
  433.    
  434.     def initiate(self):
  435.         raise Exception("SwingEffect not meant to initiate")
  436.  
  437. class Pattern:
  438.     def __init__(self, rows, strat, ref):
  439.         self.rows = rows
  440.         self.strat = strat
  441.         self.ref = ref
  442.         self.data = [[[-1,-1,-1,-1,0] for i in xrange(64)] for i in xrange(rows)]
  443.    
  444.     def save(self, fp):
  445.         # don't bother packing well, you'll need to add your samples in -GM
  446.         odat = []
  447.        
  448.         for l in self.data:
  449.             for ch in xrange(64):
  450.                 m = 0x00
  451.                
  452.                 if l[ch][0] != -1:
  453.                     m |= 0x01
  454.                 if l[ch][1] != -1:
  455.                     m |= 0x02
  456.                 if l[ch][2] != -1:
  457.                     m |= 0x04
  458.                 if l[ch][3] != -1:
  459.                     m |= 0x08
  460.                
  461.                 odat.append(0x80|(ch+1))
  462.                 odat.append(m)
  463.                 if m & 0x01:
  464.                     odat.append(l[ch][0])
  465.                 if m & 0x02:
  466.                     odat.append(l[ch][1])
  467.                 if m & 0x04:
  468.                     odat.append(l[ch][2])
  469.                 if m & 0x08:
  470.                     odat.append(l[ch][3])
  471.                     odat.append(l[ch][4])
  472.             odat.append(0)
  473.        
  474.         fp.write(struct.pack("<HHI",len(odat),self.rows,0))
  475.         fp.write(''.join(chr(v) for v in odat))
  476.  
  477. class Song:
  478.     def __init__(self):
  479.         self.orders = []
  480.         self.samples = self.gen_samples()
  481.         self.patterns = []
  482.         self.pat_chorus = []
  483.         self.pat_chorus_base = 0
  484.         self.pat_chorus_ord = 0
  485.         self.chnbase = 0
  486.         self.chn_tonal = [False for i in xrange(64)]
  487.        
  488.         self.lead_data = []
  489.         self.beat_data = []
  490.         self.bass_data = []
  491.         self.chord_data = []
  492.        
  493.         self.key = random.randint(-6,6) # 0 == (major ? C : Am)
  494.         self.mode = 0 # 0 == major, -3 = minor
  495.         self.strat = 0
  496.         self.ref = 0
  497.        
  498.         self.stretch_len = 0
  499.         self.stretch_pos = 0
  500.        
  501.         # OK, let's rock
  502.         lead = LeadInstrument(self, None)
  503.         bass = BassInstrument(self, lead)
  504.         drum = DrumInstrument(self, lead)
  505.         chords = ChordInstrument(self, bass)
  506.         fx_fadeout = FadeoutEffect(self, lead)
  507.         fx_swing = SwingEffect(self, lead)
  508.         self.root = lead
  509.        
  510.         self.epictronise()
  511.    
  512.     def fakesin(self,v):
  513.         v = v % math.pi*2
  514.         return -0.5 if v < math.pi else 0.5
  515.    
  516.     def saw(self,v,d):
  517.         return ((float(v)/float(d)) % 1.0)*2.0-1.0
  518.    
  519.     def gen_samples(self):
  520.         l = []
  521.        
  522.         # sample 1: lead: square
  523.         l.append(([(-60 if i%256 < 128 else 60)+(-60 if i%255 < 128 else 60) for i in xrange(256*255)],8363*8,True))
  524.        
  525.         # sample 2: bass: sawtooth
  526.         l.append(([(((i%64)-32)*180*(i**2))/(64*((64*50)**2)) for i in xrange(64*50)][::-1],8363,True))
  527.        
  528.         # sample 3: hihat
  529.         q = []
  530.         for i in xrange(650):
  531.             q = [(random.randint(-1,1)*120*i*i)/(650*650)] + q
  532.         l.append((q,8134*4,False))
  533.        
  534.         # sample 4: kick
  535.         q = []
  536.         for i in xrange(4000):
  537.             q = [int(120*self.fakesin(i*i*i*math.pi*2.0*4/(4000.0**3)))] + q
  538.         l.append((q,8134*4,False))
  539.        
  540.         # sample 5: snare
  541.         q = []
  542.         for i in xrange(650*4):
  543.             q = [(random.randint(-1,1)*120*i*i)/(650*650*4*4)] + q
  544.         l.append((q,8134*2,False))
  545.        
  546.         # sample 6: chord: hypersaw
  547.         # NOTE: this is not a proper hypersaw as it doesn't distort enough.
  548.         #q = []
  549.         #for i in xrange(8363*16):
  550.         #   q.append(int(15*(
  551.         #       self.saw(i*2048+12345,8363*16)*4
  552.         #       + self.saw(i*1024+67890,8363*16)*2
  553.         #       + self.saw(i*512+2467,8363*16)*1
  554.         #       + self.saw(i*4096+5437,8363*16)*1
  555.         #   )))
  556.         # decided to change the waveform.
  557.         # it's now a triangle wave.
  558.         q = [((i-32)*120)/64 for i in xrange(64)]
  559.         l.append((q+q[::-1],8363*4,True))
  560.        
  561.         return l
  562.    
  563.     def refpat(self, ref):
  564.         return self.patterns[self.orders[ref]]
  565.    
  566.     def gen_pat(self, strat, ref = -1):
  567.         p = Pattern(64, strat, ref)
  568.        
  569.         self.strat = strat
  570.         self.ref = ref
  571.        
  572.         for i in xrange(4):
  573.             self.stretch_pos = i
  574.             self.root.initiate()
  575.        
  576.         self.orders.append(len(self.patterns))
  577.         self.patterns.append(p)
  578.         if (strat&255) == STRAT_CHORUS and len(self.pat_chorus) < self.stretch_len:
  579.             self.pat_chorus.append(p)
  580.    
  581.     def gen_pat_round(self, strat):
  582.         choref = self.pat_chorus_ord
  583.         if (strat&255) == STRAT_CHORUS:
  584.             if self.pat_chorus and not (strat & STRAT_FLAGS_COPY):
  585.                 print "rep", self.pat_chorus_base, self.pat_chorus_ord, len(self.orders)
  586.                
  587.                 # just tweak the orderlist and sync the song data
  588.                 for i in xrange(len(self.pat_chorus)):
  589.                     self.orders.append(self.pat_chorus_base+i)
  590.                     for j in xrange(4):
  591.                         for q in [self.lead_data, self.chord_data, self.bass_data, self.beat_data]:
  592.                             if q:
  593.                                 q.append(q[(self.pat_chorus_ord+i)*4+j])
  594.                 return
  595.             else:
  596.                 self.pat_chorus = []
  597.                 self.pat_chorus_ord = len(self.orders)
  598.                 self.pat_chorus_base = len(self.patterns)
  599.        
  600.         for i in xrange(self.stretch_len):
  601.             ts = strat
  602.             ref = -1
  603.            
  604.             if i == 0:
  605.                 ts |= STRAT_FLAGS_START
  606.             else:
  607.                 k = i
  608.                 ref = 1
  609.                 while k and not k&1:
  610.                     ref <<= 1
  611.                     k >>= 1
  612.                
  613.                 ref = len(self.orders)-ref
  614.                
  615.                 ts |= STRAT_FLAGS_RELATE
  616.            
  617.             if strat & STRAT_FLAGS_COMPOTRANSPOSE:
  618.                 if (strat&255) == STRAT_CHORUS:
  619.                     ref = choref+i
  620.                 elif (strat&255) == STRAT_OUTRO:
  621.                     ref = len(self.orders)-self.stretch_len
  622.            
  623.             if (strat&255) == STRAT_OUTRO:
  624.                 ref = len(self.orders)-1
  625.                 ts |= STRAT_FLAGS_RELATE
  626.             elif i == self.stretch_len-1:
  627.                 ts |= STRAT_FLAGS_END
  628.            
  629.             self.gen_pat(ts, ref)
  630.    
  631.     def epictronise(self):
  632.         self.strat_list = [
  633.             STRAT_INTRO,
  634.             STRAT_CHORUS,
  635.             STRAT_VERSE, STRAT_PRECHORUS, STRAT_CHORUS,
  636.             STRAT_VERSE, STRAT_PRECHORUS, STRAT_INTERLUDE, STRAT_CHORUS,
  637.             STRAT_CHORUS|STRAT_FLAGS_COPY|STRAT_FLAGS_COMPOTRANSPOSE,
  638.             STRAT_VERSE, STRAT_PRECHORUS, STRAT_CHORUS,
  639.             STRAT_CHORUS|STRAT_FLAGS_COPY|STRAT_FLAGS_COMPOTRANSPOSE,
  640.             STRAT_OUTRO
  641.         ]
  642.        
  643.         for strat in self.strat_list:
  644.             self.stretch_len = random.choice([2,4])
  645.             if (strat&255) == STRAT_INTRO:
  646.                 self.stretch_len = 2
  647.             elif (strat&255) == STRAT_CHORUS:
  648.                 self.stretch_len = 2
  649.             elif (strat&255) == STRAT_OUTRO:
  650.                 self.stretch_len = 4
  651.            
  652.            
  653.             self.curpat = None
  654.            
  655.             self.gen_pat_round(strat)
  656.        
  657.         l = []
  658.         for i in xrange(len(self.orders)):
  659.             if self.orders[i] in l:
  660.                 continue
  661.            
  662.             l.append(self.orders[i])
  663.             for j in xrange(4):
  664.                 self.root.delegate_spice(self.refpat(i), i*4+j)
  665.        
  666.         last_was_transpose = False
  667.         for p in self.patterns:
  668.             for r in xrange(p.rows):
  669.                 for ch in xrange(64):
  670.                     if self.chn_tonal[ch] and p.data[r][ch][0] != -1 and p.data[r][ch][0] < 120:
  671.                         p.data[r][ch][0] += self.key
  672.            
  673.             if p.strat & STRAT_FLAGS_COPY:
  674.                 op = self.refpat(p.ref)
  675.                 for r in xrange(p.rows):
  676.                     for ch in xrange(64):
  677.                         p.data[r][ch] = op.data[r][ch][:]
  678.            
  679.             if p.strat & STRAT_FLAGS_COMPOTRANSPOSE:
  680.                 for r in xrange(p.rows):
  681.                     for ch in xrange(64):
  682.                         if self.chn_tonal[ch] and p.data[r][ch][0] != -1 and p.data[r][ch][0] < 120:
  683.                             p.data[r][ch][0] += 2
  684.                
  685.                 if not last_was_transpose:
  686.                     self.key += 2
  687.                
  688.                 last_was_transpose = True
  689.             else:
  690.                 last_was_transpose = False
  691.    
  692.     def alloc(self, count, tonal):
  693.         b = self.chnbase
  694.         self.chnbase += count
  695.         for i in xrange(b,self.chnbase,1):
  696.             self.chn_tonal[i] = tonal
  697.         return b
  698.    
  699.     def save(self, fname):
  700.         fp = open(fname, "wb")
  701.         fp.write("IMPM AutoTracker C Module :D \x00")
  702.         fp.write(struct.pack("<HHHHH",0x1004,len(self.orders)+1,0,len(self.samples),len(self.patterns)))
  703.         fp.write(struct.pack("<HHHH",0x0216,0x0200,0x0019,0x0000))
  704.         fp.write(struct.pack("<BBBBBB",128,48,4,125,128,0))
  705.         fp.write(struct.pack("<HII",0,0,0))
  706.         fp.write("\x20"*64)
  707.         fp.write("\x40"*64)
  708.         fp.write(''.join(chr(v) for v in self.orders)+"\xFF")
  709.         patroot = fp.tell()
  710.         fp.write("\x00"*4*(len(self.samples)+len(self.patterns)))
  711.        
  712.         # do it ChibiTracker-style
  713.         for dat,frq,lpd in self.samples:
  714.             t = fp.tell()
  715.             fp.write("IMPSAUTOTRACKERC\x00")
  716.             fp.write(struct.pack("<BBB",64,0x11 if lpd else 0x01,64))
  717.             fp.write("Autotracker C sample     \x00")
  718.             fp.write(struct.pack("<BB",0x01,0x00))
  719.             fp.write(struct.pack("<IIIIIII",len(dat),0,len(dat),frq,0,0,t+0x50))
  720.             fp.write(struct.pack("<BBBB",0x00,0x00,0x00,0x00))
  721.             fp.write(''.join(chr(255&v) for v in dat))
  722.             nt = fp.tell()
  723.             fp.seek(patroot)
  724.             fp.write(struct.pack("<I",t))
  725.             patroot = fp.tell()
  726.             fp.seek(nt)
  727.        
  728.         for p in self.patterns:
  729.             t = fp.tell()
  730.             p.save(fp)
  731.             nt = fp.tell()
  732.             fp.seek(patroot)
  733.             fp.write(struct.pack("<I",t))
  734.             patroot = fp.tell()
  735.             fp.seek(nt)
  736.        
  737.         fp.close()
  738.  
  739. Song().save("dump-c.it" if len(sys.argv) < 2 else sys.argv[1])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement