OffiTracker/offitracker.py
2024-01-05 21:06:15 +01:00

125 lines
4.9 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
# This has started off as a silly little joke program, I never thought it would turn into such a complex little beast of a python project.
# (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, start_row=None, stop_row=None):
global stop_signal
global noise_data_cache
if 'playback_row_index' in locals():
global playback_row_index
# 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
total_rows = sum(1 for _ in csv_reader) # Count the total number of rows
# Reset the file pointer to the beginning
csv_file.seek(0)
next(csv_reader) # Skip the header
with sd.OutputStream(channels=1) as output_stream:
for idx, row in enumerate(csv_reader):
if start_row is not None and idx < start_row:
continue
if stop_row is not None and idx > stop_row:
break
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))
# Update row info
if 'playback_row_index' in globals():
playback_row_index = idx
else:
print(f"\rRow {idx + 1} of {total_rows}", end='', flush=True)
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.4')
if len(sys.argv) > 1:
csv_file_path = sys.argv[1]
else:
csv_file_path = input("Choose a CSV file: ")
# These should not be set in player mode
start_row = None
stop_row = None
play_csv_file(csv_file_path, start_row=start_row, stop_row=stop_row)
print("\nPlayback complete.")