Advertisement
yclee126

Desmos music player

Dec 29th, 2024
188
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.80 KB | None | 0 0
  1. import numpy as np
  2. import pydub
  3. import re
  4. from pathlib import Path
  5.  
  6. # https://www.desmos.com/calculator/dvg39kkbvz
  7. # copy the song data to a .txt file and run this script
  8.  
  9. def parse_music_file(filename):
  10.     with open(filename, "r") as file:
  11.         data = file.read()
  12.    
  13.     keys = ["f_{req}", "s_{tart}", "d_{uration}", "s_{ustain}", "v_{elocity}"]
  14.     parsed_data = []
  15.    
  16.     for key in keys:
  17.         pattern = rf"{key}=\[([\d.,\s]+)\]"
  18.         match = re.search(pattern, data)
  19.         if match:
  20.             parsed_data.append(list(map(float, match.group(1).split(","))))
  21.         else:
  22.             raise ValueError(f"Key {key} not found in the file.")
  23.    
  24.     return parsed_data
  25.  
  26. # Function to generate a sine wave for a given frequency and duration
  27. def generate_sine_wave(frequency, duration, amplitude, sample_rate=44100):
  28.     t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
  29.     wave = amplitude * np.sin(2 * np.pi * frequency * t)
  30.     return wave
  31.  
  32. def generate_sawtooth_wave(frequency, duration, amplitude, sample_rate=44100):
  33.     t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
  34.     wave = np.zeros_like(t)
  35.    
  36.     # Add harmonics up to the 7th order (psudo sawtooth)
  37.     for n in range(1, 8):  # n = 1, 2, ..., 7
  38.         harmonic = (-1)**(n+1) * (1 / n) * np.sin(2 * np.pi * n * frequency * t)
  39.         wave += harmonic
  40.  
  41.     # Normalize the wave and apply the amplitude
  42.     wave = wave / np.max(np.abs(wave))  # Normalize the waveform
  43.     wave *= amplitude  # Apply the desired amplitude scaling
  44.  
  45.     return wave
  46.  
  47. def save_numpy_audio(f, sample_rate, arr):
  48.     """numpy array to MP3"""
  49.     channels = 2 if (arr.ndim == 2 and arr.shape[1] == 2) else 1
  50.     y = np.int16(arr * 2 ** 15)
  51.  
  52.     song = pydub.AudioSegment(y.tobytes(), frame_rate=sample_rate, sample_width=2, channels=channels)
  53.     song.export(f, format="mp3", bitrate="192k")
  54.  
  55. # Main function to synthesize and play music
  56. def play_music(filename):
  57.     freqs, starts, durations, sustains, velocities = parse_music_file(filename)
  58.     print('File read OK')
  59.  
  60.     sample_rate = 44100
  61.     total_duration = max(starts) + max(durations) + max(sustains)
  62.     total_samples = int(total_duration * sample_rate)
  63.    
  64.     # Initialize the final audio buffer
  65.     audio_buffer = np.zeros(total_samples, dtype=np.float32)
  66.    
  67.     i = 0
  68.     last_percent = -1
  69.     max_count = len(freqs)
  70.     print('Processing notes...')
  71.  
  72.     for freq, start, duration, sustain, velocity in zip(freqs, starts, durations, sustains, velocities):
  73.         curr_percent = int((i+1) / max_count * 100)
  74.         if curr_percent != last_percent:
  75.             print(f'{curr_percent}%', end='\r')
  76.             last_percent = curr_percent
  77.  
  78.         amplitude = velocity  # Velocity directly maps to amplitude
  79.         start_sample = int(start * sample_rate)
  80.         sustain_samples = int(sustain * sample_rate)
  81.  
  82.         # Generate the note wave
  83.         wave = generate_sawtooth_wave(freq, duration+sustain, amplitude, sample_rate)
  84.  
  85.         # Apply sustain by fading out
  86.         fade_out = np.linspace(1, 0, sustain_samples)
  87.         wave[-sustain_samples:] *= fade_out
  88.        
  89.         # Add the wave to the audio buffer
  90.         end_sample = start_sample + len(wave)
  91.         audio_buffer[start_sample:end_sample] += wave
  92.  
  93.         i += 1
  94.  
  95.     # Normalize the audio to avoid clipping
  96.     max_val = np.max(np.abs(audio_buffer))
  97.     if max_val > 0:
  98.         audio_buffer /= max_val
  99.  
  100.     # Save the audio buffer
  101.     print("Saving file...")
  102.     save_numpy_audio(str(Path(filename).stem) + '.mp3', sample_rate, audio_buffer)
  103.    
  104.     print("Operation complete")
  105.  
  106. # Run the program with your custom file
  107. if __name__ == "__main__":
  108.     filename = "Devourer of Gods.txt"  # Replace with your file path
  109.     play_music(filename)
  110.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement