Skip to content

Commit

Permalink
adjust tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cainky committed Dec 28, 2023
1 parent c86f4ef commit c26eb46
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 230 deletions.
112 changes: 46 additions & 66 deletions midigen/midigen.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import List, Tuple
from typing import Tuple
import os, time
from music21 import scale as m21_scale
from mido import Message, MidiFile, MidiTrack, MetaMessage, bpm2tempo
from midigen.key import Key, VALID_KEYS
from mido import Message, MidiFile, MetaMessage, bpm2tempo
from midigen.key import Key
from midigen.track import Track


Expand All @@ -21,18 +21,18 @@ def __init__(
:param key_signature: The key signature as a string, e.g., 'C' for C major.
"""
self.midi_file = MidiFile()

self.tracks = []
self.active_track_index = None

self.tempo = tempo
self.time_signature = time_signature
self.key_signature = key_signature if key_signature else Key("C")

self.set_tempo(tempo)
self.set_time_signature(*time_signature)
self.set_tempo(self.tempo)
self.set_time_signature(*self.time_signature)
self.set_key_signature(self.key_signature)

# Automatically add a default track
self.active_track_index = None
self.add_track()

def __str__(self):
Expand All @@ -44,15 +44,6 @@ def __str__(self):
f"Track: {self.track}\nTempo: {self.tempo}\n \
Time Signature: {self.time_signature}\nKey Signature: {self.key_signature}"
)

@property
def midi_file(self) -> MidiFile:
"""
Get the MIDI file.
:return: The MIDI file.
"""
return self.midi_file

def add_track(self):
"""
Expand All @@ -61,6 +52,8 @@ def add_track(self):
:return: The newly created Track instance.
"""
new_track = Track()
new_track.apply_global_settings(self.tempo, self.time_signature, self.key_signature)

self.tracks.append(new_track)
self.active_track_index = len(self.tracks) - 1 # Set the new track as active
return new_track
Expand All @@ -87,44 +80,25 @@ def set_active_track(self, track_index):
raise IndexError("Track index out of range.")

self.active_track_index = track_index

def set_mode(self, key_signature: Key, mode: str) -> None:
"""
Set the mode of the MidiGen object.
:param key_signature: The key signature, e.g. 'C'.
:param mode: The mode, e.g. 'major', 'dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian'.
"""
allowed_modes = ['major', 'dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian']
if mode not in allowed_modes:
raise ValueError(f"Invalid mode. Please use a valid mode from the list: {allowed_modes}")

self.mode = mode
scale_degree = m21_scale.scale_degrees_to_key(key_signature, mode)
key = Key(scale_degree, mode)
self.set_key_signature(key)

def set_tempo(self, tempo: int) -> None:
def set_tempo(self, tempo: int):
"""
Set the tempo for the MIDI file.
Set the tempo for every track in the MIDI file.
Args:
bpm (int): Beats per minute. Must be an integer greater than 0.
Raises:
ValueError: If bpm is not an integer or is less than or equal to 0.
"""
if not isinstance(tempo, int) or tempo <= 0:
raise ValueError("Invalid tempo value: tempo must be a positive integer")

# Remove existing 'set_tempo' messages
self.midi_file.tracks[0] = [msg for msg in self.track if msg.type != "set_tempo"]
self.tempo = bpm2tempo(tempo)
self.track.append(MetaMessage("set_tempo", tempo=self.tempo))

def set_time_signature(self, numerator: int, denominator: int) -> None:
for track in self.tracks:
track.set_tempo(tempo) # Assume Track class has a set_tempo method


def set_time_signature(self, numerator: int, denominator: int):
"""
Set the time signature for the MIDI file.
Set the time signature for every track in the MIDI file.
Args:
numerator (int): The numerator of the time signature. Must be an integer greater than 0.
Expand All @@ -133,36 +107,42 @@ def set_time_signature(self, numerator: int, denominator: int) -> None:
Raises:
ValueError: If numerator or denominator is not an integer or is less than or equal to 0.
"""
if not (isinstance(numerator, int) and isinstance(denominator, int)):
raise ValueError(
"Invalid time signature value: both numerator and denominator must be integers"
)

if numerator <= 0 or denominator <= 0:
raise ValueError(
"Invalid time signature value: both numerator and denominator must be positive integers"
)

# Remove existing 'time_signature' messages
self.midi_file.tracks[0] = [msg for msg in self.track if msg.type != "time_signature"]

self.time_signature = MetaMessage(
"time_signature", numerator=numerator, denominator=denominator
)
self.track.append(self.time_signature)
if not (isinstance(numerator, int) and isinstance(denominator, int)) or numerator <= 0 or denominator <= 0:
raise ValueError("Invalid time signature values: numerator and denominator must be positive integers")

self.time_signature = (numerator, denominator)
for track in self.tracks:
track.set_time_signature(numerator, denominator)

def set_key_signature(self, key: Key) -> None:
def set_key_signature(self, key: Key):
"""
Set the key signature for the MIDI file.
Set the key signature for every track in the MIDI file.
Args:
key (Key): The key signature. Must be a valid key signature (see Key class).
Raises:
ValueError: If key is not a valid key signature string.
"""
self.key_signature = key
self.track.append(MetaMessage("key_signature", key=str(key)))
if not isinstance(key, Key):
raise ValueError("Invalid key signature: must be a Key object")

for track in self.tracks:
track.set_key_signature(key)

def set_mode(self, key_signature: Key, mode: str) -> None:
"""
Set the mode of the MidiGen object.
:param key_signature: The key signature, e.g. 'C'.
:param mode: The mode, e.g. 'major', 'dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian'.
"""
allowed_modes = ['major', 'dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian']
if mode not in allowed_modes:
raise ValueError(f"Invalid mode. Please use a valid mode from the list: {allowed_modes}")

self.mode = mode
scale_degree = m21_scale.scale_degrees_to_key(key_signature, mode)
key = Key(scale_degree, mode)
self.set_key_signature(key)

def save(self, filename: str) -> None:
"""
Expand Down
34 changes: 31 additions & 3 deletions midigen/track.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from mido import MidiTrack, Message
from mido import MidiTrack, Message, MetaMessage, bpm2tempo
from typing import List


from midigen.chord import Chord, ChordProgression, Arpeggio
from midigen.scale import Scale
from midigen.key import Key
from midigen.note import Note
from midigen.drums import DrumKit

Expand All @@ -18,12 +19,16 @@ def __init__(self):
self.track = MidiTrack()
self.notes = []


def get_notes(self) -> List[Note]:
return self.notes

def get_track(self):
return self.track

def apply_global_settings(self, tempo, time_signature, key_signature):
self.track.append(MetaMessage('set_tempo', tempo=bpm2tempo(tempo)))
self.track.append(MetaMessage('time_signature', numerator=time_signature[0], denominator=time_signature[1]))
self.track.append(MetaMessage('key_signature', key=str(key_signature)))

def add_program_change(self, channel: int, program: int) -> None:
"""
Expand Down Expand Up @@ -173,3 +178,26 @@ def quantize(self, time_value: int, quantization_value: int) -> int:
raise ValueError(f"Quantization value must not exceed maximum MIDI ticks: {MAX_MIDI_TICKS}")

return round(time_value / quantization_value) * quantization_value


def set_tempo(self, tempo: int) -> None:
if not isinstance(tempo, int) or tempo <= 0:
raise ValueError("Invalid tempo value: tempo must be a positive integer")

tempo_meta = bpm2tempo(tempo)
self.track = [msg for msg in self.track if msg.type != "set_tempo"]
self.track.append(MetaMessage('set_tempo', tempo=tempo_meta))

def set_time_signature(self, numerator: int, denominator: int) -> None:
if not (isinstance(numerator, int) and isinstance(denominator, int)) or numerator <= 0 or denominator <= 0:
raise ValueError("Invalid time signature values: numerator and denominator must be positive integers")

self.track = [msg for msg in self.track if msg.type != "time_signature"]
self.track.append(MetaMessage('time_signature', numerator=numerator, denominator=denominator))

def set_key_signature(self, key: Key) -> None:
if not isinstance(key, Key):
raise ValueError("Invalid key signature: must be a Key object")

self.track = [msg for msg in self.track if msg.type != "key_signature"]
self.track.append(MetaMessage('key_signature', key=str(key)))
Loading

0 comments on commit c26eb46

Please sign in to comment.