Files
audio-playground/audio_python/sound.py
T
2026-10-27 22:10:43 +01:00

69 lines
1.6 KiB
Python

import math
import struct
from itertools import chain
from operator import mul
from random import random
from player import AplaySink
SAMPLE_RATE = 44100
def sin(f, amplitude, duration):
samples = (amplitude*math.sin(2*math.pi*f*(t/SAMPLE_RATE)) for t in range(int(duration*SAMPLE_RATE)))
return samples
def square(f, amplitude, duration):
period = SAMPLE_RATE / f
half_period = period / 2
for t in range(int(duration*SAMPLE_RATE)):
if t % period < half_period:
yield amplitude
else:
yield -amplitude
def noise(amplitude, duration):
return (amplitude*(2*random()-1) for _ in range(int(duration*SAMPLE_RATE)))
def envelope(samples, attack_duration, release_duration):
# TODO: rewrite ADSR envelope
def compose(a, b):
return map(lambda x: mul(*x), zip(a, b))
a = int(attack_duration * SAMPLE_RATE)
r = int(release_duration * SAMPLE_RATE)
samples = list(samples)
attack = (x/a for x in range(0, a))
sustain = len(samples) - a - r
release = (x/r for x in range(r, -1, -1))
return chain(
compose(samples[:a], attack),
samples[a:a+sustain],
compose(samples[a+sustain:], release))
def mix(*sources):
return (sum(x) for x in zip(*sources))
def pack(samples) -> bytes:
return b"".join(struct.pack("<f", sample) for sample in samples)
def main():
with AplaySink(SAMPLE_RATE) as sink:
data = mix(
sin(440, 0.05, 1),
sin(440*2**(4/12), 0.05, 1),
sin(440*2**(7/12), 0.05, 1))
packed = pack(data)
sink.submit(packed)
if __name__ == "__main__":
main()