/* Audio File Library Copyright (C) 1998-2000, 2011-2013, Michael Pruett This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* NeXT.cpp This file contains routines for reading and writing NeXT/Sun .snd/.au sound files. */ #include "config.h" #include "NeXT.h" #include #include #include #include #include "File.h" #include "Setup.h" #include "Track.h" #include "afinternal.h" #include "audiofile.h" #include "byteorder.h" #include "util.h" const int _af_next_compression_types[_AF_NEXT_NUM_COMPTYPES] = { AF_COMPRESSION_G711_ULAW, AF_COMPRESSION_G711_ALAW }; static const _AFfilesetup nextDefaultFileSetup = { _AF_VALID_FILESETUP, /* valid */ AF_FILE_NEXTSND, /* fileFormat */ true, /* trackSet */ true, /* instrumentSet */ true, /* miscellaneousSet */ 1, /* trackCount */ NULL, /* tracks */ 0, /* instrumentCount */ NULL, /* instruments */ 0, /* miscellaneousCount */ NULL /* miscellaneous */ }; enum { _AU_FORMAT_UNSPECIFIED = 0, _AU_FORMAT_MULAW_8 = 1, /* CCITT G.711 mu-law 8-bit */ _AU_FORMAT_LINEAR_8 = 2, _AU_FORMAT_LINEAR_16 = 3, _AU_FORMAT_LINEAR_24 = 4, _AU_FORMAT_LINEAR_32 = 5, _AU_FORMAT_FLOAT = 6, _AU_FORMAT_DOUBLE = 7, _AU_FORMAT_INDIRECT = 8, _AU_FORMAT_NESTED = 9, _AU_FORMAT_DSP_CORE = 10, _AU_FORMAT_DSP_DATA_8 = 11, /* 8-bit fixed point */ _AU_FORMAT_DSP_DATA_16 = 12, /* 16-bit fixed point */ _AU_FORMAT_DSP_DATA_24 = 13, /* 24-bit fixed point */ _AU_FORMAT_DSP_DATA_32 = 14, /* 32-bit fixed point */ _AU_FORMAT_DISPLAY = 16, _AU_FORMAT_MULAW_SQUELCH = 17, /* 8-bit mu-law, squelched */ _AU_FORMAT_EMPHASIZED = 18, _AU_FORMAT_COMPRESSED = 19, _AU_FORMAT_COMPRESSED_EMPHASIZED = 20, _AU_FORMAT_DSP_COMMANDS = 21, _AU_FORMAT_DSP_COMMANDS_SAMPLES = 22, _AU_FORMAT_ADPCM_G721 = 23, /* CCITT G.721 ADPCM 32 kbits/s */ _AU_FORMAT_ADPCM_G722 = 24, /* CCITT G.722 ADPCM */ _AU_FORMAT_ADPCM_G723_3 = 25, /* CCITT G.723 ADPCM 24 kbits/s */ _AU_FORMAT_ADPCM_G723_5 = 26, /* CCITT G.723 ADPCM 40 kbits/s */ _AU_FORMAT_ALAW_8 = 27, /* CCITT G.711 a-law */ _AU_FORMAT_AES = 28, _AU_FORMAT_DELTA_MULAW_8 = 29 }; static const uint32_t _AU_LENGTH_UNSPECIFIED = 0xffffffff; NeXTFile::NeXTFile() { setFormatByteOrder(AF_BYTEORDER_BIGENDIAN); } status NeXTFile::readInit(AFfilesetup setup) { uint32_t id, offset, length, encoding, sampleRate, channelCount; m_fh->seek(0, File::SeekFromBeginning); m_fh->read(&id, 4); assert(!memcmp(&id, ".snd", 4)); readU32(&offset); readU32(&length); readU32(&encoding); readU32(&sampleRate); readU32(&channelCount); if (!channelCount) { _af_error(AF_BAD_CHANNELS, "invalid file with 0 channels"); return AF_FAIL; } Track *track = allocateTrack(); if (!track) return AF_FAIL; track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; track->f.sampleRate = sampleRate; track->f.channelCount = channelCount; track->f.framesPerPacket = 1; /* Override the compression type later if necessary. */ track->f.compressionType = AF_COMPRESSION_NONE; track->fpos_first_frame = offset; off_t lengthAvailable = m_fh->length() - offset; if (length == _AU_LENGTH_UNSPECIFIED || static_cast(length) > lengthAvailable) length = lengthAvailable; track->data_size = length; switch (encoding) { case _AU_FORMAT_MULAW_8: track->f.sampleWidth = 16; track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; track->f.compressionType = AF_COMPRESSION_G711_ULAW; track->f.byteOrder = _AF_BYTEORDER_NATIVE; track->f.bytesPerPacket = track->f.channelCount; break; case _AU_FORMAT_ALAW_8: track->f.sampleWidth = 16; track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; track->f.compressionType = AF_COMPRESSION_G711_ALAW; track->f.byteOrder = _AF_BYTEORDER_NATIVE; track->f.bytesPerPacket = track->f.channelCount; break; case _AU_FORMAT_LINEAR_8: track->f.sampleWidth = 8; track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; break; case _AU_FORMAT_LINEAR_16: track->f.sampleWidth = 16; track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; break; case _AU_FORMAT_LINEAR_24: track->f.sampleWidth = 24; track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; break; case _AU_FORMAT_LINEAR_32: track->f.sampleWidth = 32; track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; break; case _AU_FORMAT_FLOAT: track->f.sampleWidth = 32; track->f.sampleFormat = AF_SAMPFMT_FLOAT; break; case _AU_FORMAT_DOUBLE: track->f.sampleWidth = 64; track->f.sampleFormat = AF_SAMPFMT_DOUBLE; break; default: /* This encoding method is not recognized. */ _af_error(AF_BAD_SAMPFMT, "bad sample format"); return AF_FAIL; } if (track->f.isUncompressed()) track->f.computeBytesPerPacketPCM(); _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); track->computeTotalFileFrames(); return AF_SUCCEED; } bool NeXTFile::recognize(File *fh) { uint8_t buffer[4]; fh->seek(0, File::SeekFromBeginning); if (fh->read(buffer, 4) != 4 || memcmp(buffer, ".snd", 4) != 0) return false; return true; } AFfilesetup NeXTFile::completeSetup(AFfilesetup setup) { if (setup->trackSet && setup->trackCount != 1) { _af_error(AF_BAD_NUMTRACKS, "NeXT files must have exactly 1 track"); return AF_NULL_FILESETUP; } TrackSetup *track = setup->getTrack(); if (!track) return AF_NULL_FILESETUP; if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) { _af_error(AF_BAD_FILEFMT, "NeXT format does not support unsigned data"); _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth); } if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP) { if (track->f.sampleWidth != 8 && track->f.sampleWidth != 16 && track->f.sampleWidth != 24 && track->f.sampleWidth != 32) { _af_error(AF_BAD_WIDTH, "invalid sample width %d for NeXT file (only 8-, 16-, 24-, and 32-bit data are allowed)", track->f.sampleWidth); return AF_NULL_FILESETUP; } } if (track->f.compressionType != AF_COMPRESSION_NONE && track->f.compressionType != AF_COMPRESSION_G711_ULAW && track->f.compressionType != AF_COMPRESSION_G711_ALAW) { _af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not implemented for NeXT files"); return AF_NULL_FILESETUP; } if (track->f.isUncompressed() && track->byteOrderSet && track->f.byteOrder != AF_BYTEORDER_BIGENDIAN && track->f.isByteOrderSignificant()) { _af_error(AF_BAD_BYTEORDER, "NeXT format supports only big-endian data"); return AF_NULL_FILESETUP; } if (track->f.isUncompressed()) track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; if (track->aesDataSet) { _af_error(AF_BAD_FILESETUP, "NeXT files cannot have AES data"); return AF_NULL_FILESETUP; } if (track->markersSet && track->markerCount != 0) { _af_error(AF_BAD_FILESETUP, "NeXT format does not support markers"); return AF_NULL_FILESETUP; } if (setup->instrumentSet && setup->instrumentCount != 0) { _af_error(AF_BAD_FILESETUP, "NeXT format does not support instruments"); return AF_NULL_FILESETUP; } if (setup->miscellaneousSet && setup->miscellaneousCount != 0) { _af_error(AF_BAD_FILESETUP, "NeXT format does not support miscellaneous data"); return AF_NULL_FILESETUP; } return _af_filesetup_copy(setup, &nextDefaultFileSetup, false); } static uint32_t nextencodingtype (AudioFormat *format); status NeXTFile::update() { writeHeader(); return AF_SUCCEED; } status NeXTFile::writeHeader() { Track *track = getTrack(); if (m_fh->seek(0, File::SeekFromBeginning) != 0) _af_error(AF_BAD_LSEEK, "bad seek"); uint32_t offset = track->fpos_first_frame; uint32_t length = track->data_size; uint32_t encoding = nextencodingtype(&track->f); uint32_t sampleRate = track->f.sampleRate; uint32_t channelCount = track->f.channelCount; m_fh->write(".snd", 4); writeU32(&offset); writeU32(&length); writeU32(&encoding); writeU32(&sampleRate); writeU32(&channelCount); return AF_SUCCEED; } static uint32_t nextencodingtype (AudioFormat *format) { uint32_t encoding = 0; if (format->compressionType != AF_COMPRESSION_NONE) { if (format->compressionType == AF_COMPRESSION_G711_ULAW) encoding = _AU_FORMAT_MULAW_8; else if (format->compressionType == AF_COMPRESSION_G711_ALAW) encoding = _AU_FORMAT_ALAW_8; } else if (format->sampleFormat == AF_SAMPFMT_TWOSCOMP) { if (format->sampleWidth == 8) encoding = _AU_FORMAT_LINEAR_8; else if (format->sampleWidth == 16) encoding = _AU_FORMAT_LINEAR_16; else if (format->sampleWidth == 24) encoding = _AU_FORMAT_LINEAR_24; else if (format->sampleWidth == 32) encoding = _AU_FORMAT_LINEAR_32; } else if (format->sampleFormat == AF_SAMPFMT_FLOAT) encoding = _AU_FORMAT_FLOAT; else if (format->sampleFormat == AF_SAMPFMT_DOUBLE) encoding = _AU_FORMAT_DOUBLE; return encoding; } status NeXTFile::writeInit(AFfilesetup setup) { if (initFromSetup(setup) == AF_FAIL) return AF_FAIL; writeHeader(); Track *track = getTrack(); track->fpos_first_frame = 28; return AF_SUCCEED; }