Skip to content

Commit 06910b4

Browse files
committed
Added support for other formats
1 parent 9f92e4f commit 06910b4

File tree

5 files changed

+315
-8
lines changed

5 files changed

+315
-8
lines changed

GUI_code/audio_to_params.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import tkinter as tk
2+
from tkinter import filedialog
3+
from tkinter import messagebox
4+
import argparse
5+
import os
6+
import numpy as np
7+
from scipy.io import wavfile
8+
import moviepy.editor
9+
from tqdm import trange
10+
import subprocess
11+
12+
def convert_to_wav(input_file):
13+
output_file = "temp.wav"
14+
subprocess.call(["ffmpeg", "-i", input_file, output_file])
15+
return output_file
16+
17+
def select_file():
18+
filename = filedialog.askopenfilename(filetypes=[("Audio Files", "*.wav *.mp3 *.ogg *.mp4 *.mov" )])
19+
if filename:
20+
entry_path.delete(0, tk.END)
21+
entry_path.insert(tk.END, filename)
22+
23+
def run_script():
24+
audio_file = entry_path.get()
25+
fps = int(entry_fps.get())
26+
output_file = entry_output.get()
27+
formula = entry_formula.get()
28+
29+
if audio_file and fps and output_file and formula:
30+
try:
31+
convert_temp_wav = False
32+
33+
# Convert input file to WAV if it is not already in WAV format
34+
if not audio_file.lower().endswith('.wav'):
35+
audio_file = convert_to_wav(audio_file)
36+
convert_temp_wav = True
37+
38+
# Check if the input audio file exists
39+
if not os.path.exists(audio_file):
40+
# If not, convert the audio using moviepy
41+
audio_clip = moviepy.editor.AudioFileClip(audio_file)
42+
audio_clip.write_audiofile(audio_file, fps=44100, nbytes=2, codec='pcm_s16le')
43+
44+
# Get the track name from the input file
45+
track_name = os.path.basename(audio_file)[:-4]
46+
47+
# Read the audio file and convert to mono
48+
rate, signal = wavfile.read(audio_file)
49+
signal = np.mean(signal, axis=1)
50+
51+
# Calculate the absolute values of the audio signal
52+
signal = np.abs(signal)
53+
54+
# Calculate the duration, frames, and samples per frame
55+
duration = signal.shape[0] / rate
56+
frames = int(np.ceil(duration * fps))
57+
samples_per_frame = signal.shape[0] / frames
58+
59+
# Initialize the audio array
60+
audio = np.zeros(frames, dtype=signal.dtype)
61+
62+
# Process each frame and calculate the mean value
63+
for frame in range(frames):
64+
start = int(round(frame * samples_per_frame))
65+
stop = int(round((frame + 1) * samples_per_frame))
66+
audio[frame] = np.mean(signal[start:stop], axis=0)
67+
68+
# Normalize the audio data
69+
audio /= np.max(audio)
70+
71+
# Create an empty output string
72+
output = ""
73+
74+
# Apply the formula to each audio sample and create the output string
75+
for n in trange(len(audio), desc="Sampling"):
76+
result = evaluate_formula(audio[n], formula)
77+
output += f"{n}:({result}),"
78+
79+
# Add .txt extension to the output file if it is not present
80+
if not output_file.lower().endswith('.txt'):
81+
output_file = output_file + ".txt"
82+
83+
# Write the output string to a text file
84+
with open(output_file, "w") as text_file:
85+
text_file.write(output)
86+
87+
# Delete temporary WAV file if it was converted
88+
if convert_temp_wav:
89+
os.remove(audio_file)
90+
print("Temporary WAV file deleted.")
91+
92+
messagebox.showinfo("Success", "Audio analysis completed successfully!")
93+
except Exception as e:
94+
messagebox.showerror("Error", f"An error occurred during audio analysis: {str(e)}")
95+
else:
96+
messagebox.showwarning("Missing Input", "Please fill in all the required fields.")
97+
98+
def evaluate_formula(x, formula):
99+
try:
100+
# Safely evaluate the formula expression
101+
result = eval(formula, {}, {'x': x})
102+
return result
103+
except Exception as e:
104+
raise ValueError("Invalid formula: " + str(e))
105+
106+
# Create the main window
107+
window = tk.Tk()
108+
window.title("Audio Analysis")
109+
window.geometry("400x250")
110+
111+
# Create GUI elements
112+
label_path = tk.Label(window, text="Audio File:")
113+
entry_path = tk.Entry(window)
114+
button_browse = tk.Button(window, text="Browse", command=select_file)
115+
116+
label_fps = tk.Label(window, text="FPS:")
117+
entry_fps = tk.Entry(window)
118+
119+
label_output = tk.Label(window, text="Output File:")
120+
entry_output = tk.Entry(window)
121+
122+
label_formula = tk.Label(window, text="Formula:")
123+
entry_formula = tk.Entry(window)
124+
125+
button_run = tk.Button(window, text="Run", command=run_script)
126+
127+
# Arrange the GUI elements using grid layout
128+
label_path.grid(row=0, column=0, sticky=tk.W)
129+
entry_path.grid(row=0, column=1, padx=10, pady=5)
130+
button_browse.grid(row=0, column=2)
131+
132+
label_fps.grid(row=1, column=0, sticky=tk.W)
133+
entry_fps.grid(row=1, column=1, padx=10, pady=5)
134+
135+
label_output.grid(row=2, column=0, sticky=tk.W)
136+
entry_output.grid(row=2, column=1, padx=10, pady=5)
137+
138+
label_formula.grid(row=3, column=0, sticky=tk.W)
139+
entry_formula.grid(row=3, column=1, padx=10, pady=5)
140+
141+
button_run.grid(row=4, column=0, columnspan=3, pady=10)
142+
143+
# Start the GUI event loop
144+
window.mainloop()

