From a5c08adea813708983f79db510e4e7d138ba5423 Mon Sep 17 00:00:00 2001 From: James Sinton Date: Fri, 21 Oct 2016 22:37:56 -0500 Subject: [PATCH 1/9] Use compressed audio in ffdec --- audioread/__init__.py | 50 ++++++++++++++++++------------------ audioread/ffdec.py | 60 ++++++++++++++++++++++++++++++++++--------- decode.py | 4 ++- 3 files changed, 76 insertions(+), 38 deletions(-) mode change 100644 => 100755 decode.py diff --git a/audioread/__init__.py b/audioread/__init__.py index eecce32..325438d 100644 --- a/audioread/__init__.py +++ b/audioread/__init__.py @@ -68,45 +68,45 @@ def _mad_available(): return True -def audio_open(path): +def audio_open(path=None, audio=None): """Open an audio file using a library that is available on this system. """ # Standard-library WAV and AIFF readers. - from . import rawread - try: - return rawread.RawAudioFile(path) - except DecodeError: - pass + #from . import rawread + #try: + # return rawread.RawAudioFile(path) + #except DecodeError: + # pass # Core Audio. - if _ca_available(): - from . import macca - try: - return macca.ExtAudioFile(path) - except DecodeError: - pass + #if _ca_available(): + # from . import macca + # try: + # return macca.ExtAudioFile(path) + # except DecodeError: + # pass # GStreamer. - if _gst_available(): - from . import gstdec - try: - return gstdec.GstAudioFile(path) - except DecodeError: - pass + #if _gst_available(): + # from . import gstdec + # try: + # return gstdec.GstAudioFile(path) + # except DecodeError: + # pass # MAD. - if _mad_available(): - from . import maddec - try: - return maddec.MadAudioFile(path) - except DecodeError: - pass + #if _mad_available(): + # from . import maddec + # try: + # return maddec.MadAudioFile(path) + # except DecodeError: + # pass # FFmpeg. from . import ffdec try: - return ffdec.FFmpegAudioFile(path) + return ffdec.FFmpegAudioFile(path,audio) except DecodeError: pass diff --git a/audioread/ffdec.py b/audioread/ffdec.py index 622f31d..0dfdf3e 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -22,6 +22,7 @@ import threading import time import os +from cStringIO import StringIO try: import queue except ImportError: @@ -51,6 +52,8 @@ class NotInstalledError(FFmpegError): class ReadTimeoutError(FFmpegError): """Reading from the ffmpeg command-line tool timed out.""" +class NoInputError(FFmpegError): + """Reading from the ffmpeg command-line tool timed out.""" class QueueReaderThread(threading.Thread): """A thread that consumes data from a filehandle and sends the data @@ -100,7 +103,9 @@ def popen_multiple(commands, command_args, *args, **kwargs): class FFmpegAudioFile(object): """An audio file decoded by the ffmpeg command-line utility.""" - def __init__(self, filename, block_size=4096): + def __init__(self, filename=None, audio=None, block_size=4096): + self.filename=filename + self.audio = audio # On Windows, we need to disable the subprocess's crash dialog # in case it dies. Passing SEM_NOGPFAULTERRORBOX to SetErrorMode # disables this behavior. @@ -118,14 +123,26 @@ def __init__(self, filename, block_size=4096): ) try: - self.devnull = open(os.devnull) - self.proc = popen_multiple( - COMMANDS, - ['-i', filename, '-f', 's16le', '-'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=self.devnull, - ) + if filename is not None: + self.devnull = open(os.devnull) + self.proc = popen_multiple( + COMMANDS, + ['-i', filename, '-f', 's16le', '-'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=self.devnull, + ) + elif audio is not None: + self.devnull = open(os.devnull) + self.proc = popen_multiple( + COMMANDS, + ['-i', '-', '-f', 's16le', '-'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + ) + else: + raise NoInputError() except OSError: raise NotInstalledError() @@ -143,7 +160,17 @@ def __init__(self, filename, block_size=4096): # Start another thread to consume the standard output of the # process, which contains raw audio data. - self.stdout_reader = QueueReaderThread(self.proc.stdout, block_size) + if filename is not None: + self.stdout_reader = QueueReaderThread(self.proc.stdout, block_size) + elif audio is not None: + o, e = self.proc.communicate(input=audio.read()) + print e + self.output = StringIO(o) + self.error = StringIO(e) + self.stdout_reader = QueueReaderThread(self.output, block_size) + else: + raise NoInputError() + self.stdout_reader.start() # Read relevant information from stderr. @@ -152,7 +179,12 @@ def __init__(self, filename, block_size=4096): # Start a separate thread to read the rest of the data from # stderr. This (a) avoids filling up the OS buffer and (b) # collects the error output for diagnosis. - self.stderr_reader = QueueReaderThread(self.proc.stderr) + if filename is not None: + self.stderr_reader = QueueReaderThread(self.proc.stderr) + elif audio is not None: + self.stderr_reader = QueueReaderThread(self.error, block_size) + else: + raise NoInputError() self.stderr_reader.start() def read_data(self, timeout=10.0): @@ -191,7 +223,11 @@ def _get_info(self): """ out_parts = [] while True: - line = self.proc.stderr.readline() + if self.filename is not None: + line = self.proc.stderr.readline() + else: + line = self.error.readline() + if not line: # EOF and data not found. raise CommunicationError("stream info not found") diff --git a/decode.py b/decode.py old mode 100644 new mode 100755 index 40d369a..b20c115 --- a/decode.py +++ b/decode.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # This file is part of audioread. # Copyright 2011, Adrian Sampson. # @@ -28,7 +30,7 @@ def decode(filename): sys.exit(1) try: - with audioread.audio_open(filename) as f: + with audioread.audio_open(audio=open(filename,"r")) as f: print('Input file: %i channels at %i Hz; %.1f seconds.' % (f.channels, f.samplerate, f.duration), file=sys.stderr) From 8ca3f7a640a63fc31ee7bdd816bf83453a036630 Mon Sep 17 00:00:00 2001 From: James Sinton Date: Sat, 22 Oct 2016 12:20:21 -0500 Subject: [PATCH 2/9] Updates to handling compressed audio in ffdec --- audioread/ffdec.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/audioread/ffdec.py b/audioread/ffdec.py index 0dfdf3e..a9c322d 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -104,8 +104,9 @@ def popen_multiple(commands, command_args, *args, **kwargs): class FFmpegAudioFile(object): """An audio file decoded by the ffmpeg command-line utility.""" def __init__(self, filename=None, audio=None, block_size=4096): - self.filename=filename - self.audio = audio + self.openFile = True if filename is not None else False + self.readAudio = True if audio is not None else False + # On Windows, we need to disable the subprocess's crash dialog # in case it dies. Passing SEM_NOGPFAULTERRORBOX to SetErrorMode # disables this behavior. @@ -121,9 +122,9 @@ def __init__(self, filename=None, audio=None, block_size=4096): ctypes.windll.kernel32.SetErrorMode( previous_error_mode | SEM_NOGPFAULTERRORBOX ) - + try: - if filename is not None: + if self.openFile: self.devnull = open(os.devnull) self.proc = popen_multiple( COMMANDS, @@ -132,7 +133,7 @@ def __init__(self, filename=None, audio=None, block_size=4096): stderr=subprocess.PIPE, stdin=self.devnull, ) - elif audio is not None: + elif self.readAudio: self.devnull = open(os.devnull) self.proc = popen_multiple( COMMANDS, @@ -160,18 +161,17 @@ def __init__(self, filename=None, audio=None, block_size=4096): # Start another thread to consume the standard output of the # process, which contains raw audio data. - if filename is not None: + if self.openFile: self.stdout_reader = QueueReaderThread(self.proc.stdout, block_size) - elif audio is not None: + self.stdout_reader.start() + elif self.readAudio: o, e = self.proc.communicate(input=audio.read()) print e self.output = StringIO(o) self.error = StringIO(e) - self.stdout_reader = QueueReaderThread(self.output, block_size) else: raise NoInputError() - self.stdout_reader.start() # Read relevant information from stderr. self._get_info() @@ -179,13 +179,9 @@ def __init__(self, filename=None, audio=None, block_size=4096): # Start a separate thread to read the rest of the data from # stderr. This (a) avoids filling up the OS buffer and (b) # collects the error output for diagnosis. - if filename is not None: + if self.openFile: self.stderr_reader = QueueReaderThread(self.proc.stderr) - elif audio is not None: - self.stderr_reader = QueueReaderThread(self.error, block_size) - else: - raise NoInputError() - self.stderr_reader.start() + self.stderr_reader.start() def read_data(self, timeout=10.0): """Read blocks of raw PCM data from the file.""" @@ -196,7 +192,12 @@ def read_data(self, timeout=10.0): # Wait for data to be available or a timeout. data = None try: - data = self.stdout_reader.queue.get(timeout=timeout) + if self.openFile: + data = self.stdout_reader.queue.get(timeout=timeout) + elif self.readAudio: + data = self.output.read() + else: + raise NoInputError() if data: yield data else: @@ -223,10 +224,12 @@ def _get_info(self): """ out_parts = [] while True: - if self.filename is not None: + if self.openFile: line = self.proc.stderr.readline() - else: + elif self.readAudio: line = self.error.readline() + else: + raise NoInputError() if not line: # EOF and data not found. From 1ec95c79cf06b450de70e1ec8a1dc8173df0a760 Mon Sep 17 00:00:00 2001 From: James Sinton Date: Tue, 25 Oct 2016 21:26:31 -0500 Subject: [PATCH 3/9] Use compressed audio in ffdec --- audioread/ffdec.py | 55 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/audioread/ffdec.py b/audioread/ffdec.py index a9c322d..837ef9f 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -58,7 +58,7 @@ class NoInputError(FFmpegError): class QueueReaderThread(threading.Thread): """A thread that consumes data from a filehandle and sends the data over a Queue. - """ + """ def __init__(self, fh, blocksize=1024, discard=False): super(QueueReaderThread, self).__init__() self.fh = fh @@ -70,12 +70,25 @@ def __init__(self, fh, blocksize=1024, discard=False): def run(self): while True: data = self.fh.read(self.blocksize) + #print data if not self.discard: self.queue.put(data) if not data: # Stream closed (EOF). break +class WriterThread(threading.Thread): + """A thread that writes data to a filehandle + """ + def __init__(self, fh, audio=None): + super(WriterThread, self).__init__() + self.fh = fh + self.audio = audio + self.daemon = True + + def run(self): + self.fh.write(self.audio.read()) + self.fh.close() def popen_multiple(commands, command_args, *args, **kwargs): """Like `subprocess.Popen`, but can try multiple commands in case @@ -159,29 +172,24 @@ def __init__(self, filename=None, audio=None, block_size=4096): finally: windows_error_mode_lock.release() + # Start a thread to write the compressed audio to Popen.stdin + if self.readAudio: + self.stdin_writer = WriterThread(self.proc.stdin,audio) + self.stdin_writer.start() + # Start another thread to consume the standard output of the # process, which contains raw audio data. - if self.openFile: - self.stdout_reader = QueueReaderThread(self.proc.stdout, block_size) - self.stdout_reader.start() - elif self.readAudio: - o, e = self.proc.communicate(input=audio.read()) - print e - self.output = StringIO(o) - self.error = StringIO(e) - else: - raise NoInputError() - - + self.stdout_reader = QueueReaderThread(self.proc.stdout, block_size) + self.stdout_reader.start() + # Read relevant information from stderr. self._get_info() # Start a separate thread to read the rest of the data from # stderr. This (a) avoids filling up the OS buffer and (b) # collects the error output for diagnosis. - if self.openFile: - self.stderr_reader = QueueReaderThread(self.proc.stderr) - self.stderr_reader.start() + self.stderr_reader = QueueReaderThread(self.proc.stderr) + self.stderr_reader.start() def read_data(self, timeout=10.0): """Read blocks of raw PCM data from the file.""" @@ -192,12 +200,8 @@ def read_data(self, timeout=10.0): # Wait for data to be available or a timeout. data = None try: - if self.openFile: - data = self.stdout_reader.queue.get(timeout=timeout) - elif self.readAudio: - data = self.output.read() - else: - raise NoInputError() + data = self.stdout_reader.queue.get(timeout=timeout) + if data: yield data else: @@ -224,12 +228,7 @@ def _get_info(self): """ out_parts = [] while True: - if self.openFile: - line = self.proc.stderr.readline() - elif self.readAudio: - line = self.error.readline() - else: - raise NoInputError() + line = self.proc.stderr.readline() if not line: # EOF and data not found. From 2b1c58186bfbec04ae6aa1aae2776e5aecbf3621 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 26 Oct 2016 17:47:17 -0400 Subject: [PATCH 4/9] Resolve some "diff noise" --- audioread/ffdec.py | 12 ++++-------- decode.py | 4 +--- 2 files changed, 5 insertions(+), 11 deletions(-) mode change 100755 => 100644 decode.py diff --git a/audioread/ffdec.py b/audioread/ffdec.py index 837ef9f..fc498f0 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -22,7 +22,6 @@ import threading import time import os -from cStringIO import StringIO try: import queue except ImportError: @@ -58,7 +57,7 @@ class NoInputError(FFmpegError): class QueueReaderThread(threading.Thread): """A thread that consumes data from a filehandle and sends the data over a Queue. - """ + """ def __init__(self, fh, blocksize=1024, discard=False): super(QueueReaderThread, self).__init__() self.fh = fh @@ -70,7 +69,6 @@ def __init__(self, fh, blocksize=1024, discard=False): def run(self): while True: data = self.fh.read(self.blocksize) - #print data if not self.discard: self.queue.put(data) if not data: @@ -135,7 +133,7 @@ def __init__(self, filename=None, audio=None, block_size=4096): ctypes.windll.kernel32.SetErrorMode( previous_error_mode | SEM_NOGPFAULTERRORBOX ) - + try: if self.openFile: self.devnull = open(os.devnull) @@ -176,12 +174,12 @@ def __init__(self, filename=None, audio=None, block_size=4096): if self.readAudio: self.stdin_writer = WriterThread(self.proc.stdin,audio) self.stdin_writer.start() - + # Start another thread to consume the standard output of the # process, which contains raw audio data. self.stdout_reader = QueueReaderThread(self.proc.stdout, block_size) self.stdout_reader.start() - + # Read relevant information from stderr. self._get_info() @@ -201,7 +199,6 @@ def read_data(self, timeout=10.0): data = None try: data = self.stdout_reader.queue.get(timeout=timeout) - if data: yield data else: @@ -229,7 +226,6 @@ def _get_info(self): out_parts = [] while True: line = self.proc.stderr.readline() - if not line: # EOF and data not found. raise CommunicationError("stream info not found") diff --git a/decode.py b/decode.py old mode 100755 new mode 100644 index b20c115..40d369a --- a/decode.py +++ b/decode.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # This file is part of audioread. # Copyright 2011, Adrian Sampson. # @@ -30,7 +28,7 @@ def decode(filename): sys.exit(1) try: - with audioread.audio_open(audio=open(filename,"r")) as f: + with audioread.audio_open(filename) as f: print('Input file: %i channels at %i Hz; %.1f seconds.' % (f.channels, f.samplerate, f.duration), file=sys.stderr) From da0491a5a9223574a80bcbe79b2e9599274bdb7c Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 26 Oct 2016 17:56:56 -0400 Subject: [PATCH 5/9] New APIs for decoding audio data from a stream --- audioread/__init__.py | 65 ++++++++++++++++++++++++++----------------- decode.py | 29 +++++++++++++------ 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/audioread/__init__.py b/audioread/__init__.py index 325438d..5ecf0d4 100644 --- a/audioread/__init__.py +++ b/audioread/__init__.py @@ -68,45 +68,60 @@ def _mad_available(): return True -def audio_open(path=None, audio=None): +def audio_open(path): """Open an audio file using a library that is available on this system. """ # Standard-library WAV and AIFF readers. - #from . import rawread - #try: - # return rawread.RawAudioFile(path) - #except DecodeError: - # pass + from . import rawread + try: + return rawread.RawAudioFile(path) + except DecodeError: + pass # Core Audio. - #if _ca_available(): - # from . import macca - # try: - # return macca.ExtAudioFile(path) - # except DecodeError: - # pass + if _ca_available(): + from . import macca + try: + return macca.ExtAudioFile(path) + except DecodeError: + pass # GStreamer. - #if _gst_available(): - # from . import gstdec - # try: - # return gstdec.GstAudioFile(path) - # except DecodeError: - # pass + if _gst_available(): + from . import gstdec + try: + return gstdec.GstAudioFile(path) + except DecodeError: + pass # MAD. - #if _mad_available(): - # from . import maddec - # try: - # return maddec.MadAudioFile(path) - # except DecodeError: - # pass + if _mad_available(): + from . import maddec + try: + return maddec.MadAudioFile(path) + except DecodeError: + pass + + # FFmpeg. + from . import ffdec + try: + return ffdec.FFmpegAudioFile(path) + except DecodeError: + pass + # All backends failed! + raise NoBackendError() + + +def decode(audio): + """Given a file-like object containing encoded audio data, create an + audio file object that produces its *raw* data. + """ # FFmpeg. from . import ffdec try: - return ffdec.FFmpegAudioFile(path,audio) + return ffdec.FFmpegAudioFile(audio=audio) except DecodeError: pass diff --git a/decode.py b/decode.py index 40d369a..dfba250 100644 --- a/decode.py +++ b/decode.py @@ -21,21 +21,31 @@ import contextlib -def decode(filename): - filename = os.path.abspath(os.path.expanduser(filename)) - if not os.path.exists(filename): - print("File not found.", file=sys.stderr) - sys.exit(1) +def decode(filename=None): + """Decode audio from a file on disk or, if no file is specified, + from the standard input. + """ + if filename: + filename = os.path.abspath(os.path.expanduser(filename)) + if not os.path.exists(filename): + print("File not found.", file=sys.stderr) + sys.exit(1) try: - with audioread.audio_open(filename) as f: + if filename: + f = audioread.audio_open(filename) + else: + f = audioread.decode(sys.stdin) + + with f: print('Input file: %i channels at %i Hz; %.1f seconds.' % (f.channels, f.samplerate, f.duration), file=sys.stderr) print('Backend:', str(type(f).__module__).split('.')[1], file=sys.stderr) - with contextlib.closing(wave.open(filename + '.wav', 'w')) as of: + outname = filename or 'out' + with contextlib.closing(wave.open(outname + '.wav', 'w')) as of: of.setnchannels(f.channels) of.setframerate(f.samplerate) of.setsampwidth(2) @@ -49,4 +59,7 @@ def decode(filename): if __name__ == '__main__': - decode(sys.argv[1]) + if sys.argv[1:]: + decode(sys.argv[1]) + else: + decode() From ff7c15ca8b05eb7d1ec1cab2a10abec755cb327b Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 26 Oct 2016 18:12:44 -0400 Subject: [PATCH 6/9] Simplify some duplicated code --- audioread/ffdec.py | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/audioread/ffdec.py b/audioread/ffdec.py index fc498f0..8caba75 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -51,8 +51,6 @@ class NotInstalledError(FFmpegError): class ReadTimeoutError(FFmpegError): """Reading from the ffmpeg command-line tool timed out.""" -class NoInputError(FFmpegError): - """Reading from the ffmpeg command-line tool timed out.""" class QueueReaderThread(threading.Thread): """A thread that consumes data from a filehandle and sends the data @@ -75,6 +73,7 @@ def run(self): # Stream closed (EOF). break + class WriterThread(threading.Thread): """A thread that writes data to a filehandle """ @@ -88,6 +87,7 @@ def run(self): self.fh.write(self.audio.read()) self.fh.close() + def popen_multiple(commands, command_args, *args, **kwargs): """Like `subprocess.Popen`, but can try multiple commands in case some are not available. @@ -115,8 +115,17 @@ def popen_multiple(commands, command_args, *args, **kwargs): class FFmpegAudioFile(object): """An audio file decoded by the ffmpeg command-line utility.""" def __init__(self, filename=None, audio=None, block_size=4096): - self.openFile = True if filename is not None else False - self.readAudio = True if audio is not None else False + """Start decoding an audio file. + + Provide either `filename` to read from the filesystem or + `audio`, a file-like object, to read from an open stream. + """ + if filename: + self.from_file = True + elif audio: + self.from_file = False + else: + raise ValueError('one of `filename` or `audio` must be provided') # On Windows, we need to disable the subprocess's crash dialog # in case it dies. Passing SEM_NOGPFAULTERRORBOX to SetErrorMode @@ -134,27 +143,18 @@ def __init__(self, filename=None, audio=None, block_size=4096): previous_error_mode | SEM_NOGPFAULTERRORBOX ) + # Start the subprocess. try: - if self.openFile: - self.devnull = open(os.devnull) - self.proc = popen_multiple( - COMMANDS, - ['-i', filename, '-f', 's16le', '-'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=self.devnull, - ) - elif self.readAudio: - self.devnull = open(os.devnull) - self.proc = popen_multiple( - COMMANDS, - ['-i', '-', '-f', 's16le', '-'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - ) - else: - raise NoInputError() + in_arg = filename if self.from_file else '-' + self.devnull = open(os.devnull) + in_stream = self.devnull if self.from_file else subprocess.PIPE + self.proc = popen_multiple( + COMMANDS, + ['-i', in_arg, '-f', 's16le', '-'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=in_stream, + ) except OSError: raise NotInstalledError() @@ -170,9 +170,11 @@ def __init__(self, filename=None, audio=None, block_size=4096): finally: windows_error_mode_lock.release() - # Start a thread to write the compressed audio to Popen.stdin - if self.readAudio: - self.stdin_writer = WriterThread(self.proc.stdin,audio) + # If the input data comes from a stream, start a thread to write + # the compressed audio to the subprocess's standard input + # stream. + if not self.from_file: + self.stdin_writer = WriterThread(self.proc.stdin, audio) self.stdin_writer.start() # Start another thread to consume the standard output of the From 0153f6bb2c2ca6970242e500dec6364256f9fe49 Mon Sep 17 00:00:00 2001 From: James Sinton Date: Wed, 26 Oct 2016 21:04:37 -0500 Subject: [PATCH 7/9] Updates to WriterThread --- audioread/ffdec.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/audioread/ffdec.py b/audioread/ffdec.py index 8caba75..367d05f 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -75,17 +75,25 @@ def run(self): class WriterThread(threading.Thread): - """A thread that writes data to a filehandle + """A thread that writes the data of readfile into a writefile + a block at a time """ - def __init__(self, fh, audio=None): + def __init__(self, writefile, readfile=None, blocksize=1024): super(WriterThread, self).__init__() - self.fh = fh - self.audio = audio + self.writefile = writefile + self.readfile = readfile + self.blocksize = blocksize self.daemon = True def run(self): - self.fh.write(self.audio.read()) - self.fh.close() + while True: + data = self.readfile.read(self.blocksize) + if data: + self.writefile.write(data) + else: + # readfile closed (EOF) + break + self.writefile.close() def popen_multiple(commands, command_args, *args, **kwargs): @@ -174,7 +182,7 @@ def __init__(self, filename=None, audio=None, block_size=4096): # the compressed audio to the subprocess's standard input # stream. if not self.from_file: - self.stdin_writer = WriterThread(self.proc.stdin, audio) + self.stdin_writer = WriterThread(self.proc.stdin, audio, block_size) self.stdin_writer.start() # Start another thread to consume the standard output of the From dd5a1d04fcc5f445849af54a58ee411508ae7650 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 26 Oct 2016 22:25:01 -0400 Subject: [PATCH 8/9] A little more documentation --- audioread/ffdec.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/audioread/ffdec.py b/audioread/ffdec.py index 367d05f..2a6ae7d 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -53,12 +53,12 @@ class ReadTimeoutError(FFmpegError): class QueueReaderThread(threading.Thread): - """A thread that consumes data from a filehandle and sends the data - over a Queue. + """A thread that consumes data from a file-like object and sends the + data over a Queue. """ - def __init__(self, fh, blocksize=1024, discard=False): + def __init__(self, file, blocksize=1024, discard=False): super(QueueReaderThread, self).__init__() - self.fh = fh + self.file = file self.blocksize = blocksize self.daemon = True self.discard = discard @@ -66,7 +66,7 @@ def __init__(self, fh, blocksize=1024, discard=False): def run(self): while True: - data = self.fh.read(self.blocksize) + data = self.file.read(self.blocksize) if not self.discard: self.queue.put(data) if not data: @@ -75,10 +75,13 @@ def run(self): class WriterThread(threading.Thread): - """A thread that writes the data of readfile into a writefile - a block at a time + """A thread that reads data from one file-like object and writes it + to another, one block at a time. """ def __init__(self, writefile, readfile=None, blocksize=1024): + """Create a thread that reads data from `readfile` and writes it + to `writefile`. + """ super(WriterThread, self).__init__() self.writefile = writefile self.readfile = readfile @@ -91,7 +94,7 @@ def run(self): if data: self.writefile.write(data) else: - # readfile closed (EOF) + # EOF. break self.writefile.close() From f83a9b2bb2cac11a7288cb7ecbab3f4c33f3d80e Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 26 Oct 2016 22:25:21 -0400 Subject: [PATCH 9/9] Fix a long line --- audioread/ffdec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audioread/ffdec.py b/audioread/ffdec.py index 2a6ae7d..ca5b846 100644 --- a/audioread/ffdec.py +++ b/audioread/ffdec.py @@ -185,7 +185,8 @@ def __init__(self, filename=None, audio=None, block_size=4096): # the compressed audio to the subprocess's standard input # stream. if not self.from_file: - self.stdin_writer = WriterThread(self.proc.stdin, audio, block_size) + self.stdin_writer = WriterThread(self.proc.stdin, audio, + block_size) self.stdin_writer.start() # Start another thread to consume the standard output of the