Compare commits
No commits in common. "main" and "main" have entirely different histories.
12
README.md
12
README.md
|
@ -7,10 +7,6 @@ OffiTracker lets you use CSV spreadsheets to create chiptunes. Its as simple as
|
|||
![Screenshot of a spreadsheet program showing tracker data](img/excel.png)
|
||||
![The softwares CLI](img/cli.png)
|
||||
|
||||
Here is an example of how this might sound:
|
||||
|
||||
https://git.muellers-software.org/Minki/OffiTracker/raw/branch/main/example/demo1.mp3
|
||||
|
||||
## Usage
|
||||
|
||||
Running OffiTracker requires the following packages to be present on your system:
|
||||
|
@ -23,12 +19,6 @@ Assuming `python3` is already installed, you can use the following command to in
|
|||
pip3 install csv numpy sounddevice
|
||||
```
|
||||
|
||||
If you need the GUI, additionally install PySimpleGui:
|
||||
|
||||
```bash
|
||||
pip3 install PySimpleGui
|
||||
```
|
||||
|
||||
After that, simply run `python3 offitracker.py` and enjoy. The command can also take a path to a file as a parameter if you wish to use your shells autocompletion.
|
||||
|
||||
____
|
||||
|
@ -37,4 +27,4 @@ Examples can be found in the `example` folder.
|
|||
|
||||
A utility for converting midi files to csv can be found in the utility folder (monophonic only).
|
||||
|
||||
Documentation on how to use offitracker as a library and on creating songs compatible with offitracker can be found in the `docs` folder.
|
||||
For usage information, check the contents of the `offitracker.py` file
|
||||
|
|
Binary file not shown.
|
@ -1,73 +0,0 @@
|
|||
# Creating songs in the OffiTracker format
|
||||
|
||||
This document contains a quick tutorial on how to use the different features of the OffiTracker format to create music.
|
||||
|
||||
## Spreadsheets galore
|
||||
|
||||
The first step for your song is to create a new spreadsheet a program of your choice.
|
||||
|
||||
It should be in the CSV format with comma separated columns.
|
||||
|
||||
## Channel your creativity
|
||||
|
||||
Like any tracker, OffiTracker uses multiple channels for playing different tones at once.
|
||||
|
||||
Currently we only support square waves but this is subject to change soon. We will keep compatibility with existing songs however by making square waves the default.
|
||||
|
||||
Each channel has a `Frequency` and an `Effect` column. `Frequency` is the notes frequency in Hz and `Effect` the pulse width of your square wave.
|
||||
|
||||
You start counting your channels at 1 and can add as many as you like, just note that having more than 8 channels may result in bad audio quality or artifacting.
|
||||
|
||||
Example:
|
||||
|
||||
| Frequency1 | Effect1 | Frequency2 | Effect2 |
|
||||
| ---------- | ------- | ---------- | ------- |
|
||||
| 440 | 50 | 318 | 35 |
|
||||
|
||||
## A matter of time
|
||||
|
||||
The example above can obviously not work by itself yet as there is no way of knowing for how long to play the notes.
|
||||
|
||||
For this, you use the `Duration` column.
|
||||
|
||||
This column is recommended to be the furthest right one for consistency.
|
||||
|
||||
`Duration` stores the time your row is played for in milliseconds.
|
||||
|
||||
Example:
|
||||
|
||||
| Frequency1 | Effect1 | Frequency2 | Effect2 | Duration |
|
||||
| ---------- | ------- | ---------- | ------- | -------- |
|
||||
| 440 | 50 | 318 | 35 | 80 |
|
||||
| 519 | 50 | 411 | 28 | 114 |
|
||||
|
||||
## Noisy company
|
||||
|
||||
Having square waves is all fun and games but a good song also has drums.
|
||||
|
||||
Introducing: The `Noise` column.
|
||||
|
||||
The `Noise` column unlike the previously shown columns is optional. You do not need to include it in your file if you don't want to use it.
|
||||
|
||||
There are 5 different noises that you can play back at the start of your row:
|
||||
|
||||
1. Bass drum
|
||||
|
||||
2. Kick drum
|
||||
|
||||
3. Click
|
||||
|
||||
4. Snare
|
||||
|
||||
5. Hihat
|
||||
|
||||
A value of 0 or no value will mean that no noise is played.
|
||||
|
||||
Example:
|
||||
|
||||
| Frequency1 | Effect1 | Frequency2 | Effect2 | Noise | Duration |
|
||||
| ---------- | ------- | ---------- | ------- | ----- | -------- |
|
||||
| 440 | 50 | 318 | 35 | 0 | 80 |
|
||||
| 519 | 50 | 411 | 28 | 3 | 114 |
|
||||
|
||||
Noises have their own duration ranging from long to short depending on the noise. In case the duration set in the `Duration` column is shorter than the noise, the noise will be cut off.
|
|
@ -1,91 +0,0 @@
|
|||
# OffiTracker as a python library
|
||||
|
||||
The OffiTracker program is designed in a way that allows it to be integrated in other projects by importing it.
|
||||
|
||||
A reference design for this use case can be found in the `offiplayergui.py` file.
|
||||
|
||||
## Importing
|
||||
|
||||
The OffiTracker library can be imported by putting the line
|
||||
|
||||
```python
|
||||
import offitracker
|
||||
```
|
||||
|
||||
in the head of your python project. For this to work, you need to place the `offitracker.py` file in your projects directory as well as the `drums` folder.
|
||||
|
||||
## The stop signal
|
||||
|
||||
Before we start playing anything, it would be useful to know how to stop the playback again.
|
||||
|
||||
For that, OffiTracker has a `stop_signal` variable which we can change from outside.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
import offitracker as oftr
|
||||
|
||||
# Set stop signal to False to allow for playback
|
||||
oftr.stop_signal = False
|
||||
|
||||
# Set stop signal to True to stop playback
|
||||
oftr.stop_signal = True
|
||||
```
|
||||
|
||||
## Playing a csv file
|
||||
|
||||
Playing a csv file in the OffiTracker format can be done using the `play_csv_file` function.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
import offitracker as oftr
|
||||
|
||||
# Set stop signal to False to allow for playback
|
||||
oftr.stop_signal = False
|
||||
# Start playing back a file
|
||||
oftr.play_csv_file("example.csv")
|
||||
```
|
||||
|
||||
## Playback position, threading
|
||||
|
||||
The `play_csv_file` function has an optional `playback_row_index` variable that stores the currently playing row. If the variable is not initialized, the function will print a status message to stdout. Initializing it will disable that message and instead store the value which is useful when writing more complex software around the library.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
import offitracker as oftr
|
||||
import threading
|
||||
import time
|
||||
|
||||
def playback_thread(csv_file_path):
|
||||
# Set stop signal to False to allow for playback
|
||||
oftr.stop_signal = False
|
||||
# Initialise playback row index
|
||||
oftr.playback_row_index = 0
|
||||
# Start playing back the file
|
||||
oftr.play_csv_file(csv_file_path)
|
||||
|
||||
# Example CSV file path
|
||||
csv_file_path = "example.csv"
|
||||
|
||||
# Create a thread for playback
|
||||
playback_thread = threading.Thread(target=playback_thread, args=(csv_file_path,))
|
||||
|
||||
try:
|
||||
# Start the playback thread
|
||||
playback_thread.start()
|
||||
|
||||
while not oftr.stop_signal:
|
||||
# Read the current row index
|
||||
current_row_index = oftr.playback_row_index
|
||||
print(f"Current Row Index: {current_row_index}")
|
||||
time.sleep(1) # Adjust the sleep duration as needed
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Set stop signal to True to stop playback when Ctrl+C is pressed
|
||||
oftr.stop_signal = True
|
||||
playback_thread.join() # Wait for the playback thread to finish
|
||||
|
||||
print("Playback stopped.")
|
||||
```
|
File diff suppressed because one or more lines are too long
BIN
drums/drum1.wav
BIN
drums/drum1.wav
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
drums/drum2.wav
BIN
drums/drum2.wav
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
[0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 1.4296875, 1.421875, 1.421875, 1.421875, 1.421875, 1.4375, 0.9921875, 0.9921875, 0.9921875, 0.9921875, 0.984375, 0.984375, 0.984375, 0.984375, 0.9765625, 0.9765625, 0.9765625, 0.96875, 0.96875, 0.96875, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.9609375, 0.953125, 0.953125, 0.9453125, 0.9453125, 0.9453125, 0.9375, 0.9375, 0.9375, 0.9296875, 0.921875, 0.921875, 0.921875, 0.90625, 0.90625, 0.90625, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.8984375, 0.890625, 0.890625, 0.8828125, 0.8828125, 0.875, 0.875, 0.8671875, 0.8671875, 0.859375, 0.859375, 0.859375, 0.859375, 0.859375, 0.859375, 0.8515625, 0.8515625, 0.8515625, 0.8515625, 0.8515625, 0.8515625, 0.8515625, 0.8515625, 0.84375, 0.84375, 0.8359375, 0.828125, 0.8203125, 0.8203125, 0.8125, 0.8125, 0.8046875, 0.796875, 0.796875, 0.7890625, 0.78125, 0.7734375, 0.765625, 0.75, 0.7421875, 0.734375, 0.7265625, 0.71875, 0.7109375, 0.6953125, 0.6875, 0.671875, 0.6640625, 0.65625, 0.640625, 0.6328125, 0.6171875, 0.609375, 0.59375, 0.578125, 0.5625, 0.546875, 0.5390625, 0.53125, 0.5078125, 0.4921875, 0.484375, 0.4765625, 0.46875, 0.453125, 0.4375, 0.421875, 0.40625, 0.390625, 0.3828125, 0.375, 0.3671875, 0.3515625, 0.34375, 0.328125, 0.3203125, 0.3125, 0.296875, 0.2890625, 0.2734375, 0.265625, 0.25, 0.2421875, 0.2265625, 0.21875, 0.203125, 0.1953125, 0.1875, 0.171875, 0.1640625, 0.1484375, 0.140625, 0.1328125, 0.125, 0.1015625, 0.0859375, 0.078125, 0.078125, 0.0703125, 0.0546875, 0.046875, 0.03125, 0.015625, 0.015625, 0.0078125, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875, 0.0, 1.9921875, 0.0, 0.0, 1.9921875]
|
BIN
drums/drum3.wav
BIN
drums/drum3.wav
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
drums/drum4.wav
BIN
drums/drum4.wav
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
drums/drum5.wav
BIN
drums/drum5.wav
Binary file not shown.
|
@ -1,64 +0,0 @@
|
|||
import os
|
||||
import wave
|
||||
import numpy as np
|
||||
|
||||
def save_noise_data(file_path, noise_data):
|
||||
try:
|
||||
with open(file_path, 'w') as file:
|
||||
file.write(str(noise_data.tolist()))
|
||||
print(f"Noise data saved to {file_path}")
|
||||
except Exception as e:
|
||||
print(f"Error saving noise data to {file_path}: {e}")
|
||||
|
||||
def extract_noise_data(file_path, duration_ms=100, sample_rate=44100):
|
||||
try:
|
||||
with wave.open(file_path, 'rb') as wav_file:
|
||||
num_channels = wav_file.getnchannels()
|
||||
sample_width = wav_file.getsampwidth()
|
||||
frame_rate = wav_file.getframerate()
|
||||
total_frames = wav_file.getnframes()
|
||||
|
||||
if num_channels > 1:
|
||||
print("Input file must be mono (single channel).")
|
||||
return
|
||||
|
||||
# Adjust for 8-bit audio
|
||||
if sample_width == 1:
|
||||
dtype = np.uint8
|
||||
elif sample_width == 2:
|
||||
dtype = np.int16
|
||||
else:
|
||||
print("Unsupported sample width.")
|
||||
return
|
||||
|
||||
# Use the minimum of specified duration and actual duration of the file
|
||||
duration_secs = min(duration_ms / 1000.0, total_frames / frame_rate)
|
||||
num_frames = int(duration_secs * frame_rate)
|
||||
|
||||
signal = np.frombuffer(wav_file.readframes(num_frames), dtype=dtype)
|
||||
|
||||
# Normalize the signal for 8-bit audio
|
||||
if sample_width == 1:
|
||||
signal = (signal - 128) / 128.0
|
||||
|
||||
return signal
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
directory_path = input("Enter the path to the directory containing WAV files (press Enter for current directory): ").strip() or '.'
|
||||
duration_ms = float(input("Enter the maximum duration in milliseconds: "))
|
||||
|
||||
for file_name in os.listdir(directory_path):
|
||||
if file_name.endswith(".wav"):
|
||||
input_wave_file = os.path.join(directory_path, file_name)
|
||||
output_noise_data = extract_noise_data(input_wave_file, duration_ms)
|
||||
|
||||
if output_noise_data is not None:
|
||||
output_file_name = os.path.splitext(file_name)[0] + ".txt"
|
||||
save_noise_data(output_file_name, output_noise_data)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
Frequency1,Effect1,Frequency2,Effect2,Duration
|
||||
294,60,147,30,165
|
||||
294,51,73,30,165
|
||||
294,60,147,30,165
|
||||
294,47,73,30,165
|
||||
330,60,147,30,165
|
||||
330,60,73,30,165
|
||||
349,60,147,30,165
|
||||
349,56,73,30,165
|
||||
233,49,117,30,165
|
||||
233,47,58,30,165
|
||||
262,60,117,30,165
|
||||
294,49,58,30,165
|
||||
294,48,117,30,165
|
||||
294,49,58,30,165
|
||||
330,55,117,30,165
|
||||
330,42,58,30,165
|
||||
349,51,175,30,165
|
||||
349,59,87,30,165
|
||||
349,58,175,30,165
|
||||
349,59,87,30,165
|
||||
440,40,175,30,165
|
||||
440,57,87,30,165
|
||||
349,55,175,30,165
|
||||
349,60,87,30,165
|
||||
392,44,131,30,165
|
||||
392,57,65,30,165
|
||||
349,51,131,30,165
|
||||
330,55,65,30,165
|
||||
330,58,131,30,165
|
||||
330,60,65,30,165
|
||||
349,58,131,30,165
|
||||
349,43,65,30,165
|
||||
294,56,147,30,165
|
||||
294,52,73,30,165
|
||||
294,60,147,30,165
|
||||
294,55,73,30,165
|
||||
330,60,147,30,165
|
||||
330,60,73,30,165
|
||||
349,54,147,30,165
|
||||
349,60,73,30,165
|
||||
392,60,117,30,165
|
||||
392,55,58,30,165
|
||||
349,46,117,30,165
|
||||
330,60,58,30,165
|
||||
330,50,117,30,165
|
||||
330,55,58,30,165
|
||||
392,43,117,30,165
|
||||
392,52,58,30,165
|
||||
349,50,87,30,165
|
||||
349,42,44,30,165
|
||||
349,48,87,30,165
|
||||
349,56,44,30,165
|
||||
440,51,87,30,165
|
||||
440,60,44,30,165
|
||||
349,41,87,30,165
|
||||
349,57,44,30,165
|
||||
392,43,131,30,165
|
||||
392,48,65,30,165
|
||||
349,41,131,30,165
|
||||
392,40,65,30,165
|
||||
392,54,131,30,165
|
||||
392,59,65,30,165
|
||||
440,60,131,30,165
|
||||
440,58,65,30,165
|
||||
294,45,147,30,165
|
||||
294,54,73,30,165
|
||||
294,60,147,30,165
|
||||
294,55,73,30,165
|
||||
330,60,147,30,165
|
||||
330,45,73,30,165
|
||||
349,60,147,30,165
|
||||
349,53,73,30,165
|
||||
233,41,117,30,165
|
||||
233,60,58,30,165
|
||||
262,60,117,30,165
|
||||
294,57,58,30,165
|
||||
294,49,117,30,165
|
||||
294,53,58,30,165
|
||||
330,60,117,30,165
|
||||
330,40,58,30,165
|
||||
349,46,175,30,165
|
||||
349,42,87,30,165
|
||||
349,60,175,30,165
|
||||
349,59,87,30,165
|
||||
440,60,175,30,165
|
||||
440,50,87,30,165
|
||||
349,53,175,30,165
|
||||
349,60,87,30,165
|
||||
392,52,131,30,165
|
||||
392,49,65,30,165
|
||||
349,60,131,30,165
|
||||
330,60,65,30,165
|
||||
330,47,131,30,165
|
||||
330,60,65,30,165
|
||||
349,45,131,30,165
|
||||
349,55,65,30,165
|
||||
294,42,147,30,165
|
||||
294,45,73,30,165
|
||||
294,60,147,30,165
|
||||
294,60,73,30,165
|
||||
330,52,147,30,165
|
||||
330,59,73,30,165
|
||||
349,43,147,30,165
|
||||
349,60,73,30,165
|
||||
392,60,117,30,165
|
||||
392,60,58,30,165
|
||||
349,60,117,30,165
|
||||
330,60,58,30,165
|
||||
330,60,117,30,165
|
||||
330,60,58,30,165
|
||||
392,45,117,30,165
|
||||
392,43,58,30,165
|
||||
349,42,87,30,165
|
||||
349,41,44,30,165
|
||||
349,60,87,30,165
|
||||
349,60,44,30,165
|
||||
440,51,87,30,165
|
||||
440,53,44,30,165
|
||||
349,45,87,30,165
|
||||
349,42,44,30,165
|
||||
392,60,131,30,165
|
||||
392,40,65,30,165
|
||||
349,60,131,30,165
|
||||
392,46,65,30,165
|
||||
392,60,131,30,165
|
||||
392,53,65,30,165
|
||||
392,41,131,30,165
|
||||
392,46,65,30,165
|
||||
587,58,147,30,165
|
||||
587,51,73,30,165
|
||||
587,48,147,30,165
|
||||
587,55,73,30,165
|
||||
659,60,147,30,165
|
||||
659,60,73,30,165
|
||||
698,49,147,30,165
|
||||
698,57,73,30,165
|
||||
1047,48,117,30,165
|
||||
1047,45,58,30,165
|
||||
1047,52,117,30,165
|
||||
1047,54,58,30,165
|
||||
784,60,117,30,165
|
||||
784,51,58,30,165
|
||||
784,60,117,30,165
|
||||
784,60,58,30,165
|
||||
698,42,175,30,165
|
||||
698,43,87,30,165
|
||||
784,42,175,30,165
|
||||
784,51,87,30,165
|
||||
880,47,175,30,165
|
||||
880,60,87,30,165
|
||||
784,60,175,30,165
|
||||
698,52,87,30,165
|
||||
784,60,131,30,165
|
||||
784,59,65,30,165
|
||||
698,60,131,30,165
|
||||
659,60,65,30,165
|
||||
659,49,131,30,165
|
||||
659,60,65,30,165
|
||||
523,40,131,30,165
|
||||
523,60,65,30,165
|
||||
587,48,147,30,165
|
||||
587,44,73,30,165
|
||||
587,43,147,30,165
|
||||
587,48,73,30,165
|
||||
659,54,147,30,165
|
||||
659,56,73,30,165
|
||||
698,48,147,30,165
|
||||
698,57,73,30,165
|
||||
587,43,117,30,165
|
||||
587,43,58,30,165
|
||||
587,60,117,30,165
|
||||
587,42,58,30,165
|
||||
523,60,117,30,165
|
||||
523,60,58,30,165
|
||||
523,48,117,30,165
|
||||
523,58,58,30,165
|
||||
880,60,87,30,165
|
||||
880,49,44,30,165
|
||||
784,47,87,30,165
|
||||
784,55,44,30,165
|
||||
698,55,87,30,165
|
||||
698,45,44,30,165
|
||||
659,42,87,30,165
|
||||
698,60,44,30,165
|
||||
784,60,131,30,165
|
||||
784,49,65,30,165
|
||||
698,58,131,30,165
|
||||
659,40,65,30,165
|
||||
659,53,131,30,165
|
||||
659,49,65,30,165
|
||||
698,58,131,30,165
|
||||
698,50,65,30,165
|
||||
587,42,147,30,165
|
||||
587,60,73,30,165
|
||||
587,43,147,30,165
|
||||
587,55,73,30,165
|
||||
659,41,147,30,165
|
||||
659,60,73,30,165
|
||||
698,55,147,30,165
|
||||
698,60,73,30,165
|
||||
1047,43,117,30,165
|
||||
1047,41,58,30,165
|
||||
1047,58,117,30,165
|
||||
1047,56,58,30,165
|
||||
784,45,117,30,165
|
||||
784,51,58,30,165
|
||||
784,42,117,30,165
|
||||
784,55,58,30,165
|
||||
698,58,175,30,165
|
||||
698,43,87,30,165
|
||||
784,43,175,30,165
|
||||
784,60,87,30,165
|
||||
880,60,175,30,165
|
||||
880,60,87,30,165
|
||||
784,59,175,30,165
|
||||
698,60,87,30,165
|
||||
784,60,131,30,165
|
||||
784,60,65,30,165
|
||||
698,60,131,30,165
|
||||
659,60,65,30,165
|
||||
659,40,131,30,165
|
||||
659,58,65,30,165
|
||||
523,60,131,30,165
|
||||
523,53,65,30,165
|
||||
587,55,147,30,165
|
||||
587,60,73,30,165
|
||||
587,60,147,30,165
|
||||
587,48,73,30,165
|
||||
659,46,147,30,165
|
||||
659,52,73,30,165
|
||||
698,60,147,30,165
|
||||
698,60,73,30,165
|
||||
587,57,117,30,165
|
||||
587,48,58,30,165
|
||||
587,60,117,30,165
|
||||
587,60,58,30,165
|
||||
523,60,117,30,165
|
||||
523,40,58,30,165
|
||||
523,46,117,30,165
|
||||
523,51,58,30,165
|
||||
880,60,87,30,165
|
||||
880,53,44,30,165
|
||||
784,54,87,30,165
|
||||
784,60,44,30,165
|
||||
698,45,87,30,165
|
||||
698,43,44,30,165
|
||||
659,50,87,30,165
|
||||
698,60,44,30,165
|
||||
784,53,131,30,165
|
||||
784,60,65,30,165
|
||||
698,60,131,30,165
|
||||
659,53,65,30,165
|
||||
659,47,131,30,165
|
||||
659,50,65,30,165
|
||||
659,60,131,30,165
|
||||
659,60,65,30,165
|
||||
587,60,147,30,165
|
||||
587,60,73,30,165
|
||||
587,60,147,30,165
|
||||
587,48,73,30,165
|
||||
698,60,147,30,165
|
||||
698,60,73,30,165
|
||||
784,56,147,30,165
|
||||
784,52,73,30,165
|
||||
880,60,117,30,165
|
||||
880,60,58,30,165
|
||||
698,47,117,30,165
|
||||
698,52,58,30,165
|
||||
698,45,117,30,165
|
||||
698,60,58,30,165
|
||||
784,44,117,30,165
|
||||
698,60,58,30,165
|
||||
659,44,175,30,165
|
||||
659,44,87,30,165
|
||||
698,58,175,30,165
|
||||
784,52,87,30,165
|
||||
784,41,175,30,165
|
||||
659,58,87,30,165
|
||||
659,44,175,30,165
|
||||
659,60,87,30,165
|
||||
784,60,131,30,165
|
||||
784,59,65,30,165
|
||||
698,49,131,30,165
|
||||
659,49,65,30,165
|
||||
659,60,131,30,165
|
||||
659,60,65,30,165
|
||||
523,60,131,30,165
|
||||
523,60,65,30,165
|
||||
587,55,147,30,165
|
||||
587,45,73,30,165
|
||||
587,55,147,30,165
|
||||
587,59,73,30,165
|
||||
698,59,147,30,165
|
||||
698,60,73,30,165
|
||||
784,56,147,30,165
|
||||
784,60,73,30,165
|
||||
880,60,117,30,165
|
||||
880,43,58,30,165
|
||||
698,48,117,30,165
|
||||
880,48,58,30,165
|
||||
880,60,117,30,165
|
||||
880,60,58,30,165
|
||||
932,60,117,30,165
|
||||
880,60,58,30,165
|
||||
1047,59,87,30,165
|
||||
1047,56,44,30,165
|
||||
698,57,87,30,165
|
||||
784,46,44,30,165
|
||||
784,50,87,30,165
|
||||
659,60,44,30,165
|
||||
659,41,87,30,165
|
||||
659,53,44,30,165
|
||||
784,60,131,30,165
|
||||
784,54,65,30,165
|
||||
698,55,131,30,165
|
||||
659,41,65,30,165
|
||||
659,42,131,30,165
|
||||
659,48,65,30,165
|
||||
698,41,131,30,165
|
||||
698,51,65,30,165
|
||||
587,41,147,30,165
|
||||
587,60,73,30,165
|
||||
587,58,147,30,165
|
||||
587,40,73,30,165
|
||||
698,51,147,30,165
|
||||
698,60,73,30,165
|
||||
784,57,147,30,165
|
||||
784,49,73,30,165
|
||||
880,60,117,30,165
|
||||
880,55,58,30,165
|
||||
698,60,117,30,165
|
||||
698,44,58,30,165
|
||||
698,60,117,30,165
|
||||
698,40,58,30,165
|
||||
784,50,117,30,165
|
||||
698,44,58,30,165
|
||||
659,51,175,30,165
|
||||
659,48,87,30,165
|
||||
698,40,175,30,165
|
||||
784,60,87,30,165
|
||||
0,60,175,30,165
|
||||
659,60,87,30,165
|
||||
0,60,175,30,165
|
||||
784,60,87,30,165
|
||||
784,60,131,30,165
|
||||
698,51,65,30,165
|
||||
659,47,131,30,165
|
||||
659,60,65,30,165
|
||||
659,53,131,30,165
|
||||
523,46,65,30,165
|
||||
523,60,131,30,165
|
||||
587,56,65,30,165
|
||||
587,43,147,30,165
|
||||
587,53,73,30,165
|
||||
587,42,147,30,165
|
||||
698,48,73,30,165
|
||||
698,54,147,30,165
|
||||
784,60,73,30,165
|
||||
784,60,147,30,165
|
||||
880,42,73,30,165
|
||||
880,44,117,30,165
|
||||
698,57,58,30,165
|
||||
880,60,117,30,165
|
||||
880,60,58,30,165
|
||||
880,60,117,30,165
|
||||
932,53,58,30,165
|
||||
880,59,117,30,165
|
||||
1047,42,58,30,165
|
||||
1047,48,87,30,165
|
||||
698,43,44,30,165
|
||||
784,45,87,30,165
|
||||
0,60,44,30,165
|
||||
659,60,87,30,165
|
||||
0,44,44,30,165
|
||||
784,54,87,30,165
|
||||
784,43,44,30,165
|
||||
698,48,131,30,165
|
||||
659,60,65,30,165
|
||||
659,43,131,30,165
|
||||
659,60,65,30,165
|
||||
659,42,131,30,165
|
||||
659,43,65,30,165
|
||||
|
|
Binary file not shown.
|
@ -1,140 +0,0 @@
|
|||
Frequency1,Effect1,Frequency2,Effect2,Noise,Duration
|
||||
261,50,130,20,0,151
|
||||
523,50,261,20,0,154
|
||||
220,50,110,20,0,142
|
||||
440,50,220,20,0,153
|
||||
233,50,116,20,0,147
|
||||
466,50,233,20,0,154
|
||||
0,0,0,20,0,887
|
||||
261,50,130,20,0,153
|
||||
523,50,261,20,0,154
|
||||
220,50,110,20,0,142
|
||||
440,50,220,20,0,153
|
||||
233,50,116,20,0,147
|
||||
466,50,233,20,0,154
|
||||
0,0,0,20,0,887
|
||||
174,50,87,20,0,153
|
||||
349,50,174,20,0,154
|
||||
146,50,73,20,0,142
|
||||
293,50,146,20,0,153
|
||||
155,50,77,20,0,147
|
||||
311,50,155,20,0,154
|
||||
0,0,0,20,0,887
|
||||
174,50,87,20,0,153
|
||||
349,50,174,20,0,154
|
||||
146,50,73,20,0,142
|
||||
293,50,146,20,0,153
|
||||
155,50,77,20,0,147
|
||||
311,50,155,20,0,154
|
||||
0,0,0,20,0,592
|
||||
311,50,155,20,0,93
|
||||
0,0,0,20,0,5
|
||||
293,50,146,20,0,90
|
||||
0,0,0,20,0,5
|
||||
277,50,138,20,0,93
|
||||
0,0,0,20,0,5
|
||||
261,50,130,20,0,227
|
||||
0,0,0,20,0,74
|
||||
311,50,155,20,0,220
|
||||
0,0,0,20,0,74
|
||||
293,50,146,20,0,227
|
||||
0,0,0,20,0,74
|
||||
207,50,103,20,0,220
|
||||
0,0,0,20,0,74
|
||||
195,50,97,20,0,227
|
||||
0,0,0,20,0,74
|
||||
277,50,138,20,0,220
|
||||
0,0,0,20,0,74
|
||||
261,50,130,20,0,98
|
||||
0,0,0,20,0,5
|
||||
369,50,184,20,0,92
|
||||
0,0,0,20,0,5
|
||||
349,50,174,20,0,93
|
||||
0,0,0,20,0,5
|
||||
329,50,164,20,0,93
|
||||
0,0,0,20,0,5
|
||||
466,50,233,20,0,90
|
||||
0,0,0,20,0,5
|
||||
440,50,220,20,0,93
|
||||
0,0,0,20,0,5
|
||||
415,50,207,20,0,82
|
||||
0,0,0,20,0,119
|
||||
311,50,155,20,0,79
|
||||
0,0,0,20,0,119
|
||||
246,50,123,20,0,76
|
||||
0,0,0,20,0,119
|
||||
233,50,116,20,0,82
|
||||
0,0,0,20,0,119
|
||||
220,50,110,20,0,79
|
||||
0,0,0,20,0,119
|
||||
207,50,103,20,0,76
|
||||
0,0,0,20,0,1915
|
||||
261,50,130,20,0,151
|
||||
523,50,261,20,0,154
|
||||
220,50,110,20,0,142
|
||||
440,50,220,20,0,153
|
||||
233,50,116,20,0,147
|
||||
466,50,233,20,0,154
|
||||
0,0,0,20,0,887
|
||||
261,50,130,20,0,153
|
||||
523,50,261,20,0,154
|
||||
220,50,110,20,0,142
|
||||
440,50,220,20,0,153
|
||||
233,50,116,20,0,147
|
||||
466,50,233,20,0,154
|
||||
0,0,0,20,0,887
|
||||
174,50,87,20,0,153
|
||||
349,50,174,20,0,154
|
||||
146,50,73,20,0,142
|
||||
293,50,146,20,0,153
|
||||
155,50,77,20,0,147
|
||||
311,50,155,20,0,154
|
||||
0,0,0,20,0,887
|
||||
174,50,87,20,0,153
|
||||
349,50,174,20,0,154
|
||||
146,50,73,20,0,142
|
||||
293,50,146,20,0,153
|
||||
155,50,77,20,0,147
|
||||
311,50,155,20,0,154
|
||||
0,0,0,20,0,592
|
||||
311,50,155,20,0,93
|
||||
0,0,0,20,0,5
|
||||
293,50,146,20,0,90
|
||||
0,0,0,20,0,5
|
||||
277,50,138,20,0,93
|
||||
0,0,0,20,0,5
|
||||
261,50,130,20,0,227
|
||||
0,0,0,20,0,74
|
||||
311,50,155,20,0,220
|
||||
0,0,0,20,0,74
|
||||
293,50,146,20,0,227
|
||||
0,0,0,20,0,74
|
||||
207,50,103,20,0,220
|
||||
0,0,0,20,0,74
|
||||
195,50,97,20,0,227
|
||||
0,0,0,20,0,74
|
||||
277,50,138,20,0,220
|
||||
0,0,0,20,0,74
|
||||
261,50,130,20,0,98
|
||||
0,0,0,20,0,5
|
||||
369,50,184,20,0,92
|
||||
0,0,0,20,0,5
|
||||
349,50,174,20,0,93
|
||||
0,0,0,20,0,5
|
||||
329,50,164,20,0,93
|
||||
0,0,0,20,0,5
|
||||
466,50,233,20,0,90
|
||||
0,0,0,20,0,5
|
||||
440,50,220,20,0,93
|
||||
0,0,0,20,0,5
|
||||
415,50,207,20,0,82
|
||||
0,0,0,20,0,119
|
||||
311,50,155,20,0,79
|
||||
0,0,0,20,0,119
|
||||
246,50,123,20,0,76
|
||||
0,0,0,20,0,119
|
||||
233,50,116,20,0,82
|
||||
0,0,0,20,0,119
|
||||
220,50,110,20,0,79
|
||||
0,0,0,20,0,119
|
||||
207,50,103,20,0,76
|
|
|
@ -1,7 +1,3 @@
|
|||
Frequency1,Effect1,Noise,Duration
|
||||
0,0,0,500
|
||||
0,0,1,500
|
||||
0,0,2,500
|
||||
0,0,3,500
|
||||
0,0,4,500
|
||||
0,0,5,500
|
||||
Frequency1,Effect1,Frequency2,Effect2,Frequency3,Effect3,Noise,Duration
|
||||
440,50,247,10,311,70,0,500
|
||||
440,25,247,10,311,40,5,500
|
||||
|
|
|
|
@ -1,45 +0,0 @@
|
|||
import PySimpleGUI as sg
|
||||
import glob
|
||||
import offitracker as offi
|
||||
import sys
|
||||
import csv
|
||||
import numpy as np
|
||||
import sounddevice as sd
|
||||
import threading
|
||||
|
||||
names = glob.glob("./**/*.csv", recursive=True)
|
||||
toplay = ""
|
||||
|
||||
layout = [[sg.Text('Search')],
|
||||
[sg.Input(size=(200, 1), enable_events=True, key='-INPUT-')],
|
||||
[sg.Listbox(names, size=(200, 10), enable_events=True, key='-LIST-')],
|
||||
[sg.Button('Play'), sg.Button('Stop'), sg.Text("",key="file")]]
|
||||
|
||||
window = sg.Window('OffiPlayer', layout,size=(480, 320))
|
||||
|
||||
def playthread(window):
|
||||
offi.stop_signal = False
|
||||
offi.play_csv_file(toplay)
|
||||
window.write_event_value(('-THREAD-', '** DONE **'), 'Done!')
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
if event in (sg.WIN_CLOSED, 'Exit'):
|
||||
break
|
||||
if values['-INPUT-'] != '':
|
||||
search = values['-INPUT-']
|
||||
new_values = [x for x in names if search in x]
|
||||
window['-LIST-'].update(new_values)
|
||||
else:
|
||||
window['-LIST-'].update(names)
|
||||
if event == '-LIST-' and len(values['-LIST-']):
|
||||
toplay = values['-LIST-'][0]
|
||||
window['file'].update(toplay)
|
||||
if event == 'Play':
|
||||
window.start_thread(lambda: playthread(window), ('-THREAD-', '-THEAD ENDED-'))
|
||||
if event == 'Stop':
|
||||
offi.stop_signal = True
|
||||
window.close()
|
||||
|
||||
|
||||
|
113
offitracker.py
113
offitracker.py
|
@ -2,123 +2,66 @@ 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.
|
||||
# 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.
|
||||
|
||||
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)
|
||||
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)]
|
||||
# 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)
|
||||
# Add optional noise channel
|
||||
if noise_amplitude > 0:
|
||||
noise = noise_amplitude * np.random.uniform(-1, 1, len(t))
|
||||
waves.append(noise)
|
||||
|
||||
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 = np.sum(waves, axis=0)
|
||||
combined_wave = combined_wave.astype(np.float32)
|
||||
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()
|
||||
output_stream.write(combined_wave)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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))
|
||||
|
||||
# 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)
|
||||
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')
|
||||
print('\_____ \_/ ____\/ ____\__\__ ___/___________ ____ | | __ ___________ ')
|
||||
print(' / | \ __\\\\ __\| | | | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \\')
|
||||
print('/ | \ | | | | | | | | | \// __ \\\\ \___| <\ ___/| | \/')
|
||||
print('\_______ /__| |__| |__| |____| |__| (____ /\___ >__|_ \\\\___ >__| ')
|
||||
print(' \/ \/ \/ \/ \/ ')
|
||||
print(' Version 1.1')
|
||||
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)
|
||||
|
||||
# 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.")
|
||||
|
|
Loading…
Reference in New Issue
Block a user