GUI_code/beat_detection.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import argparse
2+
import librosa
3+
import os
4+
import subprocess
5+
import tkinter as tk
6+
from tkinter import filedialog
7+
8+
def convert_to_wav(input_file):
9+
output_file = "temp.wav"
10+
subprocess.call(["ffmpeg", "-i", input_file, output_file])
11+
return output_file
12+
13+
def save_beat_frames(input_file, fps, output_file, mul):
14+
convert_temp_wav = False
15+
16+
# Convert input file to WAV if it is not already in WAV format
17+
if not input_file.lower().endswith('.wav'):
18+
input_file = convert_to_wav(input_file)
19+
convert_temp_wav = True
20+
21+
# Load the audio file
22+
y, sr = librosa.load(input_file)
23+
24+
# Use the onset detection function
25+
onset_env = librosa.onset.onset_strength(y, sr=sr)
26+
27+
# Detect the beats
28+
tempo, beat_frames = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr)
29+
30+
# Convert beat frames to frame numbers
31+
frame_numbers = librosa.frames_to_samples(beat_frames)
32+
33+
# Calculate the frame numbers corresponding to the beats
34+
beat_frames_animation = (frame_numbers * fps / sr).astype(int)
35+
36+
# Generate the frame-by-frame output with multiplier
37+
output = []
38+
for i in range(len(y)):
39+
if i in beat_frames_animation:
40+
output.append(f"{i}:({1 * mul}),")
41+
else:
42+
output.append(f"{i}:(0),")
43+
44+
# Add .txt extension to the output file if it is not present
45+
if not output_file.lower().endswith('.txt'):
46+
output_file = output_file + ".txt"
47+
48+
# Save the frame-by-frame output to a text file
49+
with open(output_file, 'w') as f:
50+
f.write('\n'.join(output))
51+
52+
print("Frame-by-frame output saved to:", output_file)
53+
54+
# Delete temporary WAV file if it was converted
55+
if convert_temp_wav:
56+
os.remove(input_file)
57+
print("Temporary WAV file deleted.")
58+
59+
def open_file():
60+
file_path = filedialog.askopenfilename(filetypes=[("Audio Files", "*.wav *.mp3 *.ogg *.mp4")])
61+
entry_input.delete(0, tk.END)
62+
entry_input.insert(tk.END, file_path)
63+
64+
def save_file():
65+
file_path = filedialog.asksaveasfilename(filetypes=[("Text Files", "*.txt")])
66+
entry_output.delete(0, tk.END)
67+
entry_output.insert(tk.END, file_path)
68+
69+
def process():
70+
input_file = entry_input.get()
71+
fps = int(entry_fps.get())
72+
output_file = entry_output.get()
73+
mul = float(entry_mul.get())
74+
75+
save_beat_frames(input_file, fps, output_file, mul)
76+
77+
# Create the main window
78+
window = tk.Tk()
79+
window.title("Beat Detection Script")
80+
81+
# Input file selection
82+
label_input = tk.Label(window, text="Input Audio File:")
83+
label_input.pack()
84+
entry_input = tk.Entry(window)
85+
entry_input.pack()
86+
button_input = tk.Button(window, text="Browse", command=open_file)
87+
button_input.pack()
88+
89+
# FPS input
90+
label_fps = tk.Label(window, text="Frames per Second (FPS):")
91+
label_fps.pack()
92+
entry_fps = tk.Entry(window)
93+
entry_fps.pack()
94+
95+
# Output file selection
96+
label_output = tk.Label(window, text="Output File:")
97+
label_output.pack()
98+
entry_output = tk.Entry(window)
99+
entry_output.pack()
100+
button_output = tk.Button(window, text="Browse", command=save_file)
101+
button_output.pack()
102+
103+
# Multiplier input
104+
label_mul = tk.Label(window, text="Multiplier:")
105+
label_mul.pack()
106+
entry_mul = tk.Entry(window)
107+
entry_mul.pack()
108+
109+
# Process button
110+
button_process = tk.Button(window, text="Process", command=process)
111+
button_process.pack()
112+
113+
# Run the GUI main loop
114+
window.mainloop()

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ That's why I've developed a script that saves volume data for each frame and all
2121

