user3535074 user3535074 - 18 days ago 5
Python Question

Getting usable data in a dictionary from the string output of SOX in Python

The opensource software SOX is a command line interface tool which does things with audio files. It has a stat function which returns data related to an audio file. This data comes back as a string - a string which is not terribly easy to use.

Examples of the strings returned by SOX are below.

\nInput File : 'E:\path\to\file\filename.wav'\nChannels : 1\nSample Rate : 176400\nPrecision : 16-bit\nDuration : 00:00:30.00 = 5292001 samples ~ 2250 CDDA sectors\nFile Size : 10.6M\nBit Rate : 2.82M\nSample Encoding: 16-bit Signed Integer PCM\n"

and...

Samples read: 5292001\nLength (seconds): 30.000006\nScaled by: 2147483647.0\nMaximum amplitude: 0.705475\nMinimum amplitude: -0.705475\nMidline amplitude: 0.000000\nMean norm: 0.449045\nMean amplitude: 0.000153\nRMS amplitude: 0.498788\nMaximum delta: 1.410950\nMinimum delta: 0.000000\nMean delta: 0.571030\nRMS delta: 0.704606\nRough frequency: 39659\nVolume adjustment: 1.417\n\nTry: -t raw -e mu-law -b 8 '

The number of characters a value may have can change from one file to another and some files will actually miss certain values altogether.

How can I get a simple dictionary of values from these strings?

Answer

You could split on '\n' and then feed pairs to the dict constructor by splitting on ':':

Given your second sample string:

>>> s = """Samples read: 5292001\nLength (seconds): 30.000006\nScaled by: 2147483647.0\nMaximum amplitude: 0.705475\nMinimum amplitude: -0.705475\nMidline amplitude: 0.000000\nMean norm: 0.449045\nMean amplitude: 0.000153\nRMS amplitude: 0.498788\nMaximum delta: 1.410950\nMinimum delta: 0.000000\nMean delta: 0.571030\nRMS delta: 0.704606\nRough frequency: 39659\nVolume adjustment: 1.417\n\nTry: -t raw -e mu-law -b 8 '"""

A dictionary can be created by:

>>> dict(r.strip().split(':', 1) for r in s.split('\n') if r)

where if r takes care to filter out empty lines and the 1 in split takes care to perform only one split (so strings like Duration that have many ":" won't get split up multiple times).

This yields:

{'Length (seconds)': ' 30.000006',
 'Maximum amplitude': ' 0.705475',
 'Maximum delta': ' 1.410950',
 'Mean amplitude': ' 0.000153',
 'Mean delta': ' 0.571030',
 'Mean norm': ' 0.449045',
 'Midline amplitude': ' 0.000000',
 'Minimum amplitude': ' -0.705475',
 'Minimum delta': ' 0.000000',
 'RMS amplitude': ' 0.498788',
 'RMS delta': ' 0.704606',
 'Rough frequency': ' 39659',
 'Samples read': ' 5292001',
 'Scaled by': ' 2147483647.0',
 'Try': " -t raw -e mu-law -b 8 '",
 'Volume adjustment': ' 1.417'}

Similarly, with the first sample string:

>>> s = """\nInput File     : 'E:\\path\\to\\file\\filename.wav'\nChannels       : 1\nSample Rate    : 176400\nPrecision      : 16-bit\nDuration       : 00:00:30.00 = 5292001 samples ~ 2250 CDDA sectors\nFile Size      : 10.6M\nBit Rate       : 2.82M\nSample Encoding: 16-bit Signed Integer PCM\n"""  
>>> dict(r.strip().split(':', 1) for r in s.strip().split('\n') if r) 
{'Bit Rate       ': ' 2.82M',
 'Channels       ': ' 1',
 'Duration       ': ' 00:00:30.00 = 5292001 samples ~ 2250 CDDA sectors',
 'File Size      ': ' 10.6M',
 'Input File     ': " 'E:\\path\\to\\file\\filename.wav'",
 'Precision      ': ' 16-bit',
 'Sample Encoding': ' 16-bit Signed Integer PCM',
 'Sample Rate    ': ' 176400'}