diff --git a/README.md b/README.md index ccbb7ff..804e9da 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,30 @@ # OffiTracker -Ever wanted to make music in Excel? Now you can! \ No newline at end of file +Ever wanted to make music in Excel? Now you can! + +OffiTracker lets you use CSV spreadsheets to create chiptunes. Its as simple as typing in the frequency, pulse width and duration. + +![Screenshot of a spreadsheet program showing tracker data](img/excel.png) +![The softwares CLI](img/cli.png) + +## Usage + +Running OffiTracker requires the following packages to be present on your system: + +`python3, python3-csv, pyhon3-numpy, python3-sounddevice` + +Assuming `python3` is already installed, you can use the following command to install the dependencies: + +```bash +pip3 install csv numpy sounddevice +``` + +After that, simply run `python3 offitracker.py` and enjoy. + + + +____ + +Examples can be found in the `example` folder. + +For usage information, check the contents of the `offitracker.py` file diff --git a/example/mario-levelcleared.csv b/example/mario-levelcleared.csv new file mode 100644 index 0000000..e7bb840 --- /dev/null +++ b/example/mario-levelcleared.csv @@ -0,0 +1,28 @@ +Frequency1,Effect1,Duration +130,50,100 +262,50,100 +330,50,100 +392,50,100 +523,50,100 +660,50,100 +784,50,300 +660,50,300 +146,50,100 +262,50,100 +311,50,100 +415,50,100 +523,50,100 +622,50,100 +831,50,300 +622,50,300 +155,50,100 +294,50,100 +349,50,100 +466,50,100 +588,50,100 +699,50,100 +933,50,300 +933,50,100 +933,50,100 +933,50,100 +1047,50,400 diff --git a/example/test2.csv b/example/test2.csv new file mode 100644 index 0000000..cac034f --- /dev/null +++ b/example/test2.csv @@ -0,0 +1,3 @@ +Frequency1,Effect1,Frequency2,Effect2,Frequency3,Effect3,Noise,Duration +440,50,247,10,311,70,0,500 +440,25,247,10,311,40,5,500 diff --git a/img/cli.png b/img/cli.png new file mode 100644 index 0000000..eecd963 Binary files /dev/null and b/img/cli.png differ diff --git a/img/excel.png b/img/excel.png new file mode 100644 index 0000000..c70570b Binary files /dev/null and b/img/excel.png differ diff --git a/offitracker.py b/offitracker.py new file mode 100644 index 0000000..2e7ca87 --- /dev/null +++ b/offitracker.py @@ -0,0 +1,60 @@ +import csv +import numpy as np +import sounddevice as sd + +# 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 = noise amplitude from 0 to 10 +# Duration = tone duration in ms +# (c) 2024 mueller_minki, Feel free to modify or share. + +def play_square_waves(frequencies, effects, duration, amplitude=1, noise_amplitude=0, sample_rate=44100): + 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 + if noise_amplitude > 0: + noise = noise_amplitude * np.random.uniform(-1, 1, len(t)) + waves.append(noise) + + combined_wave = np.sum(waves, axis=0) + + sd.play(combined_wave, sample_rate, blocking=True) + +def play_csv_file(file_path): + 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 + + 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)) + + play_square_waves(frequencies, effects, duration, noise_amplitude=noise_amplitude) + + +if __name__ == "__main__": + print(' ') + print(' Mueller\'s Software Domain proudly presents:') + print('________ _____ _____._____________ __ ') + print('\_____ \_/ ____\/ ____\__\__ ___/___________ ____ | | __ ___________ ') + print(' / | \ __\\\\ __\| | | | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \\') + print('/ | \ | | | | | | | | | \// __ \\\\ \___| <\ ___/| | \/') + print('\_______ /__| |__| |__| |____| |__| (____ /\___ >__|_ \\\\___ >__| ') + print(' \/ \/ \/ \/ \/ ') + csv_file_path = input("Choose a CSV file: ") + play_csv_file(csv_file_path) +