Skip to main content

Chain together ML models for music, LangChain-style

Project description

🎵 SongChain 🔗

Coverage Tests

LangChain made it easy to string together language models and their data processing. SongChain does the same for music models — voice, audio, and MIDI-based generation — so musicians and developers can compose AI music pipelines as easily as language pipelines.

Key differences from language chaining:

  • Focus on audio rather than language: musicians work with audio and MIDI clips, and craft songs from variations of them
  • Audio and MIDI need their own encodings: clips expose waveforms, spectrograms, piano rolls, note sequences, and EnCodec tokens — the representations music models actually consume
  • Chaining is temporal too: outputs are concatenated, layered, and looped in time to craft larger pieces

Installation

Into your own project (not yet on PyPI — install from GitHub or a local clone):

pip install git+https://github.com/asampat3090/songchain.git   # from GitHub
pip install /path/to/songchain                                  # from a local clone

# optional model backends (work with either form)
pip install "songchain[generation] @ git+https://github.com/asampat3090/songchain.git"   # MusicGen / AudioGen
pip install "/path/to/songchain[transcription]"                  # Basic Pitch, Pop2Piano

For working on songchain itself:

git clone https://github.com/asampat3090/songchain.git && cd songchain
pip install -e ".[dev]"

Then in your downstream code:

# my_project/make_jingle.py
from songchain import Chain, PromptTemplate, concat
from songchain.models import NoteSequence, Transpose, MIDISynth, Fade

jingle_chain = Chain([
    PromptTemplate("{n1}5:0.2 {n2}5:0.2 {n3}5:0.2 {n3}5:0.6"),
    NoteSequence(),
    Transpose(semitones=-5),
    MIDISynth(sample_rate=22050),
    Fade(fade_out=0.3),
])

a = jingle_chain.run(n1="C", n2="E", n3="G")
b = jingle_chain.run(n1="D", n2="F", n3="A")
concat([a, b], crossfade=0.05).save("jingle.wav")

Quickstart

Chain a prompt through MIDI generation, transformation, and synthesis to audio — no pretrained models needed:

from songchain import Chain, PromptTemplate
from songchain.models import NoteSequence, Transpose, MIDISynth, Fade

chain = Chain([
    PromptTemplate("{root}4 {third}4 {fifth}4 {root}5:1.0"),
    NoteSequence(duration=0.25),     # text  -> MIDI
    Transpose(semitones=-12),        # MIDI  -> MIDI
    MIDISynth(sample_rate=44100),    # MIDI  -> audio
    Fade(fade_out=0.5),              # audio -> audio
])

clip = chain.run(root="C", third="E", fifth="G")
clip.save("arpeggio.wav")

Generate music with a pretrained model (requires [generation]):

from songchain import Chain, PromptTemplate, concat
from songchain.models import MusicGen

chain = Chain([
    PromptTemplate("{genre} beat with {mood} vibes, {bpm} BPM"),
    MusicGen(size="small", duration=8.0),
])

verse = chain.run(genre="lofi", mood="chill", bpm=80)
chorus = chain.run(genre="lofi", mood="uplifting", bpm=80)
song = concat([verse, chorus, verse], crossfade=0.5)
song.save("song.wav")

Chains validate modalities up front — a MIDI model feeding an audio effect fails at construction time, before any weights load. Compose chains with |: Chain([...]) | MIDISynth().

Data: AudioClip and MIDIClip

Clips wrap a file (or in-memory data) and expose every ML representation, so models can be swapped without rewriting preprocessing:

from songchain import AudioClip, MIDIClip

audio = AudioClip("song.wav")
audio.waveform          # (channels x frames) float32 tensor
audio.spectrogram       # STFT power spectrogram
audio.mel_spectrogram   # mel-scaled spectrogram
audio.encodec           # EnCodec discrete codes (requires [generation])
audio.to_mono().resample(32000).slice(0, 10).save("intro.wav")

midi = MIDIClip("melody.mid")
midi.pianoroll(type="binary")     # (128 x steps) — MidiNet, MuseGAN style
midi.pianoroll(type="velocity")   # velocity-aware — Music Transformer style
midi.notes(type="index")          # monophonic — MelodyRNN, MusicVAE style
midi.notes(type="note")           # note names — DeepBach style
midi.transpose(5).time_stretch(0.5).synthesize().save("variation.wav")

Models

All models share one interface — declare input/output modality, implement generate — and register by name (get_model("musicgen", duration=8.0)).

Model Chain Backend Extra
NoteSequence text → MIDI built-in
Transpose, MIDITimeStretch MIDI → MIDI built-in
MIDISynth MIDI → audio built-in
Gain, Reverse, Fade audio → audio built-in
MusicGen text → audio Meta audiocraft [generation]
AudioGen text → audio Meta audiocraft [generation]
BasicPitch audio → MIDI Spotify basic-pitch [transcription]
Pop2Piano audio → MIDI HuggingFace [transcription]

Add your own by subclassing a base (e.g. TextToAudioModel) and decorating with @register("name").

Temporal composition

from songchain import concat, overlay, loop, concat_midi

track = concat([intro, verse, chorus], crossfade=0.25)  # join in time
mix = overlay([drums, bass, melody])                    # layer tracks
beat = loop(bar, times=8)                               # repeat
medley = concat_midi([melody_a, melody_b])              # join MIDI

Dataset preparation

Split songs into fixed-size chunks with text descriptions — the layout audiocraft-style fine-tuning expects:

from songchain.util import split_audio_and_save

split_audio_and_save(30, "my_songs/", "chill bollywood beats with vocals")
# -> my_songs/output/000_000.wav, 000_000.txt, ...

End-to-end example

examples/make_song.py composes a complete short song — a four-chord progression (C–Am–F–G) with an arpeggiated melody chain overlaid on a bass chain, arranged bar-by-bar, looped, and faded — and writes both the audio and the MIDI:

python examples/make_song.py
# wrote ./song.wav (15.7s) and ./song.mid

This exact workflow is verified by the integration tests in tests/test_integration.py, which run the example and assert the artifacts are real: audible audio at the right duration, and MIDI that reloads with the full arrangement and can itself be fed back into a chain.

Development

pip install -e ".[dev]"
pytest --cov=songchain          # run tests with coverage
coverage-badge -o coverage.svg -f  # refresh the badge
black songchain tests && flake8    # format + lint

The test suite runs without any heavy model downloads — pretrained backends are exercised through fakes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

songchain-0.2.0.tar.gz (27.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

songchain-0.2.0-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file songchain-0.2.0.tar.gz.

File metadata

  • Download URL: songchain-0.2.0.tar.gz
  • Upload date:
  • Size: 27.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for songchain-0.2.0.tar.gz
Algorithm Hash digest
SHA256 677b0574aabf8d8082950c9820d1b5dbf6ac25fe36c02584f39acf6eaa01dacb
MD5 90ffd032ff085b56f1df861253ccee82
BLAKE2b-256 10aef84f742de2ff6d65b852d302f07c9fbef90c6911968a966837ff11b17f9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for songchain-0.2.0.tar.gz:

Publisher: release.yml on asampat3090/songchain

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file songchain-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: songchain-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 21.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for songchain-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 829434370736e3059d6b075c0a92400c1e9ad72527ba87b219e1922b62ad08d6
MD5 62ee0d78173f52b2c4546c66cebb1e59
BLAKE2b-256 babd7c4e2f0a187275fa0b483f4274ae37a2f3c12086c2fab30b350eb7bd1afa

See more details on using hashes here.

Provenance

The following attestation bundles were made for songchain-0.2.0-py3-none-any.whl:

Publisher: release.yml on asampat3090/songchain

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page