2222
## Usage
2323

24+
Be sure to have the latest version of [FFMPEG](https://ffmpeg.org/) installed on your machine.
25+
2426
### Windows executable
2527

2628
Grab this user-friendly Windows executable right over [here!](https://github.com/kessoning/Audio-Offline-Analysis/releases/tag/v0.1)
@@ -36,6 +38,29 @@ Or run the script on any OS (well, at least I hope so!) by yourself:
3638
3. Replace input.wav with the path to your audio file, 30 with the desired frames per second (FPS) of the animation, beat_frames.txt with the output file path to save the beat frames, and "1 + x * 2" with your desired formula.
3739
4. Utilize the generated beat frames or keyframes in your creative coding IDE (e.g., Processing, OpenFrameworks) or Stable Diffusion Deforum script.
3840

41+
###
42+
43+
To compile an executable for other OS, there is the code in the GUI_code folder. You can try to compile it on your machine, and if you want to contribute make a pull request to add it to the release page.
44+
45+
The compilation requires pyinstaller
46+
47+
```bash
48+
pip install pyinstaller
49+
```
50+
51+
After doing so, you only need to run
52+
```bash
53+
pyinstaller --onefile script.py
54+
```
55+
56+
An issue I encountered was that librosa was missing a file, due the use of Anaconda. To solve this, you need to add librosa example data to the compiler
57+
58+
```bash
59+
pyinstaller --onefile --add-data "path/to/anaconda/envs/*env_name*/lib/site-packages/librosa/util/example_data;librosa/util/example_data" script.py
60+
```
61+
62+
Change "path/to/anaconda/envs/*env_name*/lib/site-packages/librosa/util/example_data" to your librosa library path.
63+
3964
### Example
4065

4166
For example, to use it in Processing you might need something like this:

audio_to_params.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
from scipy.io import wavfile
55
import moviepy.editor
66
from tqdm import trange
7+
import subprocess
78

9+
def convert_to_wav(input_file):
10+
output_file = "temp.wav"
11+
subprocess.call(["ffmpeg", "-i", input_file, output_file])
12+
return output_file
813

914
def parse_args():
1015
# Create an argument parser
@@ -34,17 +39,26 @@ def evaluate_formula(x, formula):
3439

3540

3641
def main(args):
42+
convert_temp_wav = False
43+
44+
input_file = args.input
45+
46+
# Convert input file to WAV if it is not already in WAV format
47+
if not input_file.lower().endswith('.wav'):
48+
input_file = convert_to_wav(input_file)
49+
convert_temp_wav = True
50+
3751
# Check if the input audio file exists
38-
if not os.path.exists(args.input):
52+
if not os.path.exists(input_file):
3953
# If not, convert the audio using moviepy
40-
audio_clip = moviepy.editor.AudioFileClip(args.input)
41-
audio_clip.write_audiofile(args.input, fps=44100, nbytes=2, codec='pcm_s16le')
54+
audio_clip = moviepy.editor.AudioFileClip(input_file)
55+
audio_clip.write_audiofile(input_file, fps=44100, nbytes=2, codec='pcm_s16le')
4256

4357
# Get the track name from the input file
44-
track_name = os.path.basename(args.input)[:-4]
58+
track_name = os.path.basename(input_file)[:-4]
4559

4660
# Read the audio file and convert to mono
47-
rate, signal = wavfile.read(args.input)
61+
rate, signal = wavfile.read(input_file)
4862
signal = np.mean(signal, axis=1)
4963

5064
# Calculate the absolute values of the audio signal
@@ -75,10 +89,20 @@ def main(args):
7589
result = evaluate_formula(audio[n], args.formula)
7690
output += f"{n}:({result}),"
7791

92+
output_file = args.output
93+
# Add .txt extension to the output file if it is not present
94+
if not output_file.lower().endswith('.txt'):
95+
output_file = output_file + ".txt"
96+
7897
# Write the output string to a text file
79-
with open(args.output, "w") as text_file:
98+
with open(output_file, "w") as text_file:
8099
text_file.write(output)
81100

101+
# Delete temporary WAV file if it was converted
102+
if convert_temp_wav:
103+
os.remove(input_file)
104+
print("Temporary WAV file deleted.")
105+
82106

83107
if __name__ == "__main__":
84108
# Parse the command-line arguments

beat_detection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ def save_beat_frames(input_file, fps, output_file, mul):
3535
output = []
3636
for i in range(len(y)):
3737
if i in beat_frames_animation:
38-
output.append(f"{i}:{1 * mul}")
38+
output.append(f"{i}:({1 * mul}),")
3939
else:
40-
output.append(f"{i}:0")
40+
output.append(f"{i}:(0),")
4141

4242
# Add .txt extension to the output file if it is not present
4343
if not output_file.lower().endswith('.txt'):

0 commit comments

Comments
 (0)