Ei vielä käännetty
Tätä sivua ei ole vielä käännetty suomeksi, joten se näytetään englanniksi. Auta kääntämään
sndhdr Module Complexity¶
The sndhdr module guesses a sound file's format from its header. what() opens a path, reads
its first 512 bytes, and hands them with the open file to each function in sndhdr.tests in turn
until one recognises the format. Most formats are decided from those bytes alone; a WAV or AIFF
file is handed on to wave or aifc, which read its chunk headers but not its samples.
Removed in Python 3.13
Deprecated in Python 3.11 and removed in Python 3.13 by PEP 594. The examples that import it need Python 3.11 or 3.12.
t is the functions in sndhdr.tests, eight built in, and c is the chunk headers read from a
WAV or AIFF file: those up to the data chunk in a WAV file, and every chunk in an AIFF or AIFC
file. The other built-in functions look only at those header bytes, so each is O(1); a function
you append costs whatever it does. Opening the file and one read or seek on it are priced at
O(1), and the bounds leave out the marker list aifc keeps from an AIFF MARK chunk.
Complexity Reference¶
Functions and data¶
| Operation | Time | Space | Notes |
|---|---|---|---|
sndhdr.what(filename) |
O(t + c) | O(1) | Returns the first SndHeaders a function in tests produces, or None. Takes a path, not an open file; raises OSError if it cannot be opened. Beyond the first 512 bytes, only a WAV or AIFF file is read further, and not its samples, so a recording's length does not enter the cost. A WAV file whose first chunk is not fmt, or a format wave or aifc does not read, such as a floating-point WAV, is None |
sndhdr.whathdr(filename) |
O(t + c) | O(1) | What what() calls; the same result |
sndhdr.tests |
O(1) | O(1) | The functions tried in order; append a function test(h, f) that takes up to 512 header bytes and the open file and returns a five-item tuple or None |
SndHeaders¶
| Operation | Time | Space | Notes |
|---|---|---|---|
sndhdr.SndHeaders |
O(1) | O(1) | The named tuple what() returns |
SndHeaders.filetype, SndHeaders.framerate, SndHeaders.nchannels, SndHeaders.nframes, SndHeaders.sampwidth |
O(1) | O(1) | The tuple's fields, in this order |
Identifying a Sound File¶
A format such as AU states its rate, channels and data size in a fixed header, so what() answers
from the first 512 bytes however long the file is.
import os
import struct
import tempfile
import warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning) # 3.11 and 3.12 warn on import
import sndhdr
# AU: magic, header size, data size, encoding 3 (16-bit linear), rate, channels
header = struct.pack('>4s5I', b'.snd', 24, 16000, 3, 8000, 1)
with tempfile.TemporaryDirectory() as directory:
path = os.path.join(directory, 'speech.au')
with open(path, 'wb') as f:
f.write(header + bytes(16000))
info = sndhdr.what(path) # O(t) - one 512-byte read
assert info.filetype == 'au'
assert (info.framerate, info.nchannels, info.sampwidth) == (8000, 1, 16)
assert info.nframes == 8000 # 16,000 data bytes / 2 bytes per frame
assert sndhdr.whathdr(path) == info
# Text no function recognises is None, not an error
text = os.path.join(directory, 'notes.txt')
with open(text, 'wb') as f:
f.write(b'not a sound file')
assert sndhdr.what(text) is None
try:
sndhdr.what(os.path.join(directory, 'missing.au'))
except OSError:
pass
else:
raise AssertionError('a missing file was not reported')
WAV and AIFF Files¶
These two are handed to wave and aifc, which read chunk headers to find the format chunk and
the frame count. wave stops at the data chunk and aifc seeks past the samples to the end, so
the cost follows the number of chunks, not the length of the audio.
import os
import tempfile
import warnings
import wave
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
import sndhdr
with tempfile.TemporaryDirectory() as directory:
path = os.path.join(directory, 'tone.wav')
with wave.open(path, 'wb') as out:
out.setnchannels(2)
out.setsampwidth(2)
out.setframerate(44100)
out.writeframes(bytes(44100 * 4)) # one second of stereo silence
info = sndhdr.what(path) # O(t + c) - the recording's length does not matter
assert info == ('wav', 44100, 2, 44100, 16)
Adding a Format¶
Appending to sndhdr.tests adds one more call to every file no earlier function recognises. A
function sees up to 512 header bytes and the open file.
import os
import tempfile
import warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
import sndhdr
def test_flac(h, f):
if h[:4] == b'fLaC':
return ('flac', 0, 0, -1, 0) # filetype, framerate, nchannels, nframes, sampwidth
sndhdr.tests.append(test_flac)
try:
with tempfile.TemporaryDirectory() as directory:
path = os.path.join(directory, 'song.flac')
with open(path, 'wb') as f:
f.write(b'fLaC' + bytes(100))
assert sndhdr.what(path).filetype == 'flac' # O(t), now one longer
finally:
sndhdr.tests.remove(test_flac)
Without sndhdr¶
From Python 3.13, wave still reads WAV files. Telling the other formats apart means comparing
their first bytes yourself, which is still a fixed-size read.
import io
import wave
def sound_type(header):
"""The format the first 12 bytes name, or None."""
if header[:4] == b'RIFF' and header[8:12] == b'WAVE':
return 'wav'
if header[:4] == b'FORM' and header[8:12] in (b'AIFF', b'AIFC'):
return header[8:12].decode('ascii').lower()
if header[:4] == b'.snd':
return 'au'
return None
buffer = io.BytesIO()
with wave.open(buffer, 'wb') as out:
out.setnchannels(1)
out.setsampwidth(2)
out.setframerate(8000)
out.writeframes(bytes(16000))
buffer.seek(0)
assert sound_type(buffer.read(12)) == 'wav' # O(1)
buffer.seek(0)
with wave.open(buffer, 'rb') as wav: # O(c) - the samples are not read
params = wav.getparams()
assert (params.framerate, params.nchannels, params.nframes) == (8000, 1, 8000)
Performance Best Practices¶
✅ Do:
- Call
what()on long recordings without worry: it reads headers, not the recording - Treat
Noneas "not a format this module can read", not "not a sound file": a floating-point WAV isNone, and so is a WAV whose first chunk is notfmt, such as one led by aJUNKchunk - Catch exceptions on files you did not write: a truncated header, such as an AU file shorter than
its 24-byte header, can raise
IndexErrorinstead of returningNone - Treat
'sndr'as a guess: two zero bytes and a plausible rate are all it checks
❌ Avoid:
- Passing an open file:
what()takes a path and raisesTypeErrorfor a file object - Appending a slow function to
sndhdr.tests: every file no earlier function recognises pays for it
Version Notes¶
- Python 3.11 and 3.12: Importing the module emits a
DeprecationWarning - Python 3.12: A PCM WAV file in the
WAVE_FORMAT_EXTENSIBLElayout is recognised as'wav'; earlier versions returnNonefor it - Python 3.13+: Removed by PEP 594, with
aifcandsunau;import sndhdrraisesModuleNotFoundError
Related Modules¶
- wave - reads WAV headers and frames, and remains after 3.13;
what()uses it - aifc - what
what()uses for AIFF and AIFC files, removed in 3.13 too - sunau - reads the AU files
what()recognises from their header, removed in 3.13 too - imghdr - the same header-sniffing design for images, removed in 3.13 too