Source code for audiodata.aio.aiffio
"""
:filename: sppas.src.audiodata.aiffio.py
:author: Nicolas Chazeau, Brigitte Bigi
:contact: develop@sppas.org
:summary: Apple AIFF support.
.. _This file is part of SPPAS: http://www.sppas.org/
..
---------------------------------------------------------------------
___ __ __ __ ___
/ | \ | \ | \ / the automatic
\__ |__/ |__/ |___| \__ annotation and
\ | | | | \ analysis
___/ | | | | ___/ of speech
Copyright (C) 2011-2021 Brigitte Bigi
Laboratoire Parole et Langage, Aix-en-Provence, France
Use of this software is governed by the GNU Public License, version 3.
SPPAS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
SPPAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SPPAS. If not, see <http://www.gnu.org/licenses/>.
This banner notice must not be removed.
---------------------------------------------------------------------
Audio Interchange File Format (AIFF) is an audio file format developed by
Apple Inc. in 1988.
"""
import struct
import aifc
from sppas.src.config import u
from ..audio import sppasAudioPCM
from ..audiodataexc import AudioDataError
# ---------------------------------------------------------------------------
[docs]class AiffIO(sppasAudioPCM):
"""An AIFF/AIFC file open/save utility class.
"""
[docs] def __init__(self):
"""Constructor."""
super(AiffIO, self).__init__()
# ------------------------------------------------------------------------
[docs] def open(self, filename):
"""Get an audio from an Audio Interchange File Format file.
:param filename: (str) input file name.
"""
# Passing a unicode filename to aifc.open() results in the argument
# being treated like a filepointer instead of opening the file.
fp = open(u(filename), "r")
# Use the standard aifc library to load the audio file
# open method return an Aifc_read object
self._audio_fp = aifc.open(fp)
# ----------------------------------------------------------------------
# Read content, for audiofp
# ----------------------------------------------------------------------
[docs] def read_frames(self, nframes):
"""Specific frame reader for aiff files.
AIFF data is in big endian and we need little endian.
:param nframes: (int) the the number of frames wanted
:returns: (str) frames
"""
if not self._audio_fp:
raise AudioDataError
data = self._audio_fp.readframes(nframes)
if self.get_sampwidth() == 4:
data = struct.unpack(">%ul" % (len(data) / 4), data)
return struct.pack("<%ul" % (len(data)), *data)
elif self.get_sampwidth() == 2:
data = struct.unpack(">%uh" % (len(data)/2), data)
return struct.pack("<%uh" % (len(data)), *data)
return data
# ----------------------------------------------------------------------
@staticmethod
def _write_frames(fp, data):
"""Specific writer for aiff files.
Data is in little endian and aiff files need big endian.
:param fp: (sppasAudioPCM) the audio file pointer to write in
:param data: (str) frames to write
"""
if fp.getsampwidth() == 4:
data = struct.unpack("<%ul" % (len(data)/4), data)
fp.writeframes(struct.pack(">%ul" % (len(data)), *data))
elif fp.getsampwidth() == 2:
data = struct.unpack("<%uh" % (len(data)/2), data)
fp.writeframes(struct.pack(">%uh" % (len(data)), *data))
else:
fp.writeframes(data)
# ----------------------------------------------------------------------
[docs] def save(self, filename):
"""Write an audio content as a Audio Interchange File Format file.
:param filename: (str) output filename.
"""
if self._audio_fp is not None:
self.rewind()
frames = self._audio_fp.readframes(self._audio_fp.getnframes())
self.save_fragment(filename, frames)
elif len(self) == 1:
channel = self._channels[0]
fp = open(filename, 'w')
f = aifc.open(fp)
f.setnchannels(1)
f.setsampwidth(channel.get_sampwidth())
f.setframerate(channel.get_framerate())
f.setnframes(channel.get_nframes())
try:
AiffIO._write_frames(f, channel.get_frames())
finally:
f.close()
else:
self.verify_channels()
frames = b""
sp = self._channels[0].get_sampwidth()
for i in range(0, self._channels[0].get_nframes()*sp, sp):
for j in range(len(self._channels)):
fc = self._channels[j].get_frames()
frames += fc[i:i+sp]
f = aifc.open(filename, 'w')
f.setnchannels(len(self._channels))
f.setsampwidth(sp)
f.setframerate(self._channels[0].get_framerate())
f.setnframes(self._channels[0].get_nframes())
try:
AiffIO._write_frames(f, frames)
finally:
f.close()
# -----------------------------------------------------------------------
[docs] def save_fragment(self, filename, frames):
"""Write an audio content as a Audio Interchange File Format file.
:param filename (str) output filename.
:param frames (str) the frames to write
"""
fp = open(u(filename), "w")
f = aifc.Aifc_write(fp)
f.setnchannels(self.get_nchannels())
f.setsampwidth(self.get_sampwidth())
f.setframerate(self.get_framerate())
f.setnframes(len(frames)/self.get_nchannels()/self.get_sampwidth())
try:
AiffIO._write_frames(f, frames)
except Exception:
raise
finally:
f.close()