2

What I want to do is the following: Create a MIDI file, but only in memory, and feed it into pygame.mixer.music.load(). Here is what I've been trying (I'm using MidiFile from here):

import pygame.mixer
import MidiFile3
import io

pygame.mixer.init()

midi = MidiFile3.MIDIFile(1) # midi file with one track
midi.addTrackName(0,0,"Track 1") #Track Name on track 0, position 0
midi.addTempo(0,0,120) #Tempo to 120 bpm on track 0, position 0

for i in range(8):
    midi.addNote(0,0,60,i,1,100) #add 8 c1-notes to the track

bytestream = io.BytesIO()
midi.writeFile(bytestream)
pygame.mixer.music.load(bytestream.getvalue())

Here I get the error message

pygame.error: File path 'MThd' contains null characters

When I do

bytestream = open('file.mid','wb')
midi.writeFile(bytestream)
bytestream.close()
pygame.mixer.music.load('file.mid')

it works as expected.

Of course, I know there is a difference between string containing a file name and a byte-string containing what the file contains. But I found this Post on Daniweb which uses bytestreams and the example works for me.

I just can't figure out how to get my example to work.

ravenfrost
  • 143
  • 1
  • 1
  • 8

3 Answers3

1

The music.load() function expectes a file name or a file object.

bytestream.getvalue() is the contents of the byte stream, which looks like a file name.

You have to use the file/BytesIO object itself:

pygame.mixer.music.load(bytestream)
CL.
  • 173,858
  • 17
  • 217
  • 259
  • When I do that I get `pygame.error: Couldn't read from RWops`. Whatever that is supposed to mean. – ravenfrost Jun 07 '15 at 10:27
  • I just found [this](http://sdl.beuc.net/sdl.wiki/SDL_RWops), but I was wondering why the example on daniweb.com [here](https://www.daniweb.com/software-development/python/code/216979/embed-and-play-midi-music-in-your-code-python#post1975147) works without using a file. – ravenfrost Jun 07 '15 at 12:41
1

I fixed the issue as follows:

bytestream = io.BytesIO()
midi.writeFile(bytestream)
temp = io.BytesIO(bytestream.getvalue())
pygame.mixer.music.load(temp)

As it seems, the writeFile() operation somehow makes the BytesIO object invalid to the pygame.mixer.music.load() method. By using the constructor of BytesIO again with the correctly created bytestream we magically get a valid bytestream we can pass to pygame.mixer.music.load().

ravenfrost
  • 143
  • 1
  • 1
  • 8
  • You are creating a new bytestream from the old bytestream, which is another, possibly expensive, step. The stream pointer just has to be reset to the beginning with seek(0) as I explain in my answer. – RufusVS Apr 27 '17 at 03:37
0

The accepted answer, while it works, is not the proper solution. All you need to do after writing the bytestream is reset the pointer to the beginning. The proper code (section) follows:

bytestream = io.BytesIO()
midi.writeFile(bytestream)
bytestream.seek(0)
pygame.mixer.music.load(bytestream)

I had actually answered a similar question here.

Community
  • 1
  • 1
RufusVS
  • 4,008
  • 3
  • 29
  • 40