Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ output/*
!output/.gitkeep
samples/*
!samples/.gitkeep
reference_tracks/
21 changes: 12 additions & 9 deletions config/moods/ethereal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,26 @@ drums:

vocals:
tts_speed: slow
reverb_wet: 0.55
delay_wet: 0.25
reverb_wet: 0.20
delay_wet: 0.08
delay_time_beats: 3

vocal_engine: edge-tts
voice: spectral

textures:
vinyl_crackle_level: 0.18
ambient_level: 0.14
tape_hiss_level: 0.03

levels:
kick: 0
snare: -3
hihat: -10
bass: -4
pads: -8
vocals: 1
textures: -16
kick: -3
snare: -5
hihat: -14
bass: -7
pads: -12
vocals: 6
textures: -20

arrangement:
intro_bars: 12
Expand Down
45 changes: 26 additions & 19 deletions config/moods/melancholic.yaml
Original file line number Diff line number Diff line change
@@ -1,44 +1,51 @@
# Mood Machine — Melancholic preset
# Slow, heavy, dark — Portishead / Dummy-era vibes
# Slow, heavy, dark — Headache / Vegyn reference-tuned
# Calibrated against "The Beginning of the End" (Headache, 2023)

mood: melancholic
bpm: 75
bpm: 70 # reference: 69.8 BPM
key: Am
time_signature: 4/4

synth:
pad_filter_cutoff: 1400
pad_filter_cutoff: 1800 # iter4: raised further toward reference centroid ~1958Hz
pad_attack: 1.2
pad_release: 2.5
pad_detune_cents: 8
bass_character: warm # warm | deep | distorted
drum_tone: dark # dark | crisp | lo-fi
bass_character: warm # warm | deep | distorted
drum_tone: crisp # iter3: reverted — dark killed HF (centroid 1216→need 1958)

drums:
pattern_style: sparse # sparse | standard | busy
pattern_style: triphop # 16th-note hats — reference has ~16 events/bar
swing: 0.62
velocity_variation: 0.20
hihat_density: 0.5 # 0.0–1.0
velocity_variation: 0.38 # iter4: reference has 5.5dB more dynamic range
hihat_density: 0.7 # raised for denser hi-hat pattern

vocals:
tts_speed: slow
reverb_wet: 0.45
delay_wet: 0.15
delay_time_beats: 3 # delay in eighth-notes
reverb_wet: 0.18
delay_wet: 0.06
delay_time_beats: 3

vocal_engine: edge-tts
voice: headache

mixer:
stereo_widen_amount: 1.0 # reference stereo width 0.31 (was 0.0)

textures:
vinyl_crackle_level: 0.14
ambient_level: 0.08
tape_hiss_level: 0.04

levels: # dB relative to kick
kick: 0
snare: -2
hihat: -9
bass: -3
pads: -11
vocals: 2
textures: -20
levels: # dB relative to kick — calibrated to reference
kick: 4 # iter3: +2dB more, still 4dB short of ref sub
snare: -4
hihat: -3 # iter4: raised +3dB — presence gap was 10dB
bass: -2 # iter3: +1dB more sub-bass
pads: -1 # iter4: mid-band still 6.6dB off
vocals: 6
textures: -24

arrangement:
intro_bars: 8
Expand Down
21 changes: 12 additions & 9 deletions config/moods/paranoid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,26 @@ drums:

vocals:
tts_speed: normal
reverb_wet: 0.30
delay_wet: 0.20
reverb_wet: 0.12
delay_wet: 0.06
delay_time_beats: 4

vocal_engine: edge-tts
voice: broken_radio

textures:
vinyl_crackle_level: 0.08
ambient_level: 0.12
tape_hiss_level: 0.06

levels:
kick: 0
snare: -1
hihat: -7
bass: -2
pads: -10
vocals: 2
textures: -18
kick: -3
snare: -4
hihat: -11
bass: -5
pads: -13
vocals: 6
textures: -22

arrangement:
intro_bars: 4
Expand Down
30 changes: 30 additions & 0 deletions generate_first_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""One-off script to generate the first release track."""

from mood_machine.config import build_config
from mood_machine.pipeline import MoodMachinePipeline

LYRICS = """They told us it would get better. They said that shit with a straight face, standing under fluorescent lights, smiling like they meant it. But the truth is nobody meant a damn thing. We just kept nodding, kept swallowing the pills they dressed up as promises. And the morning kept coming whether we asked for it or not. That is the trick, isn't it? The world doesn't stop just because you figured out the joke. It keeps spinning, keeps grinding, keeps feeding you the same bullshit breakfast with a different name.

I used to believe in something. I can't even remember what it was now. Some half-baked idea about meaning, about connection, about finding your frequency in all this noise. What a load of crap. The signal was never there. It was just static all along, and we were too desperate to admit we were listening to nothing. We built cathedrals out of dead air and called it hope. We held on to people who were already gone and called it love. And now here we are, standing in the wreckage, wondering why it all feels so goddamn familiar.

The clocks don't work anymore. Not because they're broken, but because time stopped giving a shit about us the same way we stopped giving a shit about each other. Every conversation is a funeral. Every goodbye is a rehearsal for the one that actually sticks. And somewhere between the first drink and the last cigarette, you realise that nobody is coming to save you. Nobody was ever coming. That whole rescue fantasy was just another lie we told ourselves to make it through the night. And the night, the night just keeps getting longer.

So here we are at the beginning of the end. Or maybe the end of the beginning. It doesn't matter anymore. The tape is running out, the room is getting cold, and the only honest thing left is this: we were here, we tried, and it wasn't enough. It was never going to be enough. And that is not tragedy. That is just the truth, sitting there in the corner, smoking, waiting for you to finally look it in the eye."""

config = build_config(
mood="ethereal",
bpm=73,
seed=2026,
vocal_engine="edge-tts",
vocal_text=LYRICS,
extra_overrides={"release": True, "voice": "headache"},
)

pipeline = MoodMachinePipeline(config)
result = pipeline.generate()

print("\n-- Result --")
for key in ("output_path", "mp3_path", "release_zip", "release_metadata", "lyrics_path"):
if key in result:
print(f" {key}: {result[key]}")
14 changes: 11 additions & 3 deletions generate_track.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,15 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
p.add_argument(
"--vocal-engine",
"-v",
default="gtts",
choices=["gtts", "pyttsx3", "silent"],
help="TTS engine for vocals (default: gtts)",
default="edge-tts",
choices=["edge-tts", "piper", "gtts", "pyttsx3", "silent"],
help="TTS engine for vocals (default: edge-tts)",
)
p.add_argument(
"--voice",
default=None,
choices=["headache", "spectral", "broken_radio", "clean"],
help="Vocal processing preset (default: from mood config)",
)
p.add_argument(
"--text",
Expand Down Expand Up @@ -101,6 +107,8 @@ def main(argv: list[str] | None = None) -> None:
extra = {}
if args.release:
extra["release"] = True
if args.voice:
extra["voice"] = args.voice

config = build_config(
mood=args.mood,
Expand Down
41 changes: 41 additions & 0 deletions mood_machine/analysis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
Audio analysis and reference-track reverse-engineering toolkit.

Analyse any audio file (WAV / MP3) to extract a sonic fingerprint
(BPM, frequency balance, dynamics, stereo width, etc.), compare
it against a generated track, and get data-driven parameter
suggestions for closing the gap.

Public API::

from mood_machine.analysis import analyze_track, compare_profiles, suggest_parameters

ref = analyze_track("reference.wav")
gen = analyze_track("generated.wav")
report = compare_profiles(ref, gen)
suggestions = suggest_parameters(report, current_config)
"""

from mood_machine.analysis.analyzer import TrackProfile, analyze_batch, analyze_track, load_audio
from mood_machine.analysis.comparator import ComparisonReport, MetricComparison, compare_profiles
from mood_machine.analysis.parameter_mapper import ParameterSuggestion, suggest_parameters
from mood_machine.analysis.report import (
print_analysis_report,
print_comparison_report,
print_suggestions,
)

__all__ = [
"ComparisonReport",
"MetricComparison",
"ParameterSuggestion",
"TrackProfile",
"analyze_batch",
"analyze_track",
"compare_profiles",
"load_audio",
"print_analysis_report",
"print_comparison_report",
"print_suggestions",
"suggest_parameters",
]
Loading