sonnum/core/program.py
2025-08-10 16:16:16 +03:00

133 lines
No EOL
2.7 KiB
Python

import os
import wave
from .room import Room
class Program:
NOTES_OF_OCTAVE = {
0: ['C', 'B#'],
1: ['C#', 'Db'],
2: ['D'],
3: ['D#', 'Eb'],
4: ['E', 'Fb'],
5: ['F', 'E#'],
6: ['F#', 'Gb'],
7: ['G'],
8: ['G#', 'Ab'],
9: ['A'],
10: ['A#', 'Bb'],
11: ['B', 'Cb']
}
def __init__(self, name):
self.name = name
self.room = Room()
self.volume = 0.1
self.actions = []
def reset(self):
self.room = Room()
def note_to_freq(self, note):
note_no = self.note_to_note_no(note)
return self.note_no_to_freq(note_no)
def note_to_note_no(self, note):
octave = int(note[-1])
note = note[:-1]
note_no_oct = 1
for note_no_oct, notelst in self.NOTES_OF_OCTAVE.items():
if note in notelst:
break
corr_note_no_oct = note_no_oct - 8
return corr_note_no_oct + (octave*12)
def note_no_to_freq(self, note_no):
return 2**((note_no-49) / 12.0) * 440
def st(self, t_sec):
# seconds to ticks
return self.room.sample_rate * t_sec
def generate_frames(self, start_t_sec, end_t_sec):
frames = []
start_t = int(start_t_sec * self.room.sample_rate)
end_t = int(end_t_sec * self.room.sample_rate)
for t in range(start_t, end_t):
self.tick(t)
left_frame, right_frame = self.room.generate_frame(t)
frames.append(left_frame)
frames.append(right_frame)
return frames
def write_frames_to_wavefile(self, frames, fn = 'export.wav'):
with wave.open(fn, 'wb') as wav:
wav.setnchannels(2)
wav.setsampwidth(self.room.sample_width)
wav.setframerate(self.room.sample_rate)
wav.writeframes(b''.join(frames))
def playback(self, start_t_sec, end_t_sec, fn = 'export.wav'):
cmd = f'play -q --volume {self.volume} {fn} trim {start_t_sec} {end_t_sec-start_t_sec}'
os.system(cmd)
def export(self, start_t_sec, end_t_sec, fn = "export.wav"):
frames = self.generate_frames(start_t_sec, end_t_sec)
self.write_frames_to_wavefile(frames, fn=fn)
def interface(self):
print(f'\nRunning program: {self.name}')
print(f'[g 10] to generate 10 seconds')
print(f'[vol 10] to set playback volume to 10%')
print(f'[p 1 5] to play seconds 1 to 5')
cmd = ''
while cmd != 'q':
cmd = input('\n[vol {self.volume*100}%]>> ')
if cmd.startswith('g '):
end_sec = float(cmd[2:])
self.export(0, end_sec)
if cmd.startswith('vol '):
vol = float(cmd[4:])
self.volume = vol / 100.0
if cmd.startswith('p'):
pp = cmd.split(' ')
start_sec = float(pp[1])
end_sec = float(pp[2])
self.playback(start_sec, end_sec)
def tick(self, t):
for action in self.actions:
action.tick(t)
# REIMPLEMENT THESE
def setup(self):
pass