voice.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. def norm_theta(theta):
  37. return theta % (2*math.pi)
  38. def norm_amp(amp):
  39. return min(1.0, max(-1.0, amp))
  40. def theta2lin(theta):
  41. return theta / (2*math.pi)
  42. def lin2theta(lin):
  43. return lin * 2*math.pi
  44. class ParamInfo(object):
  45. PT_ANY = 0x0000
  46. PT_CONST = 0x0001
  47. PT_SPECIAL = 0x0002
  48. PT_INT = 0x0100
  49. PT_FLOAT = 0x0200
  50. PT_STR = 0x0400
  51. PT_THETA = 0x0102
  52. PT_TIME_SEC = 0x0202
  53. PT_SAMPLES = 0x0302
  54. PT_REALTIME = 0x0402
  55. def __init__(self, name, tp=PT_ANY):
  56. self.name = name
  57. self.tp = tp
  58. class GenInfo(object):
  59. def __init__(self, name, *params):
  60. self.name = name
  61. self.params = list(params)
  62. class Generator(object):
  63. class __metaclass__(type):
  64. def __init__(self
  65. class Voice(object):
  66. @classmethod
  67. def register_gen(cls, name, params):
  68. def __init__(self, generator=None, envelope=None):
  69. self.generator = generator or self.DEFAULT_GENERATOR
  70. self.envelope = envelope or self.DEFAULT_ENVELOPE
  71. self.phase = 0
  72. self.freq = None
  73. def __call__(self, theta):
  74. return norm_amp(self.generator(norm_theta(theta)))
  75. @staticmethod
  76. def phase_vel(freq, samp_rate):
  77. return 2 * math.pi * freq / samp_rate
  78. def samples(self, frames, pvel):
  79. for i in xrange(frames):
  80. yield self(self.phase)
  81. self.phase = norm_theta(self.phase + pvel)
  82. def data(self, frames, freq, samp_rate):
  83. period = 1.0/samp_rate
  84. status = freq is not None
  85. for samp in self.samples(frames, self.phase_vel(freq, samp_rate)):
  86. yield samp * self.envelope(status, period)
  87. def pyaudio_scb(self, rate, fmt=pyaudio.paInt16):
  88. samp_size = pyaudio.get_sample_size(fmt)
  89. maxint = (1 << (8*samp_size)) - 1
  90. dtype = ['!', 'h', 'i', '!', 'l', '!', '!', '!', 'q'][samp_size]
  91. def __callback(data, frames, time, status, self=self, rate=rate, maxint=maxint, dtype=dtype):
  92. return struct.pack(dtype*frames, *[maxint*int(i) for i in self.data(frames, self.freq, rate)])
  93. return __callback
  94. class VMeanMixer(Voice):
  95. def __init__(self, *voices):
  96. self.voices = list(voices)
  97. def __call__(self, theta):
  98. return sum([i(theta)/len(self.voices) for i in self.voices])
  99. class VSumMixer(Voice):
  100. def __init__(self, *voices):
  101. self.voices = list(voices)
  102. def __call__(self, theta):
  103. return sum([i(theta) for i in self.voices])