Difficult-Rocket/libs/pyglet/media/codecs/wave.py

155 lines
5.2 KiB
Python
Raw Normal View History

2021-04-16 23:21:06 +08:00
# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2008 Alex Holkner
2022-04-30 13:56:57 +08:00
# Copyright (c) 2008-2022 pyglet contributors
2021-04-16 23:21:06 +08:00
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of pyglet nor the names of its
# contributors may be used to endorse or promote products
# derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------
"""Decoder for RIFF Wave files, using the standard library wave module.
"""
import wave
2022-04-08 23:07:41 +08:00
from pyglet.util import DecodeException
2021-04-16 23:21:06 +08:00
from .base import StreamingSource, AudioData, AudioFormat, StaticSource
from . import MediaEncoder, MediaDecoder
2022-04-08 23:07:41 +08:00
class WAVEDecodeException(DecodeException):
2021-04-16 23:21:06 +08:00
pass
class WaveSource(StreamingSource):
def __init__(self, filename, file=None):
if file is None:
file = open(filename, 'rb')
2022-04-08 23:07:41 +08:00
self._file = file
2021-04-16 23:21:06 +08:00
try:
self._wave = wave.open(file)
except wave.Error as e:
raise WAVEDecodeException(e)
nchannels, sampwidth, framerate, nframes, comptype, compname = self._wave.getparams()
self.audio_format = AudioFormat(channels=nchannels, sample_size=sampwidth * 8, sample_rate=framerate)
self._bytes_per_frame = nchannels * sampwidth
self._duration = nframes / framerate
self._duration_per_frame = self._duration / nframes
self._num_frames = nframes
self._wave.rewind()
def __del__(self):
if hasattr(self, '_file'):
self._file.close()
def get_audio_data(self, num_bytes, compensation_time=0.0):
num_frames = max(1, num_bytes // self._bytes_per_frame)
data = self._wave.readframes(num_frames)
if not data:
return None
timestamp = self._wave.tell() / self.audio_format.sample_rate
duration = num_frames / self.audio_format.sample_rate
return AudioData(data, len(data), timestamp, duration, [])
def seek(self, timestamp):
timestamp = max(0.0, min(timestamp, self._duration))
position = int(timestamp / self._duration_per_frame)
self._wave.setpos(position)
#########################################
# Decoder class:
#########################################
class WaveDecoder(MediaDecoder):
def get_file_extensions(self):
return '.wav', '.wave', '.riff'
2022-04-08 23:07:41 +08:00
def decode(self, filename, file, streaming=True):
2021-04-16 23:21:06 +08:00
if streaming:
return WaveSource(filename, file)
else:
return StaticSource(WaveSource(filename, file))
class WaveEncoder(MediaEncoder):
def get_file_extensions(self):
return '.wav', '.wave', '.riff'
2022-04-08 23:07:41 +08:00
def encode(self, source, filename, file):
2021-04-16 23:21:06 +08:00
"""Save the Source to disk as a standard RIFF Wave.
A standard RIFF wave header will be added to the raw PCM
audio data when it is saved to disk.
:Parameters:
`filename` : str
The file name to save as.
2022-04-13 19:00:36 +08:00
`file` : file-like object
A file-like object, opened with mode 'wb'.
2021-04-16 23:21:06 +08:00
"""
2022-04-08 23:07:41 +08:00
opened_file = None
if file is None:
file = open(filename, 'wb')
opened_file = True
2021-04-16 23:21:06 +08:00
source.seek(0)
wave_writer = wave.open(file, mode='wb')
wave_writer.setnchannels(source.audio_format.channels)
wave_writer.setsampwidth(source.audio_format.bytes_per_sample)
2021-04-16 23:21:06 +08:00
wave_writer.setframerate(source.audio_format.sample_rate)
# Save the data in 1-second chunks:
chunksize = source.audio_format.bytes_per_second
audiodata = source.get_audio_data(chunksize)
while audiodata:
wave_writer.writeframes(audiodata.data)
audiodata = source.get_audio_data(chunksize)
2022-04-08 23:07:41 +08:00
else:
2022-04-13 19:00:36 +08:00
wave_writer.close()
2022-04-08 23:07:41 +08:00
if opened_file:
file.close()
2021-04-16 23:21:06 +08:00
def get_decoders():
return [WaveDecoder()]
def get_encoders():
return [WaveEncoder()]