Onyx logo

Previous topic

onyx.audio.unshorten – Code for parsing the bitstream of a shorten encoded file.

Next topic

Containers

This Page

onyx.audio.audiodata – Interfaces for getting audio data from files

exception onyx.audio.audiodata.AudioException

Bases: onyx.OnyxException

Baseclass for exceptions occuring while processing audio from external sources.

>>> raise AudioException("something is wrong with the audio data")
Traceback (most recent call last):
  ...
AudioException: something is wrong with the audio data
args
message
exception onyx.audio.audiodata.AudioTypeError

Bases: onyx.audio.audiodata.AudioException, onyx.DataTypeError

Exception raised when the type of the audio cannot be determined.

>>> raise AudioTypeError("unknown audio type in file 'foobar.ima'")
Traceback (most recent call last):
  ...
AudioTypeError: unknown audio type in file 'foobar.ima'
args
message
onyx.audio.audiodata.bitjoin(values, bits)

Inverse of bitsplit, packs values according to bits and return the bytes.

>>> a = '\xf1\xfa\x90d'
>>> a1 = bitsplit(a, MP3_HEADER_BITS)
>>> a1
[1935, 3, 1, 0, 9, 0, 0, 0, 1, 2, 0, 1, 0]
>>> a2 = bitjoin(a1, MP3_HEADER_BITS)
>>> a == a2
True
>>> b = '\xff\xfa\x90d'
>>> b == bitjoin(bitsplit(b, MP3_HEADER_BITS), MP3_HEADER_BITS)
True
onyx.audio.audiodata.bitsplit(bytes, bits)

Split a sequence of bytes into a sequence of non-negative integers. The bits argument is a sequence of non-negative integers specifying the bit masks into which the bytes are split. Conceptually, the bytes are arranged left to right, and then items composed from each successive set of the bytes’ bits from left to right are placed into the result, where the number of bits in each set is given by the corresponding value in the bits argument.

For instance, an MPEG version 1, Layer III audio frame has a four byte header. The header has 13 fields of bits of lengths 11, 2, 2, 1, 4, 2, 1, 1, 2, 2, 1, 1, 2. To get the numerical values of these 13 fields, you would do the following:

>>> mp3_header_bits = 11, 2, 2, 1, 4, 2, 1, 1, 2, 2, 1, 1, 2
>>> assert sum(mp3_header_bits) == 32
>>> mp3_header_bytes = sum(mp3_header_bits) // 8
>>> assert mp3_header_bytes == 4
>>> header = '\xff\xfb\x90d'  # a valid header, e.g. via open('audio.mp3', 'rb').read(4)
>>> assert len(header) == mp3_header_bytes
>>> fields = bitsplit(header, mp3_header_bits)
>>> fields
[2047, 3, 1, 1, 9, 0, 0, 0, 1, 2, 0, 1, 0]

Now lets do some checks; in production code, failure of the checks in the asserts would mean it isn’t a valid mpeg audio header. See http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm

>>> sync, mpeg_id, layer, protection, bit_rate_index, sample_rate_index, has_padding, private, channel_mode, mode_extension, is_copyright, is_original, emphasis_index = fields
>>> assert sync == 2047
>>> assert mpeg_id != 1
>>> 'mpeg_id', ('2.5', None, '2', '1')[mpeg_id]
('mpeg_id', '1')
>>> assert layer != 0
>>> 'layer', (None, 'III', 'II', 'I')[layer]
('layer', 'III')
>>> 'protection', (True, False)[protection]
('protection', False)

The following tables are only valid for MPEG version 1, Layer III

