voice.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. '''
  2. voice -- Voices
  3. A voice is a simple, singular unit of sound generation that encompasses the following
  4. properties:
  5. -A *generator*: some function that generates a waveform. As an input, it receives theta,
  6. the phase of the signal it is to generate (in [0, 2pi)) and, as an output, it produces
  7. the sample at that point, a normalized amplitude value in [-1, 1].
  8. -An *envelope*: a function that receives a boolean (the status of whether or not a note
  9. is playing now) and the change in time, and outputs a factor in [0, 1] that represents
  10. a modification to the volume of the generator (pre-output mix).
  11. All of these functions may internally store state or other data, usually by being
  12. implemented as a class with a __call__ method.
  13. Voices are meant to generate audio data. This can be done in a number of ways, least to
  14. most abstracted:
  15. -A sample at a certain phase (theta) may be gotten from the generator; this can be done
  16. by calling the voice outright;
  17. -A set of samples can be generated via the .samples() method, which receives the number
  18. of samples to generate and the phase velocity (a function of the sample rate and the
  19. desired frequency of the waveform's period; this can be calculated using the static
  20. method .phase_vel());
  21. -Audio data with enveloping can be generated using the .data() method, which calls the
  22. envelope function as if the note is depressed at the given phase velocity; if the
  23. freq is specified as None, then the note is treated as released. Note that
  24. this will often be necessary for envelopes, as many of them are stateful (as they
  25. depend on the first derivative of time). Also, at this level, the Voice will maintain
  26. some state (namely, the phase at the end of generation) which will ensure (C0) smooth
  27. transitions between already smooth generator functions, even if the frequency changes.
  28. -Finally, a pyaudio-compatible stream callback can be provided with .pyaudio_scb(), a
  29. method that returns a function that arranges to call .data() with the appropriate values.
  30. The freq input to .data() will be taken from the .freq member of the voice in a possibly
  31. non-atomic manner.
  32. '''
  33. import math
  34. import pyaudio
  35. import struct
  36. import time
  37. def norm_theta(theta):
  38. return theta % (2*math.pi)
  39. def norm_amp(amp):
  40. return min(1.0, max(-1.0, amp))
  41. def theta2lin(theta):
  42. return theta / (2*math.pi)
  43. def lin2theta(lin):
  44. return lin * 2*math.pi
  45. class Voice(object):
  46. @classmethod
  47. def register_gen(cls, name, params):
  48. def __init__(self, generator=None, envelope=None):
  49. self.generator = generator or self.DEFAULT_GENERATOR
  50. self.envelope = envelope or self.DEFAULT_ENVELOPE
  51. self.phase = 0
  52. self.freq = None
  53. def __call__(self, theta):
  54. return norm_amp(self.generator(norm_theta(theta)))
  55. @staticmethod
  56. def phase_vel(freq, samp_rate):
  57. return 2 * math.pi * freq / samp_rate
  58. def samples(self, frames, pvel):
  59. for i in xrange(frames):
  60. yield self(self.phase)
  61. self.phase = norm_theta(self.phase + pvel)
  62. def data(self, frames, freq, samp_rate):
  63. period = 1.0/samp_rate
  64. status = freq is not None
  65. for samp in self.samples(frames, self.phase_vel(freq, samp_rate)):
  66. yield samp * self.envelope(status, period)
  67. def pyaudio_scb(self, rate, fmt=pyaudio.paInt16):
  68. samp_size = pyaudio.get_sample_size(fmt)
  69. maxint = (1 << (8*samp_size)) - 1
  70. dtype = ['!', 'h', 'i', '!', 'l', '!', '!', '!', 'q'][samp_size]
  71. def __callback(data, frames, time, status, self=self, rate=rate, maxint=maxint, dtype=dtype):
  72. return struct.pack(dtype*frames, *[maxint*int(i) for i in self.data(frames, self.freq, rate)])
  73. return __callback
  74. class VMeanMixer(Voice):
  75. def __init__(self, *voices):
  76. self.voices = list(voices)
  77. def __call__(self, theta):
  78. return norm_amp(sum([i(theta)/len(self.voices) for i in self.voices]))
  79. class VSumMixer(Voice):
  80. def __init__(self, *voices):
  81. self.voices = list(voices)
  82. def __call__(self, theta):
  83. return norm_amp(sum([i(theta) for i in self.voices]))
  84. class VLFOMixer(Voice):
  85. def __init__(self, lfo, *voices):
  86. self.lfo = lfo
  87. self.voices = list(voices)
  88. def __call__(self, theta):
  89. i = int(len(self.voices) * self.lfo * (time.time() % (1.0 / self.lfo)))
  90. return self.voices[i](theta)