Browse Source

first commit

Tomoya Sagae 2 months ago
commit
223f4353d7
1 changed files with 85 additions and 0 deletions
  1. 85 0
      play_file.py

+ 85 - 0
play_file.py

@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+"""Load an audio file into memory and play its contents.
+
+NumPy and the soundfile module (https://python-soundfile.readthedocs.io/)
+must be installed for this to work.
+
+This example program loads the whole file into memory before starting
+playback.
+To play very long files, you should use play_long_file.py instead.
+
+This example could simply be implemented like this::
+
+    import sounddevice as sd
+    import soundfile as sf
+
+    data, fs = sf.read('my-file.wav')
+    sd.play(data, fs)
+    sd.wait()
+
+... but in this example we show a more low-level implementation
+using a callback stream.
+
+"""
+import argparse
+import threading
+
+import sounddevice as sd
+import soundfile as sf
+
+
+def int_or_str(text):
+    """Helper function for argument parsing."""
+    try:
+        return int(text)
+    except ValueError:
+        return text
+
+
+parser = argparse.ArgumentParser(add_help=False)
+parser.add_argument(
+    '-l', '--list-devices', action='store_true',
+    help='show list of audio devices and exit')
+args, remaining = parser.parse_known_args()
+if args.list_devices:
+    print(sd.query_devices())
+    parser.exit(0)
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter,
+    parents=[parser])
+parser.add_argument(
+    'filename', metavar='FILENAME',
+    help='audio file to be played back')
+parser.add_argument(
+    '-d', '--device', type=int_or_str,
+    help='output device (numeric ID or substring)')
+args = parser.parse_args(remaining)
+
+event = threading.Event()
+
+try:
+    data, fs = sf.read(args.filename, always_2d=True)
+
+    current_frame = 0
+
+    def callback(outdata, frames, time, status):
+        global current_frame
+        if status:
+            print(status)
+        chunksize = min(len(data) - current_frame, frames)
+        outdata[:chunksize] = data[current_frame:current_frame + chunksize]
+        if chunksize < frames:
+            outdata[chunksize:] = 0
+            raise sd.CallbackStop()
+        current_frame += chunksize
+
+    stream = sd.OutputStream(
+        samplerate=fs, device=args.device, channels=data.shape[1],
+        callback=callback, finished_callback=event.set)
+    with stream:
+        event.wait()  # Wait until playback is finished
+except KeyboardInterrupt:
+    parser.exit('\nInterrupted by user')
+except Exception as e:
+    parser.exit(type(e).__name__ + ': ' + str(e))