>>> assert mpeg_id == 3 and layer == 1 # i.e. Version 1, Layer III
>>> V1LIIIbitrate = None, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, None
>>> V1LIIIsample_rate = 44100, 48000, 32000, None
>>> assert bit_rate_index != 0x00 and bit_rate_index != 0xff
>>> 'bit_rate', V1LIIIbitrate[bit_rate_index]
('bit_rate', 128)
>>> assert sample_rate_index != 3
>>> 'sample_rate', V1LIIIsample_rate[sample_rate_index]
('sample_rate', 44100)
>>> 'padding', bool(has_padding)
('padding', False)
>>> 'frame_bytes', 144 * V1LIIIbitrate[bit_rate_index] * 1000 // V1LIIIsample_rate[sample_rate_index] + has_padding 
('frame_bytes', 417)
>>> 'private', private
('private', 0)
>>> 'channel_mode', ('Stereo', 'JointStereo', 'DualChannel', 'SingleChannel')[channel_mode]
('channel_mode', 'JointStereo')
>>> 'mode_extension', hex(mode_extension)
('mode_extension', '0x2')
>>> 'copyright', bool(is_copyright)
('copyright', False)
>>> 'original', bool(is_original)
('original', True)
>>> assert emphasis_index != 2
>>> 'emphasis', ('none', '50/15 msec', None, 'CCIT J.17')[emphasis_index]
('emphasis', 'none')
onyx.audio.audiodata.c_char_pp

alias of LP_c_char_p

onyx.audio.audiodata.c_int_p

alias of LP_c_int

onyx.audio.audiodata.c_int_pp

alias of LP_LP_c_int

onyx.audio.audiodata.c_void_pp

alias of LP_c_void_p

onyx.audio.audiodata.char_pp()
onyx.audio.audiodata.check_positive_ints(mapping, name, exclusions=())
onyx.audio.audiodata.ctypes_pp_wrapper(free_func, *pp_args)

Returns a decorator to be used on a function that calls a C function in an Onyx-style shared library via Python’s ctypes module. The decorator itself returns a Context Manager that manages the output arguments to and from the C function.

Such C functions must adhere to the signature and allocation idioms of an Onyx-style shared library. These functions take input parameters that are strings, integers, pointers to arrays, etc, the C types of which the user has set up using the C function’s argtypes attribute in the ctypes module.

In the C function’s signature the input parameters are followed by output parameters. The output arguments are always pointers to pointers. The returned Context Manager takes care of setting up the pointers to pointer for the C function. The C function itself does the pointed-to pointer allocation. The contents of the pointer(s) to pointer are made available as the target of the with statement. When the with statement suite exits, the Context Manager takes care of calling a function in the shared library to free the memory that was allocated by the C function.

The ctypes_pp_wrapper function takes one or more arguments. The required first argument is a memory-freeing function in the shared library. This function is will be used by the Context Manager to free memory that is allocated for the return arguments of the wrapped C function. Each of the remaining arguments is a ctypes pointer-to-pointer type for the output type of the C function.

When the decorated user function is called, the outer pointers will be instantiated, and the user’s function will be called with an argument list that is the catenation of arguments supplied by the user and the pointer instances. The C function will use the input values, and it will allocate the inner pointers of the output arguments, and it will fill in the output data. If no errors occur it will return a NULL string pointer.

In this case, the target of the with statement will be assigned the sequence of the contents of the ctypes pointer-to-pointer output arguments. These ctypes are used in the body of the with statement. Immutable output types are available as the value attribute of the corresponding target element; these values will persist after the with statement is exited. The values in (mutable) output arrays must be used, e.g. copied, because the underlying data will be deallocated when the with statement is exited.

The Onyx-style shared-library idiom is that if an error occured in the C function, the returned pointer will be a non-NULL string pointer. The error string will be such that the first whitespace-separated token is the name of a Python exception, and the remainder of the string is an error message. If the return value is non-NULL, the decorator will parse the return value, and it will construct and raise the appropriate Python error. It will also free any memory that the C function allocated in the output pointer to points.

Some usage examples should clarify the robust simplicity of using the generator returned by this function, as well as the idioms in the C shared library.

onyx.audio.audiodata.get_api_info()

Get information about the audiodata interface and the underlying libsndfile library.

Returns a triple, (api_name, version_tuple, libs-info), where api_name is a string giving the name of this api, version_tuple is a pair of integers, (major, minor), giving the version number of this api, and libs-info is a tuple of descriptive strings about the underlying C libraries that are being used.

XXX will eventually fail on some upgrade

>>> get_api_info() 
('audiodata', (0, 2), ('libsndfile-1.0...', 'mpg123-...'))
onyx.audio.audiodata.get_audio_str(str_data, name, wave_format=None)
onyx.audio.audiodata.get_file_audio(filename, wave_format)

