Question

I use SoX in an application. The application uses it to apply various operations on audiofiles, such as trimming.

This works fine:

from subprocess import Popen, PIPE

kwargs = {'stdin': PIPE, 'stdout': PIPE, 'stderr': PIPE}

pipe = Popen(['sox','-t','mp3','-', 'test.mp3','trim','0','15'], **kwargs)
output, errors = pipe.communicate(input=open('test.mp3','rb').read())
if errors:
    raise RuntimeError(errors)

This will cause problems on large files hower, since read() loads the complete file to memory; which is slow and may cause the pipes' buffer to overflow. A workaround exists:

from subprocess import Popen, PIPE
import tempfile
import uuid
import shutil
import os

kwargs = {'stdin': PIPE, 'stdout': PIPE, 'stderr': PIPE}
tmp = os.path.join(tempfile.gettempdir(), uuid.uuid1().hex + '.mp3')

pipe = Popen(['sox','test.mp3', tmp,'trim','0','15'], **kwargs)
output, errors = pipe.communicate()

if errors:
    raise RuntimeError(errors)

shutil.copy2(tmp, 'test.mp3')
os.remove(tmp)

So the question stands as follows: Are there any alternatives to this approach, aside from writing a Python extension to the Sox C API?

Was it helpful?

Solution

A Python wrapper for SoX already exists: sox. Maybe the simplest solution is to switch to use that instead of invoking external SoX command line utilities through subprocess.

The following achieves what you want in your example using the sox package (see documentation), and should work on Linux and macOS on Python 2.7, 3.4 and 3.5 (it might also work on Windows but I haven't been able to test because I don't have access to a Windows box):

>>> import sox
>>> transformer = sox.Transformer()  # create transformer 
>>> transformer.trim(0, 15)  # trim the audio between 0 and 15 seconds 
>>> transformer.build('test.mp3', 'out.mp3')  # create the output file 

Note: this answer used to mention the no longer maintained pysox package. Thanks to @erik for the tip.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top