Skip to content

Instantly share code, notes, and snippets.

@lynn
Created December 26, 2019 03:52
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save lynn/0d67efb2da73c490386865ee6597f32f to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
#
# Listen to justly-tuned progressions.
#
# python3.8 ji.py 1+5:4+7:4 1+4:3+5:3 1+5:4+6:4 1+5:4+7:5 3:4+9:8+4:3 1:2+3:4+5:4 | aplay -r44 -f S16_LE
# python3.8 ji.py 1+5:4+3:2 15:16+9:8+3:2 1+5:4+3:2 | ffmpeg -f s16le -ar 44.1k -ac 1 -i - out.mp3
#
import struct
import sys
from math import *
def parse_ratio(string):
if ':' in string:
a, b = string.split(':')
return float(a) / float(b)
return float(string)
sample_rate = 44100.0
def sample(amplitude: float) -> int:
return int(max(-1, min(1, amplitude)) * 32767)
def organ(t: float) -> float:
t *= 2*pi
return sin(t) * 0.1 + sin(2*t) * 0.01 + sin(3*t) * 0.001
def emit(seconds: float, func) -> bytes:
samples = int(sample_rate * seconds)
soft_func = lambda t: func(t) * min(1, 50*t, 50*(1-t)) # avoid clicky audio
return struct.pack(f'<{samples}h', *(sample(soft_func(i / sample_rate)) for i in range(samples)))
if __name__ == '__main__':
if len(sys.argv) <= 1:
sys.exit(f'usage: {sys.argv[0]} 1+5:4+3:2 15:16+9:8+3:2 1+5:4+3:2')
base = 220
if sys.argv[1] == '-b':
base = float(sys.argv[2])
del sys.argv[1:3]
for chord in sys.argv[1:]:
sound = lambda t: sum(organ(base*parse_ratio(note)*t) for note in chord.split('+'))
sys.stdout.buffer.write(emit(1.0, sound))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment