I needed a quick way to turn text descriptions into playable melody sketches without leaving the terminal. In this tutorial, I will walk through a small Python tool that uses an LLM to write ABC notation and then converts it to a standard MIDI file. I built this on Oxlo.ai because its request-based pricing keeps the cost flat even when I send long, detailed prompts about mood, key, and tempo.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai - music21 for ABC-to-MIDI conversion:
pip install music21
Step 1: Set up the Oxlo.ai client
I start by importing the OpenAI SDK and pointing it at Oxlo.ai's endpoint. Because Oxlo.ai is fully OpenAI API compatible, this is a drop-in replacement.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
Step 2: Lock down the system prompt for structured music output
LLMs hallucinate less when the output format is rigid. I force the model to return only valid ABC notation with a short header and a single melody line. No markdown, no explanation.
SYSTEM_PROMPT = """You are a music generation engine. The user will describe a mood, style, or melody idea. Respond with exactly one valid ABC notation tune. Do not include markdown fences, explanations, or commentary. Use this minimal header format:
X:1
T:Generated Tune
M:4/4
L:1/8
K:C
[notes]
The K: field should match the requested key. Output only the ABC text."""
Step 3: Generate ABC notation from a text prompt
Now I wrap the API call. I send the system prompt plus the user's description and read back the raw content. I strip any accidental whitespace so the parser does not choke.
def generate_abc(prompt: str) -> str:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
temperature=0.7,
max_tokens=800,
)
abc = response.choices[0].message.content.strip()
# Remove accidental markdown fences if the model ignored instructions
abc = abc.removeprefix("
```").removeprefix("abc").removesuffix("```
")
return abc.strip()
Step 4: Convert ABC notation to a MIDI file
With the ABC string in memory, I write it to a temporary file and use music21 to parse it and write a Type-1 MIDI file. This keeps the tool self-contained.
import os
from music21 import converter
def abc_to_midi(abc_text: str, output_path: str = "output.mid"):
temp_path = "temp_tune.abc"
with open(temp_path, "w", encoding="utf-8") as f:
f.write(abc_text)
try:
score = converter.parse(temp_path)
score.write("midi", fp=output_path)
print(f"Saved MIDI to {output_path}")
finally:
if os.path.exists(temp_path):
os.remove(temp_path)
Step 5: Wrap it in a CLI that writes to disk
Finally, I tie the two functions together so I can run the script from the command line with any prompt.
if __name__ == "__main__":
import sys
user_prompt = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "A cheerful Irish jig in G major"
print(f"Generating: {user_prompt}")
abc = generate_abc(user_prompt)
print("\n--- ABC Output ---\n")
print(abc)
abc_to_midi(abc, "generated.mid")
print("Done.")
Run it
Save the full script as music_gen.py and run it with a description:
python music_gen.py "A slow ambient melody in D minor"
Example output:
Generating: A slow ambient melody in D minor
--- ABC Output ---
X:1
T:Generated Tune
M:4/4
L:1/8
K:Dmin
DEFG ABcd|e2d2 c2A2|G4 F4|E8|
Saved MIDI to generated.mid
Done.
You can open generated.mid in any DAW or media player to hear the result.
Next steps
Add a post-processing step that quantizes note lengths or filters out-of-range pitches before writing the MIDI file. Alternatively, pipe the generated MIDI into a synthesizer like FluidSynth to render a WAV file automatically instead of stopping at the MIDI stage.
Top comments (0)