Get audio data from filename. Reads all the audio data from filename and converts it to the requested wave_format which must be one of ‘int16’, ‘int32’, ‘float32’, or ‘float64’.

Returns a triple, (file_info, audio_info, wave_data): where file_info is an attrdict with information about the data as it appears in filename; audio_info is an attrdict with information about the data as it appears in the wave_data; and wave_data is a two-dimensional numpy array containing the one or more channels of wave samples from the file. If wave_format is different from the native format of the data in filename the wave samples may get normalized as per libsndfile semantics.

The file_info will be an attrdict, equal to that which would be returned by get_file_info(filename):

Attribute Name Type Description Example Value
file_item_bytes int size of each item in bytes 2
file_item_coding str coding of each item in file ‘int16’
file_name str name given to access file ‘zero.wav’
file_name_full str full name of file ‘/Users/johndoe/azure/py/onyx/audio/zero.wav’
file_num_bytes int size of file in bytes 512314
file_num_channels int number of audio channels 2
file_num_items int number of items in file 256128
file_num_samples int number of samples in file 128064
file_sample_rate int samples per second 44100
file_sndfile_extension str nominal extension ‘wav’
file_sndfile_format str libsndfile’s format ‘0x10002’
file_sndfile_type str libsndfile’s type info ‘Signed 16 bit PCM WAV (Microsoft)’

The audio_info will be an attrdict with the following fields:

Attribute Name Type Description Example Value
audio_item_bytes int size of each wave item in bytes 4
audio_item_coding str coding of each wave item ‘int32’
audio_num_bytes int size of wave data in bytes 1024512
audio_num_channels int number of audio channels 2
audio_num_items int number of items in wave 256128
audio_num_samples int number of samples in wave 128064
audio_sample_rate int samples per second 44100

The wave_data will be a two-dimensional numpy array, with shape (audio_num_channels, audio_num_samples) and dtype corresponding to wave_format. Note that audio_sample_rate is the only attribute of audio_info that cannot be determined from the wave_data itself.

>>> zero_wav = os.path.join(module_dir, 'zero.wav')
>>> zero_sph = os.path.join(module_dir, 'zero.sph')

Get the audio from a file

>>> file_info1, audio_info1, wave_data1 = get_file_audio(zero_wav, 'int32')

Look at the file info

>>> for key in sorted(file_info1): print "%-24s  %r" % (key, file_info1[key]) 
file_item_bytes           2
file_item_coding          'int16'
file_name                 '...zero.wav'
file_name_full            '/.../py/onyx/audio/zero.wav'
file_num_bytes            512314
file_num_channels         2
file_num_items            256128
file_num_samples          128064
file_sample_rate          44100
file_sndfile_extension    'wav'
file_sndfile_format       '0x10002'
file_sndfile_type         'Signed 16 bit PCM WAV (Microsoft)'

It’s the same as what get_file_info() returns

>>> file_info1 == get_file_info(zero_wav)
True

Look at the audio info

>>> for key in sorted(audio_info1): print "%-24s  %r" % (key, audio_info1[key])
audio_item_bytes          4
audio_item_coding         'int32'
audio_num_bytes           1024512
audio_num_channels        2
audio_num_items           256128
audio_num_samples         128064
audio_sample_rate         44100

Show the sets of keys in file_info and audio_info that differ only in their suffix and have the same values.

>>> all(file_info1['file_' + suffix] == audio_info1['audio_' + suffix] for suffix in ('num_channels', 'num_items', 'num_samples', 'sample_rate'))
True

Look at the int32 data

>>> wave_data1.shape
(2, 128064)
>>> wave_data1.itemsize
4
>>> wave_data1.min(), wave_data1.max()
(-645136384, 673447936)
>>> print str(wave_data1)
[[       0   -65536  -196608 ..., 13303808 13828096 13041664]
 [       0        0  -196608 ..., 14745600 14745600 14483456]]

Look at int16 version of the data

