aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2019-12-09 18:14:29 +0300
committerWohlstand <admin@wohlnet.ru>2019-12-09 18:14:29 +0300
commit473d2ed6d961cbb55b71743cb3e8dc097c3d5d5d (patch)
tree5c86ee57e0b66b2ce8adedd2a8ae28e08efc34a4
parent9a32beb93117e95ae6f4bf88d9bf5c815a774d60 (diff)
downloadlibADLMIDI-473d2ed6d961cbb55b71743cb3e8dc097c3d5d5d.tar.gz
libADLMIDI-473d2ed6d961cbb55b71743cb3e8dc097c3d5d5d.tar.bz2
libADLMIDI-473d2ed6d961cbb55b71743cb3e8dc097c3d5d5d.zip
Added python-pyaudio example
-rwxr-xr-xexamples/python-pyaudio/playmidi.py174
1 files changed, 174 insertions, 0 deletions
diff --git a/examples/python-pyaudio/playmidi.py b/examples/python-pyaudio/playmidi.py
new file mode 100755
index 0000000..aae3a26
--- /dev/null
+++ b/examples/python-pyaudio/playmidi.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python3
+
+import sys
+import time
+import signal
+import getopt
+import pyaudio
+import platform
+
+from ctypes import *
+
+if platform.system() == 'Windows':
+ adl = CDLL("libADLMIDI.dll")
+else:
+ adl = CDLL("libADLMIDI.so")
+
+_FREQUENCY = 44100
+
+playing = True
+
+# Allows to quit by Ctrl+C
+def catch_signal(signum, frame):
+ global playing
+ playing = False
+ print("Caught Ctrl+C!")
+
+
+class MidiPlayer:
+ """A streaming IMF music player."""
+ SAMPLE_SIZE = 2 # 16-bit
+ CHANNELS = 2 # stereo
+ BUFFER_SAMPLE_COUNT = 1024 # Max allowed: 512
+
+ def __init__(self, freq=_FREQUENCY):
+ """Initializes the PyAudio and OPL synth."""
+ self._freq = freq
+ # Prepare PyAudio
+ self._audio = pyaudio.PyAudio()
+ # Prepare buffers.
+ self._data = create_string_buffer(MidiPlayer.BUFFER_SAMPLE_COUNT * MidiPlayer.SAMPLE_SIZE * MidiPlayer.CHANNELS)
+ self._stream = None # Created later.
+ self._opl = adl.adl_init(freq)
+ self._song = None
+
+ def set_bank_no(self, bank_no):
+ if adl.adl_setBank(self._opl, bank_no):
+ print("%s" % c_char_p(adl.adl_errorInfo(self._opl)).value)
+ return False
+ return True
+
+ def set_bank_file(self, bank_file):
+ if adl.adl_openBankFile(self._opl, bank_file) < 0:
+ print("%s" % c_char_p(adl.adl_errorInfo(self._opl)).value)
+ return False
+ return True
+
+ def set_loop_enabled(self, loop_en):
+ print(loop_en)
+ adl.adl_setLoopEnabled(self._opl, 1 if loop_en else 0)
+
+ def _create_stream(self, start=True):
+ """Create a new PyAudio stream."""
+ self._stream = self._audio.open(
+ format=self._audio.get_format_from_width(MidiPlayer.SAMPLE_SIZE),
+ channels=MidiPlayer.CHANNELS,
+ frames_per_buffer=MidiPlayer.BUFFER_SAMPLE_COUNT,
+ rate=self._freq,
+ output=True,
+ start=start, # Don't start playing immediately!
+ stream_callback=self._callback)
+
+ def set_song(self, song):
+ self._song = song
+ if adl.adl_openFile(self._opl, self._song.encode()) < 0:
+ print("%s" % c_char_p(adl.adl_errorInfo(self._opl)).value.decode())
+ return False
+
+ return True
+
+ # noinspection PyUnusedLocal
+ def _callback(self, input_data, frame_count, time_info, status):
+ global playing
+ got = adl.adl_play(self._opl, int(MidiPlayer.BUFFER_SAMPLE_COUNT * 2), cast(self._data, POINTER(c_short)));
+ if got <= 0:
+ playing = False
+ return None, pyaudio.paComplete
+ return self._data, pyaudio.paContinue
+
+ def play(self, repeat=False):
+ global playing
+ """Starts playing the song at the current position."""
+ self.repeat = repeat
+ if self._song is None:
+ playing = False
+ return
+ # If a stream exists and it is not active and not stopped, it needs to be closed and a new one created.
+ if self._stream is not None and not self._stream.is_active() and not self._stream.is_stopped():
+ self._stream.close() # close for good measure.
+ self._stream = None
+
+ # If there's no stream at this point, create one.
+ if self._stream is None:
+ self._create_stream(False)
+ self._stream.start_stream()
+
+ def pause(self):
+ """Stops the PyAudio stream, but does not rewind the playback position."""
+ self._stream.stop_stream()
+
+ def stop(self):
+ """Stops the PyAudio stream and resets the playback position."""
+ self._stream.stop_stream()
+
+ def close(self):
+ """Closes the PyAudio stream and terminates it."""
+ if self._stream is not None:
+ self._stream.stop_stream()
+ self._stream.close()
+ self._stream = None
+ self._audio.terminate()
+ adl.adl_close(self._opl)
+
+
+def usage(arg):
+ print('usage: %s [-b <embedded bank number>|-bf <external bank file>] [-l 0|1] <file>' % arg, file=sys.stderr)
+ sys.exit(2)
+
+if __name__ == '__main__':
+ bank_no = 72
+ bank_file = None
+ enable_loop = False
+ sample_rate = 44100
+
+ opts, args = getopt.getopt(sys.argv[1:], 'b:bg:l:')
+ for o, a in opts:
+ if o == '-b':
+ bank_no = int(a)
+ if o == '-l':
+ enable_loop = int(a) == 1
+ elif o == '-bg':
+ bank_file = a
+
+ if not args:
+ usage(sys.argv[0])
+
+ midi_file = args[0]
+
+ MIDIPlay = MidiPlayer(sample_rate)
+
+ if bank_file:
+ if not MIDIPlay.set_bank_file(bank_file):
+ exit(1)
+ print("Using bank file: %s" % bank_file)
+ else:
+ if not MIDIPlay.set_bank_no(bank_no):
+ exit(1)
+ print("Using embedded bank: %d" % bank_no)
+
+ MIDIPlay.set_loop_enabled(enable_loop)
+
+ if not MIDIPlay.set_song(midi_file):
+ exit(1)
+
+ MIDIPlay.play()
+
+ signal.signal(signal.SIGINT, catch_signal)
+
+ print("Playing MIDI file %s..." % (midi_file))
+ while playing:
+ time.sleep(0.1)
+ MIDIPlay.stop()
+
+ MIDIPlay.close()
+