mido
mido copied to clipboard
Iterating over a MidiFile returns messages with incorrect channel
The documentation says:
Iterating over a
MidiFile
object will generate all MIDI messages in the file in playback order. Thetime
attribute of each message is the number of seconds since the last message or the start of the file.
However, when doing this, all the messages are returned with channel=0
.
I assume this is because __iter__
is implemented with a call to merge_tracks(self.tracks)
.
This creates a single track with all the messages, but all the original track info is lost.
I attached a file with many channels. Each channel has a control_change
message at time=0
.
You can reproduce the issue like this:
from mido import MidiFile
for msg in MidiFile(filename):
print(msg)
Your file is read correctly, but I understand the confusion. The reason why you're getting channel=0
is that Mido numbers channels 0-15 instead of 1-16, so a channel=0
is the same as channel 1 on the device.
This has caused a lot of confusion and I would not have designed it like this today, but unfortunately I can't see a way to change it without breaking a lot of code.
Hi @olemb , have you looked at the file? It has messages on all the channels, not just on the first channel. According to your comment, Mido should show messages on channels 0-15. Instead, it shows all the messages as if they're on channel 0.
The file you have provided has data on different tracks but all have the same MIDI channel.
The two concepts are different and I think you're conflating them. Tracks can hold messages for any MIDI channel and channels are not tied to any particular track. Channels must be between 0 and 15 while the number of tracks is virtually unlimited.
As an illustration, if I produce a file with messages sent on different channels for each track:
from mido import Message, MidiFile, MidiTrack
mid = MidiFile()
for i in range(16):
track = MidiTrack()
mid.tracks.append(track)
track.append(Message('control_change', channel=i, control=121, value=0))
mid.save('tracks_are_not_midi_channels.mid')
I get the result you seem to expect:
from mido import MidiFile
mid = MidiFile('tracks_are_not_midi_channels.mid')
MidiFile(type=1, ticks_per_beat=480, tracks=[
MidiTrack([
Message('control_change', channel=0, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=1, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=2, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=3, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=4, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=5, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=6, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=7, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=8, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=9, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=10, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=11, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=12, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=13, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=14, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)]),
MidiTrack([
Message('control_change', channel=15, control=121, value=0, time=0),
MetaMessage('end_of_track', time=0)])
])
Iterating over them and printing the results yields:
control_change channel=0 control=121 value=0 time=0
control_change channel=1 control=121 value=0 time=0
control_change channel=2 control=121 value=0 time=0
control_change channel=3 control=121 value=0 time=0
control_change channel=4 control=121 value=0 time=0
control_change channel=5 control=121 value=0 time=0
control_change channel=6 control=121 value=0 time=0
control_change channel=7 control=121 value=0 time=0
control_change channel=8 control=121 value=0 time=0
control_change channel=9 control=121 value=0 time=0
control_change channel=10 control=121 value=0 time=0
control_change channel=11 control=121 value=0 time=0
control_change channel=12 control=121 value=0 time=0
control_change channel=13 control=121 value=0 time=0
control_change channel=14 control=121 value=0 time=0
control_change channel=15 control=121 value=0 time=0
MetaMessage('end_of_track', time=0)
mido is behaving correctly in both instances but you had the wrong expectations!
Thanks for following up on this. Can't say I remember what I was originally thinking, but your explanation makes sense!
On Thu, Jan 19, 2023, 18:32 Raphaël Doursenaud @.***> wrote:
The file you have provided has data on different tracks but all have the same MIDI channel.
The two concepts are different and I think you're conflating them. Tracks can hold messages for any MIDI channel and channels are not tied to any particular track. Channels must be between 0 and 15 while the number of tracks is virtually unlimited.
As an illustration, if I produce a file with messages sent on different channels for each track:
from mido import Message, MidiFile, MidiTrack mid = MidiFile()for i in range(16): track = MidiTrack() mid.tracks.append(track) track.append(Message('control_change', channel=i, control=121, value=0)) mid.save('tracks_are_not_midi_channels.mid')
I get the result you seem to expect:
from mido import MidiFile mid = MidiFile('tracks_are_not_midi_channels.mid')
MidiFile(type=1, ticks_per_beat=480, tracks=[ MidiTrack([ Message('control_change', channel=0, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=1, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=2, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=3, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=4, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=5, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=6, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=7, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=8, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=9, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=10, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=11, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=12, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=13, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=14, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]), MidiTrack([ Message('control_change', channel=15, control=121, value=0, time=0), MetaMessage('end_of_track', time=0)]) ])
Iterating over them and printing the results yields:
control_change channel=0 control=121 value=0 time=0control_change channel=1 control=121 value=0 time=0control_change channel=2 control=121 value=0 time=0control_change channel=3 control=121 value=0 time=0control_change channel=4 control=121 value=0 time=0control_change channel=5 control=121 value=0 time=0control_change channel=6 control=121 value=0 time=0control_change channel=7 control=121 value=0 time=0control_change channel=8 control=121 value=0 time=0control_change channel=9 control=121 value=0 time=0control_change channel=10 control=121 value=0 time=0control_change channel=11 control=121 value=0 time=0control_change channel=12 control=121 value=0 time=0control_change channel=13 control=121 value=0 time=0control_change channel=14 control=121 value=0 time=0control_change channel=15 control=121 value=0 time=0MetaMessage('end_of_track', time=0)
mido is behaving correctly in both instances but you had the wrong expectations!
— Reply to this email directly, view it on GitHub https://github.com/mido/mido/issues/250#issuecomment-1397264730, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAOWLC5YV2HQ4EKBSVJ3CKDWTFUBFANCNFSM4TKDFY2A . You are receiving this because you authored the thread.Message ID: @.***>