>>> _, _, wave_data1a = get_file_audio(zero_wav, 'int16')
>>> wave_data1a.shape
(2, 128064)
>>> wave_data1a.itemsize
2
>>> wave_data1a.min(), wave_data1a.max()
(-9844, 10276)
>>> wave_data1a
array([[  0,  -1,  -3, ..., 203, 211, 199],
       [  0,   0,  -3, ..., 225, 225, 221]], dtype=int16)

Look at another file that has a different internal format, but the same audio_info and wave_data

>>> file_info2, audio_info2, wave_data2 = get_file_audio(zero_sph, 'int32')
>>> file_info2 == file_info1
False
>>> audio_info2 == audio_info1
True
>>> (wave_data2 == wave_data1).all()
True

Show how wave_format ‘int16’ and ‘int32’ return differently scaled data

>>> file_info3, audio_info3, wave_data3 = get_file_audio(zero_sph, 'int16')
>>> wave_data3.sum() << 16 == wave_data2.sum()
True
>>> (np.array(wave_data3, dtype=np.int32) << 16 == wave_data2).all()
True

Show that wave_format ‘float32’ and ‘float64’ return equal data

>>> file_info4, audio_info4, wave_data4 = get_file_audio(zero_wav, 'float32')
>>> file_info5, audio_info5, wave_data5 = get_file_audio(zero_sph, 'float64')
>>> (wave_data5 == wave_data4).all()
True

Test mp3 reading since we know it’s using a separate library under the covers

>>> seven_mp3 = os.path.join(module_dir, 'seven.mp3')
>>> file_info6, audio_info6, wave_data6 = get_file_audio(seven_mp3, 'int16')
>>> for key in sorted(file_info6): print "%-24s  %r" % (key, file_info6[key]) 
file_item_bytes           -1
file_item_coding          'int16'
file_name                 '...seven.mp3'
file_name_full            '/.../py/onyx/audio/seven.mp3'
file_num_bytes            14192
file_num_channels         2
file_num_items            56542
file_num_samples          28271
file_sample_rate          44100
file_sndfile_extension    'mp3'
file_sndfile_format       '0xd0'
file_sndfile_type         'MPEG_Version 1.0  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 128'
>>> file_info6 == get_file_info(seven_mp3)
True
>>> for key in sorted(audio_info6): print "%-24s  %r" % (key, audio_info6[key])
audio_item_bytes          2
audio_item_coding         'int16'
audio_num_bytes           113084
audio_num_channels        2
audio_num_items           56542
audio_num_samples         28271
audio_sample_rate         44100
>>> wave_data6.shape
(2, 28271)
>>> wave_data6.itemsize
2
>>> wave_data6.min(), wave_data6.max() 
(-306..., 250...)
>>> wave_data6
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=int16)

Note that for mp3 we can’t assert that the int32 data is just a 16 bit shift from the int16 data.

>>> file_info7, audio_info7, wave_data7 = get_file_audio(seven_mp3, 'int32')
>>> file_info7 == file_info6
True
>>> for key in sorted(audio_info7): print "%-24s  %r" % (key, audio_info7[key])
audio_item_bytes          4
audio_item_coding         'int32'
audio_num_bytes           226168
audio_num_channels        2
audio_num_items           56542
audio_num_samples         28271
audio_sample_rate         44100
>>> wave_data7.shape
(2, 28271)
>>> wave_data7.itemsize
4
>>> wave_data7.min(), wave_data7.max() 
(-2007331..., 16422...)
>>> print str(wave_data7)  
[[   0    0    0 ...,  48...  574  278]
 [   0    0    0 ..., -48... -574 -278]]
>>> file_info8, audio_info8, wave_data8 = get_file_audio(seven_mp3, 'float32')
>>> file_info8 == file_info6
True
>>> for key in sorted(audio_info8): print "%-24s  %r" % (key, audio_info8[key])
audio_item_bytes          4
audio_item_coding         'float32'
audio_num_bytes           226168
audio_num_channels        2
audio_num_items           56542
audio_num_samples         28271
audio_sample_rate         44100
>>> wave_data8.shape
(2, 28271)
>>> wave_data8.itemsize
4
>>> wave_data8.min(), wave_data8.max() 
(-0.93473..., 0.7647...)
>>> wave_data8 
array([[  0.00000000e+00,   0.00000000e+00,   0.00000000e+00, ...,
          2.25688...e-07,   2.6738...e-07,   1.29675...e-07],
       [  0.00000000e+00,   0.00000000e+00,   0.00000000e+00, ...,
         -2.25688...e-07,  -2.6738...e-07,  -1.29675...e-07]], dtype=float32)

