Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import numpy as np
- import pydub
- import re
- from pathlib import Path
- # https://www.desmos.com/calculator/dvg39kkbvz
- # copy the song data to a .txt file and run this script
- def parse_music_file(filename):
- with open(filename, "r") as file:
- data = file.read()
- keys = ["f_{req}", "s_{tart}", "d_{uration}", "s_{ustain}", "v_{elocity}"]
- parsed_data = []
- for key in keys:
- pattern = rf"{key}=\[([\d.,\s]+)\]"
- match = re.search(pattern, data)
- if match:
- parsed_data.append(list(map(float, match.group(1).split(","))))
- else:
- raise ValueError(f"Key {key} not found in the file.")
- return parsed_data
- # Function to generate a sine wave for a given frequency and duration
- def generate_sine_wave(frequency, duration, amplitude, sample_rate=44100):
- t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
- wave = amplitude * np.sin(2 * np.pi * frequency * t)
- return wave
- def generate_sawtooth_wave(frequency, duration, amplitude, sample_rate=44100):
- t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
- wave = np.zeros_like(t)
- # Add harmonics up to the 7th order (psudo sawtooth)
- for n in range(1, 8): # n = 1, 2, ..., 7
- harmonic = (-1)**(n+1) * (1 / n) * np.sin(2 * np.pi * n * frequency * t)
- wave += harmonic
- # Normalize the wave and apply the amplitude
- wave = wave / np.max(np.abs(wave)) # Normalize the waveform
- wave *= amplitude # Apply the desired amplitude scaling
- return wave
- def save_numpy_audio(f, sample_rate, arr):
- """numpy array to MP3"""
- channels = 2 if (arr.ndim == 2 and arr.shape[1] == 2) else 1
- y = np.int16(arr * 2 ** 15)
- song = pydub.AudioSegment(y.tobytes(), frame_rate=sample_rate, sample_width=2, channels=channels)
- song.export(f, format="mp3", bitrate="192k")
- # Main function to synthesize and play music
- def play_music(filename):
- freqs, starts, durations, sustains, velocities = parse_music_file(filename)
- print('File read OK')
- sample_rate = 44100
- total_duration = max(starts) + max(durations) + max(sustains)
- total_samples = int(total_duration * sample_rate)
- # Initialize the final audio buffer
- audio_buffer = np.zeros(total_samples, dtype=np.float32)
- i = 0
- last_percent = -1
- max_count = len(freqs)
- print('Processing notes...')
- for freq, start, duration, sustain, velocity in zip(freqs, starts, durations, sustains, velocities):
- curr_percent = int((i+1) / max_count * 100)
- if curr_percent != last_percent:
- print(f'{curr_percent}%', end='\r')
- last_percent = curr_percent
- amplitude = velocity # Velocity directly maps to amplitude
- start_sample = int(start * sample_rate)
- sustain_samples = int(sustain * sample_rate)
- # Generate the note wave
- wave = generate_sawtooth_wave(freq, duration+sustain, amplitude, sample_rate)
- # Apply sustain by fading out
- fade_out = np.linspace(1, 0, sustain_samples)
- wave[-sustain_samples:] *= fade_out
- # Add the wave to the audio buffer
- end_sample = start_sample + len(wave)
- audio_buffer[start_sample:end_sample] += wave
- i += 1
- # Normalize the audio to avoid clipping
- max_val = np.max(np.abs(audio_buffer))
- if max_val > 0:
- audio_buffer /= max_val
- # Save the audio buffer
- print("Saving file...")
- save_numpy_audio(str(Path(filename).stem) + '.mp3', sample_rate, audio_buffer)
- print("Operation complete")
- # Run the program with your custom file
- if __name__ == "__main__":
- filename = "Devourer of Gods.txt" # Replace with your file path
- play_music(filename)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement