Last active
December 9, 2022 07:36
-
-
Save TeaPoly/59263912750bd17190221f4dbb7f96dd to your computer and use it in GitHub Desktop.
Fast way to get duration for WAVE PCM format audio.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Copyright 2022 Lucky Wong | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License | |
import wave | |
import contextlib | |
import struct | |
def compute_duration(path): | |
""" | |
Get duration in seconds. | |
:param path: a wave file | |
:return: duration in seconds | |
""" | |
try: | |
return compute_duration_from_header(path) | |
except: | |
return compute_duration_by_wave(path) | |
def compute_duration_from_header(path): | |
""" | |
Get duration from wave header | |
:param path: a wave file | |
:return: duration in seconds | |
""" | |
def _read_wave_raw(filename): | |
""" | |
Modified from https://gist.github.com/chief7/54873e6e7009a087180902cb1f4e27be | |
Just pass in a filename and get bytes representation of | |
audio data as result | |
:param filename: a wave file | |
:param rate: | |
:return: tuple -> data, #channels, samplerate, datatype (in bits) | |
""" | |
with open(filename, "rb") as wav: | |
# RIFF-Header: "RIFF<size as uint32_t>WAVE" (12 bytes) | |
riff_header = wav.read(12) | |
riff, filesize, wave = struct.unpack("<4sI4s", riff_header) | |
assert riff.decode("utf-8") == "RIFF" | |
assert wave.decode("utf-8") == "WAVE" | |
""" | |
Format header: | |
'fmt ' - 4 bytes | |
header length - 4 bytes | |
format tag - 2 bytes (only PCM supported here) | |
channels - 2 bytes | |
sample rate - 4 bytes | |
bytes per second - 4 bytes | |
block align - 2 bytes | |
bits per sample - 2 bytes | |
""" | |
fmt_header = wav.read(24) | |
fmt_header_data = struct.unpack("<4sIHHIIHH", fmt_header) | |
_, header_len, fmt_tag, nchannels, samplerate, _, _, dtype = fmt_header_data | |
assert fmt_tag == 1 # only PCM supported | |
""" | |
Data part | |
'data' - 4 bytes, header | |
len of data - 4 bytes | |
""" | |
data_header = wav.read(8) | |
head, data_len = struct.unpack("<4sI", data_header) | |
assert head.decode("utf-8") == "data" | |
nframes = data_len/(dtype/8) | |
return nchannels, samplerate, nframes | |
nchannels, samplerate, nframes = _read_wave_raw(path) | |
assert nchannels == 1 | |
return int(nframes/samplerate) | |
def compute_duration_by_wave(path): | |
""" | |
Get duration using wave library | |
:param path: a wave file | |
:return: duration in seconds | |
""" | |
with contextlib.closing(wave.open(path, 'r')) as f: | |
channels = f.getnchannels() | |
assert channels == 1 | |
nframes = f.getnframes() | |
framerate = f.getframerate() | |
return int(nframes/framerate) | |
if __name__ == "__main__": | |
import time | |
path = "/path/to/test.wav" | |
# compute_duration_by_wave: 0.034050941467285156 | |
# compute_duration_from_header: 0.0006167888641357422 | |
t = time.time() | |
d = compute_duration_by_wave(path) | |
print(d) | |
print(time.time()-t) | |
t = time.time() | |
d = compute_duration_from_header(path) | |
print(d) | |
print(time.time()-t) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment