984 lines
37 KiB
Python
984 lines
37 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2020 Harry Sentonbury
|
|
# GNU General Public License v3.0
|
|
|
|
import clip_dialog
|
|
import o_dialog
|
|
import preset_dialog
|
|
|
|
import numpy as np
|
|
import sounddevice as sd
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
from PyQt5.Qt import Qt
|
|
from PyQt5.QtWidgets import (QMainWindow, QAction, qApp, QApplication, QFrame,
|
|
QListWidget, QLabel, QLineEdit, QPushButton,
|
|
QHBoxLayout, QWidget, QSlider, QVBoxLayout, QShortcut
|
|
)
|
|
from PyQt5.QtGui import QIcon, QKeySequence #, QPixmap
|
|
from PyQt5.QtCore import pyqtSlot
|
|
|
|
import thyaudio as ta
|
|
|
|
|
|
class SliderFrame(QFrame):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.initUI()
|
|
|
|
def initUI(self):
|
|
def build(save=False):
|
|
"""Gets values and selections from gui then plays or saves"""
|
|
def tremelo(): # more of a tremelator!
|
|
trem_adder = 1.0 - trem_amount_value
|
|
if self.tremolo_sin[0] is True:
|
|
return np.sin(x * trem_speed) * trem_amount_value + trem_adder
|
|
else:
|
|
return np.cos(x * trem_speed) * trem_amount_value + trem_adder
|
|
|
|
|
|
def ramp_2_osc():
|
|
return ramp_2 * np.sin(fm * x)
|
|
|
|
def lfo_osc_wave(lfo_shape):
|
|
return lfo_shape() * np.sin(fm * x)
|
|
|
|
def ramp_3_fm2():
|
|
return ramp_3 * np.sin(fm2 * x)
|
|
|
|
def sine_wave(mod):
|
|
y = np.sin(x * freq + mod)
|
|
return y
|
|
|
|
def triangle(mod):
|
|
y = 2 / np.pi * np.arcsin(np.sin(freq * x + mod))
|
|
return y
|
|
|
|
def noise(ramp):
|
|
y = np.random.normal(0, 0.4, np.size(x))
|
|
y = np.clip(y, a_min=-1.0, a_max=1.0) * (ramp * 0.1)
|
|
return y
|
|
|
|
def lfo():
|
|
y = (np.sin(x * speed)) * lfo_amount
|
|
return y
|
|
|
|
def lfo_pluss():
|
|
y = np.sin(x * speed) * 2 + 1
|
|
y = np.clip(y, self.clippings[0], self.clippings[1]) * lfo_amount
|
|
return y
|
|
|
|
def lfo_fold():
|
|
y = np.sin(x * speed)
|
|
y = ta.ThyFoldinger(y, abs(self.foldings[0])).folding()
|
|
y = (ta.ThyFoldinger(y, abs(self.foldings[1]), positive=False).folding() + 1) * lfo_amount
|
|
#print(f"fold max: {np.max(y)} min: {np.min(y)}")
|
|
return y
|
|
|
|
def sweep():
|
|
return (1 - (np.abs(np.sin(x * speed)))) * lfo_amount
|
|
|
|
|
|
global waveform_stereo
|
|
freq = self.freq_slider.value() * 0.1
|
|
fm = self.fm1_slider.value() * 0.1
|
|
fm2 = self.fm2_slider.value() * 0.1
|
|
speed = self.lfo_slider.value() * 0.01
|
|
duration = self.duration_slider.value() / 10
|
|
lfo_amount = self.lfo_amp_slider.value() * 0.01
|
|
ramp_amount = self.ramp_slider.value() * 0.01
|
|
trem_speed = self.tremolo_slider.value() * 0.01
|
|
vol = self.volume_slider.value() * 0.01
|
|
trem_amount_value = self.trem_amp_slider.value() * 0.01
|
|
ramp3_divizor = self.fmt_slider.value() * 0.01
|
|
roller = self.delay_slider.value()
|
|
fade_size = 500 + int(duration * new_samplerate[0] *
|
|
self.fade_out_slider.value() * 0.01)
|
|
fade_in_size = 500 + int(duration * new_samplerate[0] *
|
|
self.fade_in_slider.value() * 0.01)
|
|
noise_shape = self.noise_slider.value() * 0.1
|
|
attenuation = 0.3
|
|
|
|
total_samples = int(duration * new_samplerate[0])
|
|
x = np.linspace(0, duration * 2 * np.pi, total_samples) # f(x)
|
|
|
|
ramp_3 = np.ones(len(x))
|
|
ramp_3_ramp = np.logspace(
|
|
1, 0, int(duration * new_samplerate[0] // ramp3_divizor))
|
|
|
|
ramp_0 = np.logspace(noise_shape, 1, total_samples, base=5)
|
|
ramp_1 = np.logspace(1, noise_shape, total_samples, base=5)
|
|
ramp_2 = np.logspace(0, 1, total_samples) * ramp_amount
|
|
ramp_3[: len(ramp_3_ramp)] = ramp_3_ramp
|
|
fade_ramp = np.linspace(1, 0, fade_size if fade_size < 120000 else 120000)
|
|
fade_in_ramp = np.linspace(0, 1, fade_in_size if fade_in_size < 120000 else 120000)
|
|
|
|
# wave selector
|
|
if self.choose_wave[0] is True:
|
|
if self.choose_fm2[0] is False:
|
|
if self.choose_lfo[0] == 0:
|
|
waveform = triangle(ramp_2_osc())
|
|
elif self.choose_lfo[0] == 1:
|
|
waveform = triangle(lfo_osc_wave(lfo))
|
|
elif self.choose_lfo[0] == 2:
|
|
waveform = triangle(lfo_osc_wave(lfo_pluss))
|
|
elif self.choose_lfo[0] == 3:
|
|
waveform = triangle(lfo_osc_wave(lfo_fold))
|
|
else:
|
|
waveform = triangle(lfo_osc_wave(sweep))
|
|
|
|
if self.choose_fm2[0] is True:
|
|
if self.choose_lfo[0] == 0:
|
|
waveform = 2 / np.pi * np.arcsin(np.sin(x * freq + ramp_2 * 2 / np.pi * np.arcsin(
|
|
np.sin(x * fm + ramp_3_fm2()))))
|
|
elif self.choose_lfo[0] == 1:
|
|
waveform = 2 / np.pi * np.arcsin(np.sin(x * freq + lfo() * 2 / np.pi * np.arcsin(
|
|
np.sin(x * fm + ramp_3_fm2())))) # bollox
|
|
elif self.choose_lfo[0] == 2:
|
|
waveform = 2 / np.pi * np.arcsin(np.sin(x * freq + lfo_pluss() * 2 / np.pi * np.arcsin(
|
|
np.sin(x * fm + ramp_3_fm2())))) # bollox
|
|
elif self.choose_lfo[0] == 3:
|
|
waveform = 2 / np.pi * np.arcsin(np.sin(x * freq + lfo_fold() * 2 / np.pi * np.arcsin(
|
|
np.sin(x * fm + ramp_3_fm2())))) # bollox
|
|
else:
|
|
waveform = 2 / np.pi * np.arcsin(np.sin(x * freq + sweep() * 2 / np.pi * np.arcsin(
|
|
np.sin(x * fm + ramp_3_fm2())))) # bollox
|
|
|
|
if self.choose_wave[0] is False:
|
|
if self.choose_fm2[0] is False:
|
|
if self.choose_lfo[0] == 0:
|
|
waveform = sine_wave(ramp_2_osc())
|
|
elif self.choose_lfo[0] == 1:
|
|
waveform = sine_wave(lfo_osc_wave(lfo))
|
|
elif self.choose_lfo[0] == 2:
|
|
waveform = sine_wave(lfo_osc_wave(lfo_pluss))
|
|
elif self.choose_lfo[0] == 3:
|
|
waveform = sine_wave(lfo_osc_wave(lfo_fold))
|
|
else:
|
|
waveform = sine_wave(lfo_osc_wave(sweep))
|
|
|
|
if self.choose_fm2[0] is True:
|
|
if self.choose_lfo[0] == 0:
|
|
waveform = np.sin(x * freq + ramp_2 * np.sin(
|
|
fm * x + ramp_3_fm2()))
|
|
elif self.choose_lfo[0] == 1:
|
|
waveform = np.sin(x * freq + lfo() * np.sin(
|
|
fm * x + ramp_3_fm2()))
|
|
elif self.choose_lfo[0] == 2:
|
|
waveform = np.sin(x * freq + lfo_pluss() * np.sin(
|
|
fm * x + ramp_3_fm2()))
|
|
elif self.choose_lfo[0] == 3:
|
|
waveform = np.sin(x * freq + lfo_fold() * np.sin(
|
|
fm * x + ramp_3_fm2()))
|
|
else:
|
|
waveform = np.sin(x * freq + sweep() * np.sin(
|
|
fm * x + ramp_3_fm2()))
|
|
|
|
if self.choose_noise[0] == 1:
|
|
waveform = waveform + noise(ramp_1)
|
|
|
|
if self.choose_noise[0] == 2:
|
|
waveform = waveform + noise(ramp_0)
|
|
|
|
if self.choose_noise[0] == 3:
|
|
waveform = waveform + noise((np.hanning(total_samples) * (15 + noise_shape)))
|
|
|
|
if self.choose_trem[0]:
|
|
waveform = waveform * tremelo()
|
|
|
|
if self.phlazing[0] is True:
|
|
waveform = ta.ThyPhlazer(waveform, new_samplerate[0], speed=self.phlaze_speed[0],
|
|
loops=self.phlaze_loops[0], depth=self.phlaze_depth[0]).phlaze()
|
|
|
|
if self.choose_fm2[0]:
|
|
waveform = (waveform / np.max(np.abs(waveform))) * attenuation * vol
|
|
else:
|
|
waveform = (waveform / np.max(np.abs(waveform))) * vol
|
|
waveform[:np.size(fade_in_ramp)] *= fade_in_ramp
|
|
waveform[- np.size(fade_ramp):] *= fade_ramp
|
|
if self.reverse_add[0] == 1:
|
|
waveform = np.concatenate((waveform, np.flip(waveform)))
|
|
if self.reverse_add[0] == 2:
|
|
waveform = np.flip(waveform)
|
|
|
|
|
|
# Split into stereo by delaying right speaker by roller variable amount.
|
|
waveform1 = np.roll(waveform, roller)
|
|
waveform1[:roller] = 0
|
|
waveform1[- 400:] *= np.linspace(1, 0, 400)
|
|
waveform_stereo = np.vstack((waveform, waveform1)).T
|
|
|
|
def stop():
|
|
global sound, sound_cache
|
|
sound = nothing
|
|
sound_cache = nothing
|
|
|
|
def apply_pressed():
|
|
global sound, waveform_stereo, sound_cache
|
|
sound = waveform_stereo
|
|
sound_cache = waveform_stereo
|
|
|
|
def add_pressed():
|
|
global waveform_stereo, sound_cache
|
|
sound_cache = waveform_stereo
|
|
|
|
def loop_pressed():
|
|
loop_flag[0] = not loop_flag[0]
|
|
if loop_flag[0] is True:
|
|
self.loop_button.setText("Looping")
|
|
else:
|
|
self.loop_button.setText("Loop")
|
|
|
|
def tremolo_pressed():
|
|
self.choose_trem[0] = next(tremolo_gen)
|
|
if self.choose_trem[0] == 1:
|
|
self.tremolo_sin[0] = True
|
|
self.trem_button.setText("Tremolo Sin")
|
|
elif self.choose_trem[0] == 0:
|
|
self.trem_button.setText("Tremolo Off")
|
|
else:
|
|
self.tremolo_sin[0] = False
|
|
self.trem_button.setText("Tremolo Cos")
|
|
|
|
|
|
def lfo_pressed():
|
|
self.choose_lfo[0] = next(lfo_gen)
|
|
if self.choose_lfo[0] == 0:
|
|
self.lfo_button.setText("Ramp")
|
|
elif self.choose_lfo[0] == 1:
|
|
self.lfo_button.setText("LFO")
|
|
elif self.choose_lfo[0] == 2:
|
|
self.lfo_button.setText("LFO Clip")
|
|
elif self.choose_lfo[0] == 3:
|
|
self.lfo_button.setText("LFO Fold")
|
|
else:
|
|
self.lfo_button.setText("Sweep")
|
|
|
|
def fm2_pressed():
|
|
self.choose_fm2[0] = not self.choose_fm2[0]
|
|
if self.choose_fm2[0] is True:
|
|
self.fm2_button.setText("FM2 On")
|
|
else:
|
|
self.fm2_button.setText("FM2 Off")
|
|
|
|
def noise_pressed():
|
|
self.choose_noise[0] = next(noise_gen)
|
|
if self.choose_noise[0] == 0:
|
|
self.noise_button.setText("Noise Off")
|
|
elif self.choose_noise[0] == 1:
|
|
self.noise_button.setText("Noise >")
|
|
elif self.choose_noise[0] == 2:
|
|
self.noise_button.setText("Noise <")
|
|
else:
|
|
self.noise_button.setText("Noise ^")
|
|
|
|
def wave_pressed():
|
|
self.choose_wave[0] = not self.choose_wave[0]
|
|
if self.choose_wave[0]:
|
|
self.wave_button.setText("Triangle")
|
|
else:
|
|
self.wave_button.setText("Sin")
|
|
|
|
def reverse_pressed():
|
|
self.reverse_add[0] = next(reverse_gen)
|
|
if self.reverse_add[0] == 1:
|
|
self.reverse_button.setText("And Reverse")
|
|
elif self.reverse_add[0] == 2:
|
|
self.reverse_button.setText("Reversed")
|
|
else:
|
|
self.reverse_button.setText("Not Reversed")
|
|
|
|
|
|
def changed_vol():
|
|
val = self.volume_slider.value() * 0.01
|
|
self.vol_val_label.setText(str("{:3.2f}".format(val)))
|
|
self.vol_val_label.adjustSize()
|
|
|
|
def changed_ramp():
|
|
val = self.ramp_slider.value() * 0.01
|
|
self.ramp_val_label.setText(str('{:3.2f}'.format(val)))
|
|
self.ramp_val_label.adjustSize()
|
|
|
|
def changed_duration():
|
|
val = self.duration_slider.value() * 0.1
|
|
self.duration_val_label.setText(str('{:2.1f}'.format(val)))
|
|
self.duration_val_label.adjustSize()
|
|
|
|
def changed_freq():
|
|
val = self.freq_slider.value() * 0.1
|
|
self.freq_val_label.setText(str('{:4.1f}'.format(val)))
|
|
self.freq_val_label.adjustSize()
|
|
|
|
def changed_fm1():
|
|
val = self.fm1_slider.value() * 0.1
|
|
self.fm1_val_label.setText(str('{:4.1f}'.format(val)))
|
|
self.fm1_val_label.adjustSize()
|
|
|
|
def changed_fm2():
|
|
val = self.fm2_slider.value() * 0.1
|
|
self.fm2_val_label.setText(str('{:4.1f}'.format(val)))
|
|
self.fm2_val_label.adjustSize()
|
|
|
|
def changed_lfo():
|
|
val = self.lfo_slider.value() * 0.01
|
|
self.lfo_val_label.setText(str('{:3.2f}'.format(val)))
|
|
self.lfo_val_label.adjustSize()
|
|
|
|
def changed_lfo_amp():
|
|
val = self.lfo_amp_slider.value() * 0.01
|
|
self.lfo_amp_val_label.setText(str('{:3.2f}'.format(val)))
|
|
self.lfo_amp_val_label.adjustSize()
|
|
|
|
def changed_fmt():
|
|
val = self.fmt_slider.value() * 0.01
|
|
self.fmt_val_label.setText(str('{:3.2f}'.format(val)))
|
|
self.fmt_val_label.adjustSize()
|
|
|
|
def changed_fade_in():
|
|
val = self.fade_in_slider.value() * 0.01
|
|
self.fade_in_val_label.setText(str('{:3.2f}'.format(val)))
|
|
self.fade_in_val_label.adjustSize()
|
|
|
|
def changed_tremolo():
|
|
val = self.tremolo_slider.value() * 0.01
|
|
self.tremolo_val_label.setText(str('{:4.2f}'.format(val)))
|
|
self.tremolo_val_label.adjustSize()
|
|
|
|
def changed_trem_amp():
|
|
val = self.trem_amp_slider.value() * 0.01
|
|
self.trem_amp_val_label.setText(str('{:4.2f}'.format(val)))
|
|
self.trem_amp_val_label.adjustSize()
|
|
|
|
def changed_noise():
|
|
val = self.noise_slider.value() * 0.1
|
|
self.noise_val_label.setText(str('{:3.1f}'.format(val)))
|
|
self.noise_val_label.adjustSize()
|
|
|
|
def changed_delay():
|
|
val = self.delay_slider.value()
|
|
self.delay_val_label.setText(str('{:4.3f}\nSeconds'.format(val / 48000)))
|
|
self.delay_val_label.adjustSize()
|
|
|
|
def changed_fade_out():
|
|
val = self.fade_out_slider.value() * 0.01
|
|
self.fade_out_val_label.setText(str('{:3.2f}'.format(val)))
|
|
self.fade_out_label.adjustSize()
|
|
|
|
def gen_3(start, max=3):
|
|
first_val = 0
|
|
n = start
|
|
while True:
|
|
yield n
|
|
n += 1
|
|
if n == max:
|
|
n = first_val
|
|
|
|
lfo_gen = gen_3(2, max=5)
|
|
noise_gen = gen_3(1, max=4)
|
|
tremolo_gen = gen_3(1, max=3)
|
|
reverse_gen = gen_3(1, max=3)
|
|
self.choose_lfo = [1]
|
|
self.choose_fm2 = [True]
|
|
self.choose_noise = [0]
|
|
self.choose_wave = [False]
|
|
self.choose_trem = [0]
|
|
self.clippings = [0.2, 1.0]
|
|
self.foldings = [0.0, 0.7]
|
|
self.reverse_add = [0]
|
|
self.phlazing = [False]
|
|
self.phlaze_speed = [0.1]
|
|
self.phlaze_loops = [2]
|
|
self.phlaze_depth = [100]
|
|
self.tremolo_sin = [True]
|
|
|
|
# play button
|
|
self.apply_button = QPushButton('Play', self)
|
|
self.apply_button.setStatusTip("Play built sound straight away")
|
|
self.apply_button.setGeometry(430, 10, 90, 70)
|
|
self.apply_button.setObjectName("blue_button")
|
|
self.apply_button.clicked.connect(apply_pressed)
|
|
|
|
self.shortcut_apply = QShortcut(QKeySequence('p'), self)
|
|
self.shortcut_apply.activated.connect(apply_pressed)
|
|
|
|
# build button
|
|
self.build_button = QPushButton("Build", self)
|
|
self.build_button.setStatusTip("Build sound ready to be played or added to loop")
|
|
self.build_button.setGeometry(430, 90, 90, 30)
|
|
self.build_button.clicked.connect(build)
|
|
|
|
self.shortcut_build = QShortcut(QKeySequence('b'), self)
|
|
self.shortcut_build.activated.connect(build)
|
|
|
|
# add button
|
|
self.add_button = QPushButton("Add", self)
|
|
self.add_button.setStatusTip("Add built sound to be played on next loop iteration")
|
|
self.add_button.setGeometry(525, 90, 40, 30)
|
|
self.add_button.clicked.connect(add_pressed)
|
|
|
|
self.shortcut_add = QShortcut(QKeySequence('a'), self)
|
|
self.shortcut_add.activated.connect(add_pressed)
|
|
|
|
# loop button
|
|
self.loop_button = QPushButton("Loop", self)
|
|
self.loop_button.setStatusTip("Repeats sound in a loop")
|
|
self.loop_button.setGeometry(430, 130, 90, 30)
|
|
self.loop_button.clicked.connect(loop_pressed)
|
|
|
|
self.shortcut_loop = QShortcut(QKeySequence('l'), self)
|
|
self.shortcut_loop.activated.connect(loop_pressed)
|
|
|
|
# stop button
|
|
self.stop_button = QPushButton("Stop", self)
|
|
self.stop_button.setGeometry(430, 170, 90, 30)
|
|
self.stop_button.clicked.connect(stop)
|
|
|
|
self.shortcut_stop = QShortcut(QKeySequence('s'), self)
|
|
self.shortcut_stop.activated.connect(stop)
|
|
|
|
# tremelo button
|
|
self.trem_button = QPushButton("Tremolo", self)
|
|
self.trem_button.setGeometry(430, 210, 90, 30)
|
|
self.trem_button.clicked.connect(tremolo_pressed)
|
|
|
|
self.shortcut_tremolo = QShortcut(QKeySequence('t'), self)
|
|
self.shortcut_tremolo.activated.connect(tremolo_pressed)
|
|
|
|
# FM2 button
|
|
self.fm2_button = QPushButton("FM2 On", self)
|
|
self.fm2_button.setStatusTip("Modulate the modulating frequency")
|
|
self.fm2_button.setGeometry(430, 250, 90, 30)
|
|
self.fm2_button.clicked.connect(fm2_pressed)
|
|
|
|
self.shortcut_fm2 = QShortcut(QKeySequence('f'), self)
|
|
self.shortcut_fm2.activated.connect(fm2_pressed)
|
|
|
|
# noise button
|
|
self.noise_button = QPushButton("Noise", self)
|
|
self.noise_button.setStatusTip("Noise fade out at start or fade in at the end")
|
|
self.noise_button.setGeometry(430, 290, 90, 30)
|
|
self.noise_button.clicked.connect(noise_pressed)
|
|
|
|
self.shortcut_noise = QShortcut(QKeySequence('n'), self)
|
|
self.shortcut_noise.activated.connect(noise_pressed)
|
|
|
|
# LFO button
|
|
self.lfo_button = QPushButton("LFO", self)
|
|
self.lfo_button.setStatusTip("Set LFO Clip Clipping values in dialogue from dropdown menu")
|
|
self.lfo_button.setGeometry(430, 330, 90, 30)
|
|
self.lfo_button.clicked.connect(lfo_pressed)
|
|
|
|
self.shortcut_lfo = QShortcut(QKeySequence('o'), self)
|
|
self.shortcut_lfo.activated.connect(lfo_pressed)
|
|
|
|
# wave button
|
|
self.wave_label = QLabel(self)
|
|
self.wave_label.setObjectName("wavetext")
|
|
self.wave_label.setText("Wave Shape")
|
|
self.wave_label.move(625, 38)
|
|
|
|
self.wave_button = QPushButton("Sin", self)
|
|
self.wave_button.setGeometry(730, 30, 110, 30)
|
|
self.wave_button.clicked.connect(wave_pressed)
|
|
|
|
self.shortcut_wave = QShortcut(QKeySequence('w'), self)
|
|
self.shortcut_wave.activated.connect(wave_pressed)
|
|
|
|
|
|
# volume
|
|
self.volume_label = QLabel(self)
|
|
self.volume_label.setText("Volume")
|
|
self.volume_label.move(20, 50)
|
|
|
|
set_vol = 70
|
|
self.volume_slider = QSlider(Qt.Horizontal, self)
|
|
self.volume_slider.setObjectName("fat_slider")
|
|
self.volume_slider.setMinimumWidth(250)
|
|
self.volume_slider.setMinimumHeight(20)
|
|
self.volume_slider.setMinimum(0)
|
|
self.volume_slider.setMaximum(100)
|
|
self.volume_slider.setSingleStep(1)
|
|
self.volume_slider.setValue(set_vol)
|
|
self.volume_slider.move(90, 50)
|
|
self.volume_slider.valueChanged.connect(changed_vol)
|
|
|
|
self.vol_val_label = QLabel(self)
|
|
self.vol_val_label.setText(str("{:3.2f}".format(set_vol * 0.01)))
|
|
self.vol_val_label.move(350, 50)
|
|
|
|
# freq
|
|
self.freq_label = QLabel(self)
|
|
self.freq_label.setText("Freq Hz")
|
|
self.freq_label.move(20, 110)
|
|
|
|
self.set_freq = 3600
|
|
self.freq_slider = QSlider(Qt.Horizontal, self)
|
|
self.freq_slider.setMinimumWidth(250)
|
|
self.freq_slider.setMinimum(100)
|
|
self.freq_slider.setMaximum(5100)
|
|
self.freq_slider.setSingleStep(1)
|
|
self.freq_slider.setValue(self.set_freq)
|
|
self.freq_slider.move(90, 110)
|
|
self.freq_slider.valueChanged.connect(changed_freq)
|
|
|
|
self.freq_val_label = QLabel(self)
|
|
self.freq_val_label.setText(str("{:4.1f}".format(self.set_freq * 0.1)))
|
|
self.freq_val_label.move(350, 110)
|
|
|
|
# fm1
|
|
self.fm1_label = QLabel(self)
|
|
self.fm1_label.setText("FM1 Hz")
|
|
self.fm1_label.move(20, 150)
|
|
|
|
self.set_fm1 = 600
|
|
self.fm1_slider = QSlider(Qt.Horizontal, self)
|
|
self.fm1_slider.setMinimumWidth(250)
|
|
self.fm1_slider.setMinimum(100)
|
|
self.fm1_slider.setMaximum(4000)
|
|
self.fm1_slider.setSingleStep(1)
|
|
self.fm1_slider.setValue(self.set_fm1)
|
|
self.fm1_slider.move(90, 150)
|
|
self.fm1_slider.valueChanged.connect(changed_fm1)
|
|
|
|
self.fm1_val_label = QLabel(self)
|
|
self.fm1_val_label.setText(str("{:4.1f}".format(self.set_fm1 * 0.1)))
|
|
self.fm1_val_label.move(350, 150)
|
|
|
|
# fm2
|
|
self.fm2_label = QLabel(self)
|
|
self.fm2_label.setText("FM2 Hz")
|
|
self.fm2_label.move(20, 190)
|
|
|
|
self.set_fm2 = 3023
|
|
self.fm2_slider = QSlider(Qt.Horizontal, self)
|
|
self.fm2_slider.setMinimumWidth(250)
|
|
self.fm2_slider.setMinimum(100)
|
|
self.fm2_slider.setMaximum(4000)
|
|
self.fm2_slider.setSingleStep(1)
|
|
self.fm2_slider.setValue(self.set_fm2)
|
|
self.fm2_slider.move(90, 190)
|
|
self.fm2_slider.valueChanged.connect(changed_fm2)
|
|
|
|
self.fm2_val_label = QLabel(self)
|
|
self.fm2_val_label.setText(str("{:4.1f}".format(self.set_fm2 * 0.1)))
|
|
self.fm2_val_label.move(350, 190)
|
|
|
|
# lfo speed
|
|
self.lfo_label = QLabel(self)
|
|
self.lfo_label.setText("LFO \nspeed")
|
|
self.lfo_label.move(20, 223)
|
|
|
|
self.set_lfo = 14
|
|
self.lfo_slider = QSlider(Qt.Horizontal, self)
|
|
self.lfo_slider.setMinimumWidth(250)
|
|
self.lfo_slider.setMinimum(5)
|
|
self.lfo_slider.setMaximum(500)
|
|
self.lfo_slider.setSingleStep(1)
|
|
self.lfo_slider.setValue(self.set_lfo)
|
|
self.lfo_slider.move(90, 230)
|
|
self.lfo_slider.valueChanged.connect(changed_lfo)
|
|
|
|
self.lfo_val_label = QLabel(self)
|
|
self.lfo_val_label.setText(str("{:3.2f}".format(self.set_lfo * 0.01)))
|
|
self.lfo_val_label.move(350, 230)
|
|
|
|
# lfo amp
|
|
self.lfo_amp_label = QLabel(self)
|
|
self.lfo_amp_label.setText("LFO Amp")
|
|
self.lfo_amp_label.move(20, 270)
|
|
|
|
self.set_lfo_amp = 180
|
|
self.lfo_amp_slider = QSlider(Qt.Horizontal, self)
|
|
self.lfo_amp_slider.setMinimumWidth(250)
|
|
self.lfo_amp_slider.setMinimum(10)
|
|
self.lfo_amp_slider.setMaximum(500)
|
|
self.lfo_amp_slider.setSingleStep(1)
|
|
self.lfo_amp_slider.setValue(self.set_lfo_amp)
|
|
self.lfo_amp_slider.move(90, 270)
|
|
self.lfo_amp_slider.valueChanged.connect(changed_lfo_amp)
|
|
|
|
self.lfo_amp_val_label = QLabel(self)
|
|
self.lfo_amp_val_label.setText(str("{:3.2f}".format(self.set_lfo_amp * 0.01)))
|
|
self.lfo_amp_val_label.move(350, 270)
|
|
|
|
# ramp_0
|
|
self.ramp_label = QLabel(self)
|
|
self.ramp_label.setText('Ramp')
|
|
self.ramp_label.move(20, 310)
|
|
|
|
self.set3 = 100
|
|
self.ramp_slider = QSlider(Qt.Horizontal, self)
|
|
self.ramp_slider.setMinimumWidth(250)
|
|
self.ramp_slider.setMinimum(10)
|
|
self.ramp_slider.setMaximum(800)
|
|
self.ramp_slider.setSingleStep(1)
|
|
self.ramp_slider.setValue(self.set3)
|
|
self.ramp_slider.move(90, 310)
|
|
self.ramp_slider.valueChanged.connect(changed_ramp)
|
|
|
|
self.ramp_val_label = QLabel(self)
|
|
self.ramp_val_label.setText(str('{:3.2f}'.format(self.set3 * 0.01)))
|
|
self.ramp_val_label.move(350, 310)
|
|
|
|
# fm2 time ratio
|
|
self.fmt_label = QLabel(self)
|
|
self.fmt_label.setText("FM2\nRamp Length")
|
|
self.fmt_label.move(20, 355)
|
|
|
|
self.set_fmt = 130
|
|
self.fmt_slider = QSlider(Qt.Horizontal, self)
|
|
self.fmt_slider.setMinimumWidth(250)
|
|
self.fmt_slider.setMinimum(130)
|
|
self.fmt_slider.setMaximum(1000)
|
|
self.fmt_slider.setSingleStep(1)
|
|
self.fmt_slider.setValue(self.set_fmt)
|
|
self.fmt_slider.move(90, 350)
|
|
self.fmt_slider.valueChanged.connect(changed_fmt)
|
|
|
|
self.fmt_val_label = QLabel(self)
|
|
self.fmt_val_label.setText(str("{:4.2f}".format(self.set_fmt * 0.01)))
|
|
self.fmt_val_label.move(350, 350)
|
|
|
|
# fade in
|
|
self.fade_in_label = QLabel(self)
|
|
self.fade_in_label.setText("Fade In")
|
|
self.fade_in_label.move(555, 166)
|
|
|
|
self.set_fade_in = 0
|
|
self.fade_in_slider = QSlider(Qt.Horizontal, self)
|
|
self.fade_in_slider.setMinimumWidth(200)
|
|
self.fade_in_slider.setMinimum(0)
|
|
self.fade_in_slider.setMaximum(50)
|
|
self.fade_in_slider.setSingleStep(1)
|
|
self.fade_in_slider.setValue(self.set_fade_in)
|
|
self.fade_in_slider.move(620, 170)
|
|
self.fade_in_slider.valueChanged.connect(changed_fade_in)
|
|
|
|
self.fade_in_val_label = QLabel(self)
|
|
self.fade_in_val_label.setText(str("{:3.2f}".format(self.set_fade_in * 0.01)))
|
|
self.fade_in_val_label.move(830, 170)
|
|
|
|
# tremelo speed
|
|
self.tremolo_label = QLabel(self)
|
|
self.tremolo_label.setText("Tremolo\nSpeed")
|
|
self.tremolo_label.move(555, 197)
|
|
|
|
self.set_tremolo = 600
|
|
self.tremolo_slider = QSlider(Qt.Horizontal, self)
|
|
self.tremolo_slider.setMinimumWidth(200)
|
|
self.tremolo_slider.setMinimum(50)
|
|
self.tremolo_slider.setMaximum(1500)
|
|
self.tremolo_slider.setSingleStep(1)
|
|
self.tremolo_slider.setValue(self.set_tremolo)
|
|
self.tremolo_slider.move(620, 210)
|
|
self.tremolo_slider.valueChanged.connect(changed_tremolo)
|
|
|
|
self.tremolo_val_label = QLabel(self)
|
|
self.tremolo_val_label.setText(str("{:4.2f}".format(self.set_tremolo * 0.01)))
|
|
self.tremolo_val_label.move(830, 210)
|
|
|
|
# tremolo amount
|
|
self.trem_amp_label = QLabel(self)
|
|
self.trem_amp_label.setText("Tremolo\n Amp")
|
|
self.trem_amp_label.move(555, 245)
|
|
|
|
self.set_trem_amp = 50
|
|
self.trem_amp_slider = QSlider(Qt.Horizontal, self)
|
|
self.trem_amp_slider.setMinimumWidth(200)
|
|
self.trem_amp_slider.setMinimum(0)
|
|
self.trem_amp_slider.setMaximum(100)
|
|
self.trem_amp_slider.setSingleStep(1)
|
|
self.trem_amp_slider.setValue(self.set_trem_amp)
|
|
self.trem_amp_slider.move(620, 250)
|
|
self.trem_amp_slider.valueChanged.connect(changed_trem_amp)
|
|
|
|
self.trem_amp_val_label = QLabel(self)
|
|
self.trem_amp_val_label.setText(str("{:4.2f}".format(self.set_trem_amp * 0.01)))
|
|
self.trem_amp_val_label.move(830, 250)
|
|
|
|
# noise shape
|
|
self.noise_label = QLabel(self)
|
|
self.noise_label.setText("Noise\nShape")
|
|
self.noise_label.move(555, 287)
|
|
|
|
self.set_noise = -20
|
|
self.noise_slider = QSlider(Qt.Horizontal, self)
|
|
self.noise_slider.setMinimumWidth(200)
|
|
self.noise_slider.setMinimum(-150)
|
|
self.noise_slider.setMaximum(0)
|
|
self.noise_slider.setSingleStep(1)
|
|
self.noise_slider.setValue(self.set_noise)
|
|
self.noise_slider.move(620, 290)
|
|
self.noise_slider.valueChanged.connect(changed_noise)
|
|
|
|
self.noise_val_label = QLabel(self)
|
|
self.noise_val_label.setText(str("{:3.1f}".format(self.set_noise * 0.1)))
|
|
self.noise_val_label.move(830, 290)
|
|
|
|
# delay
|
|
self.delay_label = QLabel(self)
|
|
self.delay_label.setText("Delay")
|
|
self.delay_label.move(555, 333)
|
|
|
|
self.set_delay = 4320
|
|
self.delay_slider = QSlider(Qt.Horizontal, self)
|
|
self.delay_slider.setMinimumWidth(200)
|
|
self.delay_slider.setMinimum(0)
|
|
self.delay_slider.setMaximum(6000)
|
|
self.delay_slider.setSingleStep(1)
|
|
self.delay_slider.setValue(self.set_delay)
|
|
self.delay_slider.move(620, 330)
|
|
self.delay_slider.valueChanged.connect(changed_delay)
|
|
|
|
self.delay_val_label = QLabel(self)
|
|
self.delay_val_label.setText(str("{:4.3f}\nSeconds".format(self.set_delay / 48000)))
|
|
self.delay_val_label.move(830, 323)
|
|
|
|
# fade out
|
|
self.fade_out_label = QLabel(self)
|
|
self.fade_out_label.setText("Fade\n Out")
|
|
self.fade_out_label.move(555, 365)
|
|
|
|
self.set_fade_out = 0
|
|
self.fade_out_slider = QSlider(Qt.Horizontal, self)
|
|
self.fade_out_slider.setMinimumWidth(200)
|
|
self.fade_out_slider.setMinimum(0)
|
|
self.fade_out_slider.setMaximum(50)
|
|
self.fade_out_slider.setSingleStep(1)
|
|
self.fade_out_slider.setValue(self.set_fade_out)
|
|
self.fade_out_slider.move(620, 370)
|
|
self.fade_out_slider.valueChanged.connect(changed_fade_out)
|
|
|
|
self.fade_out_val_label = QLabel(self)
|
|
self.fade_out_val_label.setText(str("{:3.2f}".format(self.set_fade_out * 0.01)))
|
|
self.fade_out_val_label.move(830, 370)
|
|
|
|
|
|
# duration
|
|
self.duration_label = QLabel(self)
|
|
self.duration_label.setText('Duration\nSeconds')
|
|
self.duration_label.move(20, 510)
|
|
|
|
self.set0 = 180
|
|
self.duration_slider = QSlider(Qt.Horizontal, self)
|
|
self.duration_slider.setObjectName("fat_slider")
|
|
self.duration_slider.setMinimumWidth(750)
|
|
self.duration_slider.setMinimumHeight(20)
|
|
self.duration_slider.setMinimum(1)
|
|
self.duration_slider.setMaximum(600)
|
|
self.duration_slider.setValue(self.set0)
|
|
self.duration_slider.setSingleStep(1)
|
|
self.duration_slider.move(90, 510)
|
|
self.duration_slider.valueChanged.connect(changed_duration)
|
|
|
|
self.duration_val_label = QLabel(self)
|
|
self.duration_val_label.setText(str(self.set0 * 0.1))
|
|
self.duration_val_label.move(860, 510)
|
|
|
|
self.reverse_button = QPushButton("Not Reversed", self)
|
|
self.reverse_button.setStatusTip(
|
|
"Add a fliped or reversed version of the sound to the end of each play or loop")
|
|
self.reverse_button.setGeometry(780, 550, 110, 30)
|
|
self.reverse_button.clicked.connect(reverse_pressed)
|
|
|
|
self.shortcut_apply = QShortcut(QKeySequence('r'), self)
|
|
self.shortcut_apply.activated.connect(reverse_pressed)
|
|
|
|
def open_output_dialog():
|
|
global output_dialog
|
|
if output_dialog is None or output_dialog.isVisible() is False:
|
|
output_dialog = o_dialog.OutputDialog(streaming, default_blocksize, new_blocksize,
|
|
output_device, stream_thread, stream_func, new_samplerate)
|
|
output_dialog.show()
|
|
else:
|
|
return
|
|
|
|
|
|
def open_clip_dialog():
|
|
global clipping_dialog
|
|
if clipping_dialog is None or clipping_dialog.isVisible() is False:
|
|
clipping_dialog = clip_dialog.ClipDialog(ex.widget.clippings, ex.widget.foldings,
|
|
ex.widget.phlazing, ex.widget.phlaze_speed,
|
|
ex.widget.phlaze_loops, ex.widget.phlaze_depth)
|
|
clipping_dialog.show()
|
|
else:
|
|
return
|
|
|
|
|
|
|
|
def open_save_presets():
|
|
global save_preset_window
|
|
if save_preset_window is None or save_preset_window.isVisible() is False:
|
|
settings_list = [
|
|
ex.widget.freq_slider.value(),
|
|
ex.widget.fm1_slider.value(),
|
|
ex.widget.fm2_slider.value(),
|
|
ex.widget.lfo_slider.value(),
|
|
ex.widget.duration_slider.value(),
|
|
ex.widget.lfo_amp_slider.value(),
|
|
ex.widget.ramp_slider.value(),
|
|
ex.widget.tremolo_slider.value(),
|
|
ex.widget.volume_slider.value(),
|
|
ex.widget.trem_amp_slider.value(),
|
|
ex.widget.fmt_slider.value(),
|
|
ex.widget.delay_slider.value(),
|
|
ex.widget.fade_out_slider.value(),
|
|
ex.widget.fade_in_slider.value(),
|
|
ex.widget.noise_slider.value(),
|
|
ex.widget.choose_lfo[0], # int
|
|
ex.widget.choose_fm2[0], # bool
|
|
ex.widget.choose_noise[0], # int
|
|
ex.widget.choose_wave[0], # bool
|
|
ex.widget.choose_trem[0], # bool
|
|
ex.widget.clippings[0],
|
|
ex.widget.clippings[1],
|
|
ex.widget.reverse_add[0],
|
|
ex.widget.foldings[0],
|
|
ex.widget.foldings[1],
|
|
ex.widget.phlazing[0],
|
|
ex.widget.phlaze_speed[0],
|
|
ex.widget.phlaze_loops[0], # int
|
|
ex.widget.phlaze_depth[0] # int
|
|
]
|
|
# print(f"settings_list {settings_list}")
|
|
save_preset_window = preset_dialog.PresetSaveDialog(settings_list)
|
|
save_preset_window.show()
|
|
else:
|
|
return
|
|
|
|
|
|
def open_recall_preset():
|
|
global recall_presets
|
|
if recall_presets is None or recall_presets.isVisible() is False:
|
|
recall_presets = preset_dialog.PresetRecallDialog(ex.widget)
|
|
recall_presets.show()
|
|
else:
|
|
return
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
|
def __init__(self):
|
|
super(MainWindow, self).__init__()
|
|
|
|
self.exitAc = QAction(QIcon('exit.png'), '&Exit', self)
|
|
self.exitAc.setShortcut('Ctrl+Q')
|
|
self.exitAc.setStatusTip('Exit Application')
|
|
self.exitAc.triggered.connect(qApp.quit)
|
|
|
|
self.op_dialog_ac = QAction("Set Output Device, Blocksize and Samplerate", self)
|
|
self.op_dialog_ac.setShortcut('Ctrl+o')
|
|
self.op_dialog_ac.setStatusTip("Select output device and set a blocksize or select a new samplerate for output stream")
|
|
self.op_dialog_ac.triggered.connect(open_output_dialog)
|
|
|
|
self.clip_dialog_ac = QAction("Set LFO Clipping or Folding Values and Phlaze", self)
|
|
self.clip_dialog_ac.setShortcut('Ctrl+d')
|
|
self.clip_dialog_ac.setStatusTip("Set LFO sin wave clipping or Folding low and high values. activate Phlazing")
|
|
self.clip_dialog_ac.triggered.connect(open_clip_dialog)
|
|
|
|
self.save_preset_ac = QAction("Save As Presets", self)
|
|
self.save_preset_ac.setShortcut('Ctrl+s')
|
|
self.save_preset_ac.setStatusTip("Open dialogue to save settings as presets")
|
|
self.save_preset_ac.triggered.connect(open_save_presets)
|
|
|
|
self.recall_presets_ac = QAction("Recall Presets")
|
|
self.recall_presets_ac.setShortcut('Ctrl+r')
|
|
self.recall_presets_ac.setStatusTip("Open dialogue to recall setting presets from a file")
|
|
self.recall_presets_ac.triggered.connect(open_recall_preset)
|
|
|
|
|
|
self.status_bar = self.statusBar()
|
|
self.status_bar.setObjectName("status_bar_obj")
|
|
|
|
|
|
self.menubar = self.menuBar()
|
|
self.settingsMenu = self.menubar.addMenu('Settings, Exit')
|
|
self.settingsMenu.addAction(self.exitAc)
|
|
self.settingsMenu.addAction(self.op_dialog_ac)
|
|
self.settingsMenu.addAction(self.clip_dialog_ac)
|
|
self.settingsMenu.addAction(self.save_preset_ac)
|
|
self.settingsMenu.addAction(self.recall_presets_ac)
|
|
self.menubar.setObjectName("menubar_obj")
|
|
|
|
self.setWindowTitle("The Audio FM Mummer")
|
|
self.setWindowIcon(QIcon('images/scope-icon.jpg'))
|
|
|
|
self.setGeometry(20, 100, 930, 640)
|
|
|
|
self.widget = SliderFrame()
|
|
self.setCentralWidget(self.widget)
|
|
|
|
self.show()
|
|
|
|
|
|
def stream_func(device=-1, blocksize=256, rate=48000):
|
|
def callback(outdata, frames, time, status):
|
|
try:
|
|
data = next(sound_slice)
|
|
outdata[:, :] = data
|
|
except ValueError:
|
|
outdata[:, :] = np.zeros((blocksize, 2))
|
|
|
|
|
|
def gen():
|
|
global sound, sound_cache
|
|
sound = np.zeros((blocksize, 2))
|
|
while True:
|
|
slice = sound[:blocksize, :]
|
|
yield slice
|
|
sound = sound[blocksize:, :]
|
|
if loop_flag[0] is True and np.size(sound) < (1024):
|
|
sound = np.vstack((sound, sound_cache))
|
|
|
|
|
|
sound_slice = gen()
|
|
try:
|
|
device = device if device >= 0 else None
|
|
except TypeError:
|
|
device = None
|
|
|
|
print(f"Samplerate: {rate}")
|
|
stream = sd.OutputStream(device=device,
|
|
channels=2, callback=callback, blocksize=blocksize, samplerate=rate)
|
|
with stream:
|
|
# stopping stream, dont forget assign streaming bool add args to thread
|
|
while streaming[0] is True:
|
|
time.sleep(0.5)
|
|
else:
|
|
stream.__exit__()
|
|
|
|
|
|
#tremolo_sin = [True]
|
|
loop_flag = [False]
|
|
streaming = [True]
|
|
nothing = np.zeros((2048, 2))
|
|
sound_cache = np.zeros((2048, 2))
|
|
waveform_stereo = np.zeros((2048, 2))
|
|
output_device = [-1]
|
|
output_dialog = None
|
|
clipping_dialog = None
|
|
save_preset_window = None
|
|
recall_presets = None
|
|
|
|
sample_rate = 48000
|
|
new_samplerate = [48000]
|
|
blocksize = 256
|
|
default_blocksize = 256
|
|
new_blocksize = [default_blocksize]
|
|
|
|
stream_thread = threading.Thread(target=stream_func, args=[-1, default_blocksize], daemon=True)
|
|
stream_thread.start()
|
|
|
|
app = QApplication(sys.argv)
|
|
with open("styles.css", 'r') as f:
|
|
styles = f.read()
|
|
app.setStyleSheet(styles)
|
|
ex = MainWindow()
|
|
|
|
sys.exit(app.exec_())
|