Pipe stdin to soundcard using SDL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

synth.py 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #Copyright 2017 Yann Weber
  2. #
  3. #This file is part of SndPipe.
  4. #
  5. #SndPipe is free software: you can redistribute it and/or modify
  6. #it under the terms of the GNU General Public License as published by
  7. #the Free Software Foundation, either version 3 of the License, or
  8. #any later version.
  9. #
  10. #SndPipe is distributed in the hope that it will be useful,
  11. #but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. #GNU General Public License for more details.
  14. #
  15. #You should have received a copy of the GNU General Public License
  16. #along with SndPipe. If not, see <http://www.gnu.org/licenses/>.
  17. import math
  18. import sys
  19. import struct
  20. BITMAX = lambda bsz: (1<<bsz)-1
  21. def sinwave_float32(step=0.01, endianess='little'):
  22. i = 0.0
  23. if endianess is None:
  24. e = ''
  25. elif endianess == 'little':
  26. e = '<'
  27. else:
  28. e = '>'
  29. while True:
  30. i += step
  31. i %= (math.pi*2)
  32. yield struct.pack('%cf'%e, math.sin(i))
  33. def sinwave(step=0.01, bitsz=8, signed=False):
  34. i = 0.0
  35. while True:
  36. i += step
  37. i %= (math.pi*2)
  38. if signed:
  39. yield int(math.sin(i) * (BITMAX(bitsz)/2))
  40. else:
  41. yield int((math.sin(i)+1)/2 * BITMAX(bitsz))
  42. def mixer(*waves):
  43. channels = []
  44. for wave in waves:
  45. if isinstance(wave, tuple):
  46. wave, vol = wave
  47. else:
  48. wave, vol = wave, 1.0
  49. channels.append((wave, vol/len(waves)))
  50. while True:
  51. yield int(sum([next(wave)*vol for wave, vol in channels]))
  52. def filter(wave, freq=10, high=True, glitch=False, bitsz=8):
  53. hist = [next(wave)]
  54. while True:
  55. c = next(wave)
  56. r = int(sum(hist)/len(hist))
  57. if high:
  58. if glitch:
  59. r = (c - r) % BITMAX(bitsz)
  60. elif c < r:
  61. r = 0
  62. else:
  63. r = c - r
  64. hist = [c] + (hist if len(hist) < freq else hist[:-1])
  65. yield r
  66. def lowpass(wave, freq=500):
  67. return filter(wave, freq, False)
  68. def highpass(wave, freq=500):
  69. return filter(wave, freq, True)
  70. def vol_lfo(wave, wave_arg, bitsz=8):
  71. while True:
  72. yield int(next(wave) * next(wave_arg) / BITMAX(bitsz))
  73. def output_sampler(*args, channels=2, samples=4096, freq=48000, bitsz=8,
  74. endianess='little', signed=False):
  75. if channels < 1:
  76. raise ValueError("channels must be > 1 but %d given" % channels)
  77. if len(args) == 0:
  78. raise ValueError("You must give at least one generator")
  79. if len(args) > 1 and channels != len(args):
  80. msg = "If more than one generator given, the count must match the \
  81. channel argument. But %d generator given and channels is %d"
  82. raise ValueError(msg % (len(args), channels))
  83. if len(args) == 1:
  84. if channels == 1:
  85. nxt_sample = lambda waves: [next(waves[0])]
  86. else:
  87. nxt_sample = lambda waves: [next(waves[0])]*2
  88. else:
  89. nxt_sample = lambda waves: map(next, waves)
  90. if bitsz != 8 or signed:
  91. nxt_arg = lambda: nxt_sample(args)
  92. gen = _output_sampler_multibits(nxt_arg, channels, samples, freq, bitsz,
  93. endianess, signed)
  94. while True:
  95. yield next(gen)
  96. raise StopIteration()
  97. return
  98. buff = bytearray([0 for _ in range(samples*channels)])
  99. while True:
  100. for i in range(0,samples*channels, channels):
  101. buff[i:i+channels] = nxt_sample(args)
  102. yield buff
  103. def _output_sampler_multibits(nxt_sample, channels, samples, freq, bitsz,
  104. endianess, signed):
  105. if bitsz not in (8,16,32):
  106. raise ValueError("Bitsize must be in (8,16,32) but %d given" % bitsz)
  107. Bsz = bitsz//8
  108. buff = bytearray([0 for _ in range(samples * channels * Bsz)])
  109. while True:
  110. for i in range(0, samples*channels*Bsz, channels*Bsz):
  111. nxt = nxt_sample()
  112. for j in range(channels):
  113. st = i + (j*Bsz)
  114. buff[st:st+Bsz] = nxt[j].to_bytes(Bsz, endianess,
  115. signed=signed)
  116. yield buff