112 lines
4.2 KiB
Python
112 lines
4.2 KiB
Python
import sys
|
|
import csv
|
|
import numpy as np
|
|
import sounddevice as sd
|
|
import os
|
|
|
|
# OffiTracker, the tracker that no one asked for but I made it anyways :3
|
|
# Usage: Make a CSV table in Excel or LibreOffice with the following format:
|
|
# Frequency1 Effect1 Frequency2 Effect2 .... Noise Duration
|
|
# You can make as many channels as you want.
|
|
# Effect = pulse width from 0 to 100
|
|
# Frequency = tone in Hz.
|
|
# Noise:
|
|
# - 0 = No extra sound
|
|
# - 1 = Bass drum
|
|
# - 2 = Kick drum
|
|
# - 3 = Click
|
|
# - 4 = Snare
|
|
# - 5 = Hihat
|
|
# Duration = tone duration in ms
|
|
# (c) 2024 mueller_minki, Feel free to modify or share.
|
|
stop_signal = False
|
|
|
|
noise_data_cache = {} # Cache to store loaded noise data
|
|
|
|
def load_noise_data(noise_type, sample_rate):
|
|
amplitude_factor = 0.5 # Adjust the amplitude factor as needed
|
|
noise_file_path = os.path.join('drums', f'drum{noise_type}.txt')
|
|
|
|
try:
|
|
with open(noise_file_path, 'r') as file:
|
|
noise_data = np.array(eval(file.readline()))
|
|
return amplitude_factor * noise_data
|
|
except Exception as e:
|
|
print(f"Error loading noise data from {noise_file_path}: {e}")
|
|
return None
|
|
|
|
def load_all_noise_data():
|
|
global noise_data_cache
|
|
for i in range(1, 6):
|
|
noise_data_cache[i] = load_noise_data(i, 44100)
|
|
|
|
def generate_noise(noise_type):
|
|
return noise_data_cache.get(noise_type, None)
|
|
|
|
def play_square_waves(output_stream, frequencies, effects, duration, amplitude=1, noise_amplitude=0, sample_rate=44100):
|
|
global stop_signal
|
|
if stop_signal:
|
|
output_stream.stop()
|
|
else:
|
|
num_waves = len(frequencies)
|
|
t = np.linspace(0, duration / 1000, int(sample_rate * duration / 1000), endpoint=False)
|
|
|
|
# Generate and sum square waves for each frequency with corresponding effects
|
|
waves = [amplitude * (effect / 100) * np.sign(np.sin(2 * np.pi * freq * t)) for freq, effect in zip(frequencies, effects)]
|
|
|
|
# Add optional noise channel based on the noise column values
|
|
if noise_amplitude > 0:
|
|
noise_type = int(noise_amplitude)
|
|
noise = generate_noise(noise_type)
|
|
|
|
if noise is not None:
|
|
# Pad the noise with zeros to match the duration of the other waves
|
|
noise = np.concatenate((noise, np.zeros(len(t) - len(noise))))
|
|
waves.append(noise)
|
|
|
|
combined_wave = np.sum(waves, axis=0)
|
|
combined_wave = combined_wave.astype(np.float32)
|
|
|
|
output_stream.write(combined_wave)
|
|
|
|
def play_csv_file(file_path):
|
|
global stop_signal
|
|
global noise_data_cache
|
|
|
|
# Load all noise data into the cache
|
|
load_all_noise_data()
|
|
|
|
with open(file_path, 'r') as csv_file:
|
|
csv_reader = csv.DictReader(csv_file)
|
|
header = csv_reader.fieldnames
|
|
num_columns = len(header)
|
|
num_pairs = (num_columns - 1) // 2
|
|
|
|
with sd.OutputStream(channels=1) as output_stream:
|
|
for row in csv_reader:
|
|
frequencies = [float(row[f'Frequency{i}']) for i in range(1, num_pairs + 1)]
|
|
effects = [float(row[f'Effect{i}']) for i in range(1, num_pairs + 1)]
|
|
duration = float(row['Duration'])
|
|
|
|
# Check if 'Noise' column exists in the CSV file
|
|
noise_amplitude = float(row.get('Noise', 0))
|
|
if stop_signal == False:
|
|
play_square_waves(output_stream, frequencies, effects, duration, noise_amplitude=noise_amplitude)
|
|
|
|
if __name__ == "__main__":
|
|
print(' ')
|
|
print(' Mueller\'s Software Domain proudly presents:')
|
|
print('________ _____ _____._____________ __ ')
|
|
print('\_____ \_/ ____\/ ____\__\__ ___/___________ ____ | | __ ___________ ')
|
|
print(' / | \ __\\\\ __\| | | | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \\')
|
|
print('/ | \ | | | | | | | | | \// __ \\\\ \___| <\ ___/| | \/')
|
|
print('\_______ /__| |__| |__| |____| |__| (____ /\___ >__|_ \\\\___ >__| ')
|
|
print(' \/ \/ \/ \/ \/ ')
|
|
print(' Version 1.3')
|
|
if len(sys.argv) > 1:
|
|
csv_file_path = sys.argv[1]
|
|
else:
|
|
csv_file_path = input("Choose a CSV file: ")
|
|
play_csv_file(csv_file_path)
|
|
|