Test a bunch of files, many with different mp3 configurations

>>> mp3files = 'zero.sph zero.wav zero__.mp3 zero__r08000_c1.mp3 zero__r08000_c2.mp3 zero__r11025_c1.mp3 zero__r11025_c2.mp3 zero__r16000_c1.mp3 zero__r16000_c2.mp3 zero__r22000_c1.mp3 zero__r22000_c2.mp3'.split()
>>> for mp3file in mp3files:
...  file_info9, audio_info9, wave_data9 = get_file_audio(os.path.join(module_dir, mp3file), 'int16')
...  file_info9.file_sndfile_type
'pcm SPH (NIST Sphere)'
'Signed 16 bit PCM WAV (Microsoft)'
'MPEG_Version 1.0  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 128'
'MPEG_Version 2.5  Audio_Layer 3  Channel_Mode mono  BitrateKbps 8'
'MPEG_Version 2.5  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 24'
'MPEG_Version 2.5  Audio_Layer 3  Channel_Mode mono  BitrateKbps 16'
'MPEG_Version 2.5  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 32'
'MPEG_Version 2.0  Audio_Layer 3  Channel_Mode mono  BitrateKbps 24'
'MPEG_Version 2.0  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 48'
'MPEG_Version 2.0  Audio_Layer 3  Channel_Mode mono  BitrateKbps 32'
'MPEG_Version 2.0  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 64'

Sometimes you lose

>>> file_info9, audio_info9, wave_data9 = get_file_audio(seven_mp3, 'float64') 
Traceback (most recent call last):
  ...
ValueError: .../cpp/liveaudio/mpg123wrap.c:... get_audio(): unsupported format 'float64' for file '...seven.mp3'

Again, we skip fiddling around with the problem file because different versions of mpg123 handle it differently.

>>> file_info10, audio_info10, wave_data10 = get_file_audio(os.path.join(module_dir, 'problem.mp3'), 'int16') 
>>> for key in sorted(file_info10): print "%-24s  %r" % (key, file_info10[key]) 
file_item_bytes           -1
file_item_coding          'int16'
file_name                 'problem.mp3'
file_name_full            '/.../py/onyx/audio/problem.mp3'
file_num_bytes            512318
file_num_channels         2
file_num_items            1536
file_num_samples          768
file_sample_rate          44100
file_sndfile_extension    'mp3'
file_sndfile_format       '0xd0'
file_sndfile_type         'MPEG_Version 1.0  Audio_Layer 1  Channel_Mode stereo  BitrateKbps 0'
>>> for key in sorted(audio_info10): print "%-24s  %r" % (key, audio_info10[key]) 
audio_item_bytes          2
audio_item_coding         'int16'
audio_num_bytes           3072
audio_num_channels        2
audio_num_items           1536
audio_num_samples         768
audio_sample_rate         44100
onyx.audio.audiodata.get_file_info(filename)

Get information about the audio data in filename.

Returns an attrdict with the following attributes:

