Graham Northup 7 ani în urmă
părinte
comite
ec177bb81a
2 a modificat fișierele cu 52 adăugiri și 16 ștergeri
  1. 45 13
      client.py
  2. 7 3
      mkiv.py

+ 45 - 13
client.py

@@ -32,6 +32,7 @@ parser.add_option('-c', '--clamp', dest='clamp', action='store_true', help='Clam
 parser.add_option('-C', '--chorus', dest='chorus', default=0.0, type='float', help='Apply uniform random offsets (in MIDI pitch space)')
 parser.add_option('--vibrato', dest='vibrato', default=0.0, type='float', help='Apply periodic perturbances in pitch space by this amplitude (in MIDI pitches)')
 parser.add_option('--vibrato-freq', dest='vibrato_freq', default=6.0, type='float', help='Frequency of the vibrato perturbances in Hz')
+parser.add_option('--fmul', dest='fmul', default=1.0, type='float', help='Multiply requested frequencies by this amount')
 parser.add_option('--pg-fullscreen', dest='fullscreen', action='store_true', help='Use a full-screen video mode')
 parser.add_option('--pg-samp-width', dest='samp_width', type='int', help='Set the width of the sample pane (by default display width / 2)')
 parser.add_option('--pg-bgr-width', dest='bgr_width', type='int', help='Set the width of the bargraph pane (by default display width / 2)')
@@ -72,6 +73,9 @@ DRIFT_FACTOR = 1.0
 DRIFT_ERROR = 0.0
 LAST_SYN = None
 
+CUR_PERIODS = [0] * STREAMS
+CUR_PERIOD = 0.0
+
 def lin_interp(frm, to, p):
     return p*to + (1-p)*frm
 
@@ -245,23 +249,41 @@ def square_wave(theta):
 def noise(theta):
     return random.random() * 2 - 1
 
-@generator('File generator', '(<file>[, <bits=8>[, <signed=True>[, <0=linear interp (default), 1=nearest>[, <swapbytes=False>]]]])')
+@generator('File generator', '(<file>[, <bits=8>[, <signed=True>[, <0=linear interp (default), 1=nearest>[, <swapbytes=False>[, <loop=(fraction to loop, 0.0 is all, 1.0 is end, or False to not loop)>[, <loopend=1.0>[, periods=1 (periods in wave file)/freq=None (base frequency)/pitch=None (base MIDI pitch)]]]]]]])')
 class file_samp(object):
     LINEAR = 0
     NEAREST = 1
     TYPES = {8: 'B', 16: 'H', 32: 'L'}
-    def __init__(self, fname, bits=8, signed=True, samp=LINEAR, swab=False):
+    def __init__(self, fname, bits=8, signed=True, samp=LINEAR, swab=False, loop=0.0, loopend=1.0, periods=1.0, freq=None, pitch=None):
         tp = self.TYPES[bits]
         if signed:
             tp = tp.lower()
         self.max = float((2 << bits) - 1)
+        if signed:
+            self.max /= 2.0
         self.buffer = array.array(tp)
         self.buffer.fromstring(open(fname, 'rb').read())
         if swab:
             self.buffer.byteswap()
         self.samp = samp
+        self.loop = loop
+        self.loopend = loopend
+        self.periods = periods
+        if pitch is not None:
+            freq = 440.0 * 2 ** ((pitch - 69) / 12.0)
+        if freq is not None:
+            self.periods = freq * len(self.buffer) / RATE
+        print 'file_samp periods:', self.periods, 'freq:', freq, 'pitch:', pitch
     def __call__(self, theta):
-        norm = theta / (2*math.pi)
+        full_norm = CUR_PERIOD / (2*self.periods*math.pi)
+        if full_norm > 1.0:
+            if self.loop is False:
+                return self.buffer[0]
+            else:
+                norm = (full_norm - 1.0) / (self.loopend - self.loop) % 1.0 * (self.loopend - self.loop) + self.loop
+        else:
+            norm = full_norm
+        norm %= 1.0
         if self.samp == self.LINEAR:
             v = norm*len(self.buffer)
             l = int(math.floor(v))
@@ -411,13 +433,14 @@ if options.numpy:
         return numpy.linspace(frm, to, cnt, dtype=numpy.int32)
 
     def samps(freq, amp, phase, cnt):
+        global CUR_PERIOD
         samps = numpy.ndarray((cnt,), numpy.int32)
         pvel = 2 * math.pi * freq / RATE
         fac = options.volume * amp / float(STREAMS)
         for i in xrange(cnt):
-            samps[i] = fac * max(-1, min(1, generator(phase)))
-            phase = (phase + pvel) % (2 * math.pi)
-        return samps, phase
+            samps[i] = fac * max(-1, min(1, generator((phase + i * pvel) % (2*math.pi))))
+            CUR_PERIOD += pvel
+        return samps, phase + pvel * cnt
 
     def to_data(samps):
         return samps.tobytes()
@@ -439,11 +462,13 @@ else:
         return samps
 
     def samps(freq, amp, phase, cnt):
-        global RATE
+        global RATE, CUR_PERIOD
         samps = [0]*cnt
         for i in xrange(cnt):
-            samps[i] = int(2*amp / float(STREAMS) * max(-1, min(1, options.volume*generator((phase + 2 * math.pi * freq * i / RATE) % (2*math.pi)))))
-        return samps, (phase + 2 * math.pi * freq * cnt / RATE) % (2*math.pi)
+            samps[i] = int(amp / float(STREAMS) * max(-1, min(1, options.volume*generator((phase + 2 * math.pi * freq * i / RATE) % (2*math.pi)))))
+            CUR_PERIOD += 2 * math.pi * freq / RATE
+        next_phase = (phase + 2 * math.pi * freq * cnt / RATE)
+        return samps, next_phase
 
     def to_data(samps):
         return struct.pack('i'*len(samps), *samps)
@@ -468,7 +493,7 @@ else:
         return struct.pack(str(amt)+'i', *out)
 
 def gen_data(data, frames, tm, status):
-    global FREQS, PHASE, Z_SAMP, LAST_SAMP, LAST_SAMPLES, QUEUED_PCM, DRIFT_FACTOR, DRIFT_ERROR
+    global FREQS, PHASE, Z_SAMP, LAST_SAMP, LAST_SAMPLES, QUEUED_PCM, DRIFT_FACTOR, DRIFT_ERROR, CUR_PERIOD
     if len(QUEUED_PCM) >= frames*4:
         desired_frames = DRIFT_FACTOR * frames
         err_frames = desired_frames - int(desired_frames)
@@ -497,6 +522,7 @@ def gen_data(data, frames, tm, status):
         AMP = AMPS[i]
         EXPIRATION = EXPIRATIONS[i]
         PHASE = PHASES[i]
+        CUR_PERIOD = CUR_PERIODS[i]
         if FREQ != 0:
             if time.time() > EXPIRATION:
                 FREQ = 0
@@ -507,9 +533,11 @@ def gen_data(data, frames, tm, status):
                 fdata = mix(fdata, vdata)
                 LAST_SAMPS[i] = vdata[-1]
         else:
-            vdata, PHASE = samps(FREQ, AMP, PHASE, frames)
+            vdata, CUR_PERIOD = samps(FREQ, AMP, CUR_PERIOD, frames)
+            PHASE = (PHASE + CUR_PERIOD) % (2 * math.pi)
             fdata = mix(fdata, vdata)
             PHASES[i] = PHASE
+            CUR_PERIODS[i] = CUR_PERIOD
             LAST_SAMPS[i] = vdata[-1]
     if options.gui:
         LAST_SAMPLES.extend(fdata)
@@ -526,14 +554,17 @@ if options.gui:
 if options.test:
     FREQS[0] = 440
     EXPIRATIONS[0] = time.time() + 1
+    CUR_PERIODS[0] = 0.0
     time.sleep(1)
     FREQS[0] = 0
     time.sleep(1)
     FREQS[0] = 880
     EXPIRATIONS[0] = time.time() + 1
+    CUR_PERIODS[0] = 0.0
     time.sleep(1)
     FREQS[0] = 440
     EXPIRATIONS[0] = time.time() + 2
+    CUR_PERIODS[0] = 0.0
     time.sleep(2)
     exit()
 
@@ -567,12 +598,13 @@ while True:
     elif pkt.cmd == CMD.PLAY:
         voice = pkt.data[4]
         dur = pkt.data[0]+pkt.data[1]/1000000.0
-        freq = pkt.data[2]
+        freq = pkt.data[2] * options.fmul
         if options.chorus > 0:
             midi = 12 * math.log(freq / 440.0, 2) + 69
             midi += (random.random() * 2 - 1) * options.chorus
             freq = 440.0 * 2 ** ((midi - 69) / 12)
         FREQS[voice] = freq
+        CUR_PERIODS[voice] = 0.0
         amp = pkt.as_float(3)
         if options.clamp:
             amp = max(min(amp, 1.0), 0.0)
@@ -588,7 +620,7 @@ while True:
         if pkt.data[5] & PLF.SAMEPHASE:
             print '\x1b[1;37mSAMEPHASE',
         if pkt.data[0] == 0 and pkt.data[1] == 0:
-            print '\x1b[1;35mSTOP!!!'
+            print '\x1b[1;31mSTOP!!!'
         else:
             print '\x1b[1;36mDUR', '%08.6f'%dur
         #signal.setitimer(signal.ITIMER_REAL, dur)

+ 7 - 3
mkiv.py

@@ -27,6 +27,7 @@ parser.add_option('-p', '--program-split', dest='tracks', action='append_const',
 parser.add_option('-P', '--percussion', dest='perc', help='Which percussion standard to use to automatically filter to "perc" (GM, GM2, or none)')
 parser.add_option('-f', '--fuckit', dest='fuckit', action='store_true', help='Use the Python Error Steamroller when importing MIDIs (useful for extended formats)')
 parser.add_option('-v', '--verbose', dest='verbose', action='store_true', help='Be verbose; show important parts about the MIDI scheduling process')
+parser.add_option('-q', '--quiet', dest='quiet', action='store_true', help='Be quiet; don\'t log certain high-volume outputs')
 parser.add_option('-d', '--debug', dest='debug', action='store_true', help='Debugging output; show excessive output about the MIDI scheduling process (please use less or write to a file)')
 parser.add_option('-D', '--deviation', dest='deviation', type='int', help='Amount (in semitones/MIDI pitch units) by which a fully deflected pitchbend modifies the base pitch (0 disables pitchbend processing)')
 parser.add_option('-M', '--modwheel-freq-dev', dest='modfdev', type='float', help='Amount (in semitones/MIDI pitch unites) by which a fully-activated modwheel modifies the base pitch')
@@ -485,7 +486,8 @@ for fname in args:
                 if found:
                     break
             else:
-                print 'WARNING: Did not match %r with any stream deactivation.'%(mev,)
+                if not options.quiet:
+                    print 'WARNING: Did not match %r with any stream deactivation.'%(mev,)
                 if options.verbose:
                     print '  Current state:'
                     for group in notegroups:
@@ -503,7 +505,8 @@ for fname in args:
                         stream.Activate(base, base.ev.pitch + options.deviation * (mev.ev.pitch / float(0x2000)), parent=old)
                         found = True
             if not found:
-                print 'WARNING: Did not find any matching active streams for %r'%(mev,)
+                if not options.quiet:
+                    print 'WARNING: Did not find any matching active streams for %r'%(mev,)
                 if options.verbose:
                     print '  Current state:'
                     for group in notegroups:
@@ -521,7 +524,8 @@ for fname in args:
                         stream.Activate(base, stream.bentpitch, mev.mw, parent=old)
                         found = True
             if not found:
-                print 'WARNING: Did not find any matching active streams for %r'%(mev,)
+                if not options.quiet:
+                    print 'WARNING: Did not find any matching active streams for %r'%(mev,)
                 if options.verbose:
                     print '  Current state:'
                     for group in notegroups: