
Description of the Python Script
This Python script generates a MIDI file based on musical notes generated according to a specified musical genre. It utilizes the music21
library for music theory and MIDI file handling, along with the random
and os
modules.
Overview
The script consists of two main functions:
generate_midi(notes, filename)
: Creates a MIDI file from a list of note names.generate_notes_by_genre(genre, length)
: Generates a list of note names based on the specified musical genre.
At the end of the script, there is an example usage where the user is prompted to enter a genre, and the script generates a MIDI file named genre_output.mid
based on that genre.
Detailed Breakdown
Import Statements
from music21 import *
import random
import os
music21
: A Python library for computer-aided musicology, which provides tools to analyze, create, and manipulate music.random
: Used to generate random choices for notes and scales.os
: Provides functions for interacting with the operating system, such as file path manipulation.
Function: generate_midi(notes, filename="output.mid")
Purpose
Generates a MIDI file from a list of note names.
Parameters
notes
: A list of note names as strings (e.g.,['C4', 'D4', 'E4']
).filename
: The name of the MIDI file to create. Defaults to"output.mid"
.
Function Logic
Create a Stream: Initializes a
Stream
object frommusic21
to hold musical elements.
midi_stream = stream.Stream()
2. Add Notes to the Stream: Iterates over the list of note names, creates a Note
object for each, and appends it to the stream.
for note_name in notes:
n = note.Note(note_name)
midi_stream.append(n)
3. Translate Stream to MIDI: Converts the stream into a MIDI file object.
mf = midi.translate.streamToMidiFile(midi_stream)
4. Determine File Path: Gets the current working directory and constructs the full file path for the MIDI file.
current_directory = os.getcwd()
file_path = os.path.join(current_directory, filename)
5. Write MIDI File: Opens the file in write-binary mode, writes the MIDI data, and closes the file.
mf.open(file_path, 'wb')
mf.write()
mf.close()
6. Confirmation Message: Prints a message indicating the MIDI file has been created. The message is in Czech:
print(f"Soubor MIDI byl vytvořen v: {file_path}")
Translation: „The MIDI file was created at: [file_path]“
Function: generate_notes_by_genre(genre, length=10)
Purpose
Generates a list of note names based on the specified musical genre.
Parameters
genre
: A string indicating the musical genre (e.g., „rock“, „pop“).length
: The number of notes to generate. Defaults to10
.
Function Logic
Define Scales and Rhythms Based on Genre:
Rock:
scales = [scale.MinorScale('A'), scale.HarmonicMinorScale('E')]
rhythms = [[0.5, 0.5, 1], [1, 0.5, 0.5], [0.25, 0.25, 0.25, 0.25, 1]]
Pop:
scales = [scale.MajorScale('C'), scale.MajorScale('G')]
rhythms = [[0.5, 0.5, 1], [1, 1], [0.5, 0.5, 0.5, 0.5]]
Other Genres (Default):
scales = [scale.MajorScale('C')]
rhythms = [[1]]
2. Initialize Notes List and Select a Scale:
notes = []
current_scale = random.choice(scales)
3. Generate Notes:
- Loop
length
times to generate the specified number of notes. - For each iteration:
- Randomly select a pitch from the
current_scale
. - Randomly select a rhythm pattern from
rhythms
. - For each duration in the selected rhythm pattern, append the note’s name to the
notes
list.
- Randomly select a pitch from the
for _ in range(length):
note_pitch = random.choice(current_scale.getPitches())
note_duration = random.choice(rhythms)
for duration in note_duration:
notes.append(str(note_pitch))
Note: In this implementation, the note durations are not utilized when generating the MIDI file; only the note pitches are used.
4. Return the List of Notes:
return notes
Example Usage
# Example usage:
genre = input("Zadejte žánr (rock, pop, nebo jiný a stiskněte Enter): ")
notes = generate_notes_by_genre(genre, length=20) # Generate 20 notes
generate_midi(notes, filename="genre_output.mid")
Explanation
User Input:
The script prompts the user to enter a genre. The prompt is in Czech:
"Zadejte žánr (rock, pop, nebo jiný a stiskněte Enter): "
Translation: „Enter a genre (rock, pop, or other and press Enter): „
2. Generate Notes:
- Calls
generate_notes_by_genre
with the user’s input and generates a list of 20 notes.
3. Generate MIDI File:
- Calls
generate_midi
with the generated notes and creates a MIDI file named"genre_output.mid"
.
How to Use the Script
Install Dependencies:
Ensure you have
music21
installed:
pip install music21
2. Run the Script:
Execute the script in a Python environment.
python script_name.py
3. Enter a Genre:
- When prompted, input one of the supported genres:
"rock"
,"pop"
, or any other text for the default option.
- When prompted, input one of the supported genres:
4. Output:
- The script generates a MIDI file named
genre_output.mid
in the current working directory. - A confirmation message displays the path to the created MIDI file.
Notes and Considerations
Genre Customization:
- The script currently supports „rock“ and „pop“ genres with predefined scales and rhythms.
- For „rock“, it uses minor scales and harmonic minor scales.
- For „pop“, it uses major scales.
- Any other input defaults to a simple C major scale.
Rhythm Handling:
- While rhythms are defined in the
rhythms
lists, the current implementation does not apply note durations when adding notes to the MIDI file. To incorporate rhythms, additional code is needed to set the duration of each note.
- While rhythms are defined in the
Internationalization:
Some prompts and messages are in Czech. You may translate them to English or any other language as needed.
Input Prompt:
genre = input("Enter a genre (rock, pop, or other and press Enter): ")
Confirmation Message:
print(f"The MIDI file was created at: {file_path}")
Enhancements:
Incorporate Note Durations:
- Modify the
generate_midi
function to handle note durations based on the rhythm patterns.
- Modify the
Expand Genre Support:
- Add more genres with appropriate scales and rhythms.
Error Handling:
- Add validation for user input and handle potential errors gracefully.
User Interface:
- Create a graphical user interface (GUI) or a command-line menu for better usability.
Example Modification to Include Durations
To make the script utilize note durations from the rhythm patterns, you can modify the generate_notes_by_genre
function to return a list of tuples containing note names and durations.
Modified generate_notes_by_genre
Function
def generate_notes_by_genre(genre, length=10):
"""Generates notes with durations based on the specified genre."""
if genre.lower() == "rock":
# Rock scales and rhythms
scales = [scale.MinorScale('A'), scale.HarmonicMinorScale('E')]
rhythms = [[0.5, 0.5, 1], [1, 0.5, 0.5], [0.25, 0.25, 0.25, 0.25, 1]]
elif genre.lower() == "pop":
# Pop scales and rhythms
scales = [scale.MajorScale('C'), scale.MajorScale('G')]
rhythms = [[0.5, 0.5, 1], [1, 1], [0.5, 0.5, 0.5, 0.5]]
else:
# Default to a simple major scale
scales = [scale.MajorScale('C')]
rhythms = [[1]]
notes = []
current_scale = random.choice(scales)
for _ in range(length):
note_pitch = random.choice(current_scale.getPitches())
note_duration_pattern = random.choice(rhythms)
for duration in note_duration_pattern:
notes.append((str(note_pitch), duration))
return notes
Modified generate_midi
Function
def generate_midi(notes_with_durations, filename="output.mid"):
"""Generates a MIDI file from a list of notes with durations.
Args:
notes_with_durations: A list of tuples containing note names and durations.
filename: The name of the MIDI file to create.
"""
midi_stream = stream.Stream()
for note_name, duration in notes_with_durations:
n = note.Note(note_name)
n.quarterLength = duration
midi_stream.append(n)
mf = midi.translate.streamToMidiFile(midi_stream)
current_directory = os.getcwd()
file_path = os.path.join(current_directory, filename)
mf.open(file_path, 'wb')
mf.write()
mf.close()
print(f"The MIDI file was created at: {file_path}")
Adjusted Example Usage
# Example usage:
genre = input("Enter a genre (rock, pop, or other and press Enter): ")
notes = generate_notes_by_genre(genre, length=20) # Generate 20 notes with durations
generate_midi(notes, filename="genre_output.mid")
Conclusion
This script provides a basic framework for generating MIDI files based on musical genres. By leveraging the music21
library, it allows for music theory concepts like scales and rhythms to be incorporated into the generated music. With further development and customization, it can be expanded into a more sophisticated music generation tool.