Source code for jdxi_editor.midi.jupiter8_synthesis

"""
Jupiter 8 String Synthesis Module

This module provides functions to create Jupiter 8-like string sounds using pure Python synthesis
without requiring SoundFonts or external audio libraries.
"""

import numpy as np
import scipy.io.wavfile as wav


[docs] def create_jupiter8_string(frequency=440, duration=2.0, sample_rate=44100): """Create a Jupiter 8-like string sound using additive synthesis""" # Time array t = np.linspace(0, duration, int(sample_rate * duration), False) # Fundamental frequency fundamental = np.sin(2 * np.pi * frequency * t) # Harmonics (Jupiter 8 strings have rich harmonics) harmonics = [ 0.5 * np.sin(2 * np.pi * frequency * 2 * t), # 2nd harmonic 0.3 * np.sin(2 * np.pi * frequency * 3 * t), # 3rd harmonic 0.2 * np.sin(2 * np.pi * frequency * 4 * t), # 4th harmonic 0.1 * np.sin(2 * np.pi * frequency * 5 * t), # 5th harmonic ] # Combine all harmonics string_sound = fundamental + sum(harmonics) # Calculate envelope segments total_samples = len(string_sound) attack_samples = int(0.1 * total_samples) decay_samples = int(0.2 * total_samples) sustain_samples = int(0.5 * total_samples) release_samples = total_samples - attack_samples - decay_samples - sustain_samples # Ensure we don't have negative release samples if release_samples < 0: # Adjust sustain to fit sustain_samples += release_samples release_samples = 0 # Create ADSR envelope envelope = np.ones(total_samples) # Attack (0 to 1) if attack_samples > 0: envelope[:attack_samples] = np.linspace(0, 1, attack_samples) # Decay (1 to 0.7) if decay_samples > 0: envelope[attack_samples : attack_samples + decay_samples] = np.linspace( 1, 0.7, decay_samples ) # Sustain (0.7) if sustain_samples > 0: envelope[ attack_samples + decay_samples : attack_samples + decay_samples + sustain_samples ] = 0.7 # Release (0.7 to 0) if release_samples > 0: envelope[attack_samples + decay_samples + sustain_samples :] = np.linspace( 0.7, 0, release_samples ) # Apply envelope and normalize string_sound = string_sound * envelope string_sound = string_sound / np.max(np.abs(string_sound)) * 0.8 return string_sound, sample_rate
[docs] def create_string_chord(frequencies, duration=3.0, sample_rate=44100): """Create a chord of Jupiter 8-like strings""" # Create individual string sounds string_sounds = [] for freq in frequencies: sound, _ = create_jupiter8_string(freq, duration, sample_rate) string_sounds.append(sound) # Mix them together chord = np.sum(string_sounds, axis=0) # Normalize to prevent clipping chord = chord / np.max(np.abs(chord)) * 0.8 return chord, sample_rate
[docs] def demo_jupiter8_strings(): """Demo function to create and save Jupiter 8-like string sounds""" print("[INFO] Creating Jupiter 8-like string sounds...") # Create a single string note (A4) single_string, sr = create_jupiter8_string(frequency=440, duration=3.0) wav.write( "jupiter8_single_string.wav", sr, (single_string * 32767).astype(np.int16) ) print("[INFO] Created single string: jupiter8_single_string.wav") # Create a string chord (C major: C, E, G) chord_frequencies = [261.63, 329.63, 392.00] # C4, E4, G4 string_chord, sr = create_string_chord(chord_frequencies, duration=4.0) wav.write("jupiter8_string_chord.wav", sr, (string_chord * 32767).astype(np.int16)) print("[INFO] Created string chord: jupiter8_string_chord.wav") # Create a lower string note (C3) low_string, sr = create_jupiter8_string(frequency=130.81, duration=3.0) wav.write("jupiter8_low_string.wav", sr, (low_string * 32767).astype(np.int16)) print("[INFO] Created low string: jupiter8_low_string.wav") print("[INFO] All Jupiter 8-like string sounds created successfully!")
if __name__ == "__main__": demo_jupiter8_strings()