Attribute Name Type Description Example Value
file_item_bytes int size of each item in bytes 2
file_item_coding str coding of each item in file ‘int16’
file_name str name given to access file ‘zero.wav’
file_name_full str full name of file ‘/Users/johndoe/azure/py/onyx/audio/zero.wav’
file_num_bytes int size of file in bytes 512314
file_num_channels int number of audio channels 2
file_num_items int number of items in file 256128
file_num_samples int number of samples in file 128064
file_sample_rate int samples per second 44100
file_sndfile_extension str nominal extension ‘wav’
file_sndfile_format str libsndfile’s format ‘0x10002’
file_sndfile_type str libsndfile’s type info ‘Signed 16 bit PCM WAV (Microsoft)’
>>> info1 = get_file_info(os.path.join(module_dir, 'zero.wav'))
>>> for key in sorted(info1): print "%-24s  %r" % (key, info1[key]) 
file_item_bytes           2
file_item_coding          'int16'
file_name                 '...zero.wav'
file_name_full            '/.../py/onyx/audio/zero.wav'
file_num_bytes            512314
file_num_channels         2
file_num_items            256128
file_num_samples          128064
file_sample_rate          44100
file_sndfile_extension    'wav'
file_sndfile_format       '0x10002'
file_sndfile_type         'Signed 16 bit PCM WAV (Microsoft)'
>>> info2 = get_file_info(os.path.join(module_dir, 'zero.sph'))
>>> for key in sorted(info2): print "%-24s  %r" % (key, info2[key]) 
file_item_bytes           2
file_item_coding          'int16'
file_name                 '...zero.sph'
file_name_full            '/.../py/onyx/audio/zero.sph'
file_num_bytes            513280
file_num_channels         2
file_num_items            256128
file_num_samples          128064
file_sample_rate          44100
file_sndfile_extension    'sph'
file_sndfile_format       ''
file_sndfile_type         'pcm SPH (NIST Sphere)'

This is an incorrect file in that its header doesn’t match the data. Unfortunately, sph2pipe doesn’t notice, so we impose a blunt check. See below where libsndfile does notice the problem.

>>> info3 = get_file_info(os.path.join(module_dir, 'problem.sph')) 
Traceback (most recent call last):
  ...
AudioTypeError: unhandled sample_n_bytes, 4, in NIST Sphere file '/.../problem.sph'

Test mp3 info since we know it’s using a separate library under the covers

>>> info5 = get_file_info(os.path.join(module_dir, 'seven.mp3'))
>>> for key in sorted(info5): print "%-24s  %r" % (key, info5[key]) 
file_item_bytes           -1
file_item_coding          'int16'
file_name                 '...seven.mp3'
file_name_full            '/.../py/onyx/audio/seven.mp3'
file_num_bytes            14192
file_num_channels         2
file_num_items            56542
file_num_samples          28271
file_sample_rate          44100
file_sndfile_extension    'mp3'
file_sndfile_format       '0xd0'
file_sndfile_type         'MPEG_Version 1.0  Audio_Layer 3  Channel_Mode joint_stereo  BitrateKbps 128'

Grrrr, with mpg123 version 1.7.0, problem.mp3 would silently give a bogus sample rate. As of mpg123 version 1.9.0, mpg123 logs an error message to stderr, but returns a non-zero sample rate and a somewhat bogus info... so we skip both the old test and the new listing of the bogus info.

>>> info6 = get_file_info(os.path.join(module_dir, 'problem.mp3')) 
Traceback (most recent call last):
  ...
AudioTypeError: .../cpp/liveaudio/mpg123wrap.c:... get_audio(): invalid sample rate 0 for file '...problem.mp3'
>>> for key in sorted(info6): print "%-24s  %r" % (key, info6[key]) 
file_item_bytes           -1
file_item_coding          'int16'
file_name                 '...problem.mp3'
file_name_full            '/.../py/onyx/audio/problem.mp3'
file_num_bytes            512318
file_num_channels         2
file_num_items            1536
file_num_samples          768
file_sample_rate          44100
file_sndfile_extension    'mp3'
file_sndfile_format       '0xd0'
file_sndfile_type         'MPEG_Version 1.0  Audio_Layer 1  Channel_Mode stereo  BitrateKbps 0'
onyx.audio.audiodata.get_str_audio(str_data, name, wave_format)

BROKEN – libsndfile 1.0.19 doesn’t handle pipes correctly, mpg123 will be tricky to finesse on a pipe

Get information about the audio data in a string, where the string is the contents of an audio file.

Returns an attrdict of information about the data contained in the string. See get_file_audio() for a detailed description of what is returned.

>>> filename = 'zero.sph'
>>> with open(os.path.join(module_dir, filename), 'rb') as infile:
...   str_data = infile.read()
>>> file_info, audio_info, wave_data = get_str_audio(str_data, filename, 'int16')
Traceback (most recent call last):
  ...
