# fm_harmonics_music_generator.py # # Created at 2025-01-01 # # Most of the code is AI generated, but the script works just fine. # # Unfortunately, I don't have enough time to add more typing now. # # Author: Intel A80486DX2-66 # License: Creative Commons Zero 1.0 Universal or Unlicense # SPDX-License-Identifier: CC0-1.0 OR Unlicense import numpy as np from scipy.io.wavfile import write from random import randint, seed, getrandbits, uniform def extend(l: list, n: int) -> list: extended_list = [] # Create a new list to hold the extended values for value in l: extended_list.extend([value] * n) # Append the value n times return extended_list def generate_fm_wave(carrier_freq, modulator_freq, duration, sample_rate=44100, modulation_index=1.0): """Generate a sine wave using FM synthesis.""" t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) modulating_signal = np.sin(2 * np.pi * modulator_freq * t) # Modulator signal wave = 0.5 * np.sin(2 * np.pi * carrier_freq * t + modulation_index * modulating_signal) # FM synthesis return wave def apply_envelope(wave, attack_time, decay_time, sustain_level, release_time, sample_rate=44100): """Apply an ADSR envelope to the wave.""" total_samples = len(wave) attack_samples = int(attack_time * sample_rate) decay_samples = int(decay_time * sample_rate) release_samples = int(release_time * sample_rate) # Create the envelope envelope = np.zeros(total_samples) # Attack envelope[:attack_samples] = np.linspace(0, 1, attack_samples) # Decay if attack_samples + decay_samples < total_samples: envelope[attack_samples:attack_samples + decay_samples] = np.linspace(1, sustain_level, decay_samples) # Sustain if attack_samples + decay_samples < total_samples: envelope[attack_samples + decay_samples:total_samples - release_samples] = sustain_level # Release if total_samples - release_samples > 0: envelope[total_samples - release_samples:] = np.linspace(sustain_level, 0, release_samples) return wave * envelope def generate_sine_wave(frequencies, duration, sample_rate=44100): """Generate a sine wave for a list of frequencies.""" t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) wave = np.zeros_like(t) for freq in frequencies: wave += np.sin(2 * np.pi * freq * t) return wave def create_sound_file(patterns, repetitions, tempo, output_file, carrier, modulator, modulation_index): """ Create a sound file with specified frequency patterns, repetitions, and tempo. :param patterns: List of lists containing frequencies (in Hz) for each pattern. :param repetitions: Number of times to repeat each pattern. :param tempo: Tempo in beats per minute (BPM). :param output_file: Name of the output WAV file. """ sample_rate = 44100 # Standard sample rate beat_duration = 60 / tempo # Duration of one beat in seconds sound_wave = np.array([]) # Initialize an empty array for the sound wave pattern_extension = 10 extended_patterns = extend(patterns, pattern_extension) t = 0 for i in range(repetitions): # Repeat the entire pattern for pattern in extended_patterns: actual_beat_duration = beat_duration / pattern_extension # Generate a sine wave for the current pattern sine_wave = generate_sine_wave(pattern, actual_beat_duration) ## carrier=int(frequency / randint(1, 2)) ## modulator=randint(int(frequency / 4), int(frequency / 2)) # uniform(frequency / 4, frequency / 2) fm_wave = generate_fm_wave(carrier, modulator, actual_beat_duration, modulation_index=modulation_index) # Combine the sine wave and FM wave combined_wave = sine_wave + fm_wave # Apply envelope to the combined wave ## combined_wave = apply_envelope(combined_wave, attack_time=0.01, decay_time=8, sustain_level=0.7, release_time=0.2, sample_rate=sample_rate) # Append the combined wave to the sound wave sound_wave = np.concatenate((sound_wave, combined_wave)) t += 1 # Normalize the sound wave to the range of int16 sound_wave = np.int16(sound_wave / np.max(np.abs(sound_wave)) * 32767) # Write the sound wave to a WAV file write(output_file, sample_rate, sound_wave) def harmonics(frequency, count: int, step: int = 1) -> list: return [frequency * i for i in range(1, count, step)] if __name__ == "__main__": ## seed(0) tempo_multiplier = 4 randbool = lambda: getrandbits(1) == 1 min_frequency = 35 max_frequency = 120 frequency_limit_diff = max_frequency - min_frequency frequency = randint(min_frequency, max_frequency) n = 2**randint(1, 5) # 4/4 beat frequency_subtract = frequency // frequency_limit_diff negative_frequency_drift = randbool() precision = 2 precision_machine = 10**precision frequency_drift_toggle = randbool() frequency_drift_max = randint(0, 1) if frequency_drift_toggle else 0 frequency_drift_value = lambda: randint(-frequency_drift_max * precision_machine if negative_frequency_drift else 0, frequency_drift_max * precision_machine) / precision_machine frequency_drift = lambda: frequency_drift_value() if randbool() else 0 carrier = uniform(frequency / 4, frequency * 2) modulator = randint(0, frequency) modulation_index = randint(5, 50) patterns = [ harmonics( frequency + frequency_drift(), randint( 1, 16), randint(1, 2)) for i in range(n) ] repetitions = randint(2, 8) # 4/4 beat tempo = randint(60, 160) output_file = "output.wav" print(f"Base: {frequency} Hz, frequency drift: {'on' if frequency_drift_toggle else 'off'}") print(f"Carrier: {carrier} Hz") print(f"Modulator: {modulator} Hz") print(f"Modulation index: {modulation_index}") print(f"Tempo: {tempo} BPM") create_sound_file(patterns, repetitions, tempo * tempo_multiplier, output_file, carrier, modulator, modulation_index) print("Done!")