NotImplementedError: string-based audio is broken
>>> for key in sorted(file_info): print "%-24s  %r" % (key, file_info[key]) 
>>> for key in sorted(audio_info): print "%-24s  %r" % (key, audio_info[key]) 
>>> wave_data 
onyx.audio.audiodata.get_str_info(str_data, name)

BROKEN – libsndfile 1.0.19 doesn’t handle pipes correctly, mpg123 will be tricky to finesse on a pipe

Get information about the audio data in a string, where the string is the contents of an audio file.

Returns an attrdict of information about the data contained in the string. See get_file_info() for a detailed description of what is returned.

>>> filename = 'zero.sph'
>>> with open(os.path.join(module_dir, filename), 'rb') as infile:
...   str_data = infile.read()
>>> info = get_str_info(str_data, filename)
Traceback (most recent call last):
  ...
NotImplementedError: string-based audio is broken
>>> for key in sorted(info): print "%-24s  %r" % (key, info[key]) 
file_item_bytes           2
file_item_coding          'int16'
file_name                 'zero.sph'
file_name_full            '<from_string zero.sph>'
file_num_bytes            513280
file_num_channels         2
file_num_items            -514
file_num_samples          -257
file_sample_rate          44100
file_sndfile_extension    'wav'
file_sndfile_format       '0x10070002'
file_sndfile_type         'Signed 16 bit PCM WAV (NIST Sphere)'
onyx.audio.audiodata.int_pp()
onyx.audio.audiodata.make_audio(wave_data, sample_rate)

Given wave_data, a two-dimensional numpy array and sample_rate, a sample-rate in units of samples-per-second, return a pair, (audio_info, wave_data): where wave_data is a copy of the wave_data argument and audio_info is an attrdict describing the wave-data (see the description of the audio_info that is returned by get_file_audio()).

>>> wave = np.arange(200, dtype=np.int16).reshape((1, -1))
>>> audio_info, wave_data = make_audio(wave, sample_rate=11025)
>>> for key in sorted(audio_info): print "%-20s  %r" % (key, audio_info[key])
audio_item_bytes      2
audio_item_coding     'int16'
audio_num_bytes       400
audio_num_channels    1
audio_num_items       200
audio_num_samples     200
audio_sample_rate     11025
>>> (wave_data == wave).all()
True
>>> wave_data is wave
False
>>> audio_info2, _ = make_audio(wave.reshape((2,-1)), sample_rate=8000)
>>> for key in sorted(audio_info2): print "%-20s  %r" % (key, audio_info2[key])
audio_item_bytes      2
audio_item_coding     'int16'
audio_num_bytes       400
audio_num_channels    2
audio_num_items       200
audio_num_samples     100
audio_sample_rate     8000
onyx.audio.audiodata.mpeg_header(header)

Given a 4-byte header, return the sequence of 13 MPEG header fields if the four bytes appears to be a valid MPEG audio frame header, otherwise return False.

See http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm

>>> mp3 = mpeg_header('\xff\xfa\x90d')
>>> mp3
[2047, 3, 1, 0, 9, 0, 0, 0, 1, 2, 0, 1, 0]
>>> mpeg_header('\xf1\xfa\x90d')
False

Check all known invalid field entries: used to include (1, 0) for the now-relaxed Version 2.5 exclusion

>>> bad_slots = tuple((0, x) for x in xrange(2047)) + ((1, 1), (2, 0), (4, 0x0), (4, 0xf), (5, 3), (12, 2))
>>> checks = list()
>>> for index, value in bad_slots:
...   mpnot = list(mp3); mpnot[index] = value
...   checks.append(mpeg_header(bitjoin(mpnot, MP3_HEADER_BITS)))
>>> any(checks)
False
onyx.audio.audiodata.mpg123wrap_init(*args, **kwds)
onyx.audio.audiodata.parse_key_value(info_str)
onyx.audio.audiodata.read_fd_strict(fd, nbytes, name)

Read nbytes from file descriptor. Raises EOFError if nbytes cannot be obtained, using name in the error message.

onyx.audio.audiodata.setup(sndfilewrap)
onyx.audio.audiodata.void_pp()