/* Audio File Library Copyright (C) 1998-2000, Michael Pruett Copyright (C) 2000, Silicon Graphics, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. */ /* aiffwrite.c This file contains routines for writing AIFF and AIFF-C format sound files. */ #include #include #include #include "extended.h" #include "afinternal.h" #include "audiofile.h" #include "aiff.h" #include "byteorder.h" #include "util.h" #include "setup.h" status _af_aiff_update (AFfilehandle file); static status WriteCOMM (AFfilehandle file); static status WriteSSND (AFfilehandle file); static status WriteMARK (AFfilehandle file); static status WriteINST (AFfilehandle file); static status WriteFVER (AFfilehandle file); static status WriteAESD (AFfilehandle file); static status WriteMiscellaneous (AFfilehandle file); static _AIFFInfo *aiffinfo_new () { _AIFFInfo *aiff = _af_malloc(sizeof (_AIFFInfo)); aiff->miscellaneousPosition = 0; aiff->FVER_offset = 0; aiff->COMM_offset = 0; aiff->MARK_offset = 0; aiff->INST_offset = 0; aiff->AESD_offset = 0; aiff->SSND_offset = 0; return aiff; } status _af_aiff_write_init (AFfilesetup setup, AFfilehandle file) { u_int32_t fileSize = HOST_TO_BENDIAN_INT32(0); assert(file); assert(file->fileFormat == AF_FILE_AIFF || file->fileFormat == AF_FILE_AIFFC); if (_af_filesetup_make_handle(setup, file) == AF_FAIL) return AF_FAIL; file->formatSpecific = aiffinfo_new(); af_fwrite("FORM", 4, 1, file->fh); af_fwrite(&fileSize, 4, 1, file->fh); if (file->fileFormat == AF_FILE_AIFF) af_fwrite("AIFF", 4, 1, file->fh); else if (file->fileFormat == AF_FILE_AIFFC) af_fwrite("AIFC", 4, 1, file->fh); if (file->fileFormat == AF_FILE_AIFFC) WriteFVER(file); WriteCOMM(file); WriteMARK(file); WriteINST(file); WriteAESD(file); WriteMiscellaneous(file); WriteSSND(file); return AF_SUCCEED; } status _af_aiff_update (AFfilehandle file) { _Track *track; u_int32_t length; assert(file); track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); #ifdef DEBUG printf("_af_aiff_update called.\n"); #endif /* Get the length of the file. */ length = af_flength(file->fh); length -= 8; length = HOST_TO_BENDIAN_INT32(length); /* Set the length of the FORM chunk. */ af_fseek(file->fh, 4, SEEK_SET); af_fwrite(&length, 4, 1, file->fh); if (file->fileFormat == AF_FILE_AIFFC) WriteFVER(file); WriteCOMM(file); WriteMARK(file); WriteINST(file); WriteAESD(file); WriteMiscellaneous(file); WriteSSND(file); return AF_SUCCEED; } static status WriteCOMM (const AFfilehandle file) { _Track *track; u_int32_t size; u_int16_t sb; u_int32_t lb; unsigned char eb[10]; _AIFFInfo *aiff; aiff = file->formatSpecific; if (aiff->COMM_offset == 0) aiff->COMM_offset = af_ftell(file->fh); else af_fseek(file->fh, aiff->COMM_offset, SEEK_SET); track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); size = 18; if (file->fileFormat == AF_FILE_AIFFC) size = 38; af_fwrite("COMM", 4, 1, file->fh); size = HOST_TO_BENDIAN_INT32(size); af_fwrite(&size, 4, 1, file->fh); /* number of channels, 2 bytes */ sb = HOST_TO_BENDIAN_INT16(track->f.channelCount); af_fwrite(&sb, 2, 1, file->fh); /* number of sample frames, 4 bytes */ lb = HOST_TO_BENDIAN_INT32(track->totalfframes); af_fwrite(&lb, 4, 1, file->fh); /* sample size, 2 bytes */ sb = HOST_TO_BENDIAN_INT16(track->f.sampleWidth); af_fwrite(&sb, 2, 1, file->fh); /* sample rate, 10 bytes */ ConvertToIeeeExtended(track->f.sampleRate, eb); af_fwrite(eb, 10, 1, file->fh); if (file->fileFormat == AF_FILE_AIFFC) { char sizeByte, zero = 0; char compressionName[] = "not compressed"; af_fwrite("NONE", 4, 1, file->fh); sizeByte = strlen(compressionName); af_fwrite(&sizeByte, 1, 1, file->fh); af_fwrite(compressionName, sizeByte, 1, file->fh); if ((sizeByte % 2) == 0) af_fwrite(&zero, 1, 1, file->fh); } return AF_SUCCEED; } /* The AESD chunk contains information pertinent to audio recording devices. */ static status WriteAESD (const AFfilehandle file) { _Track *track; u_int32_t size = 24; _AIFFInfo *aiff; assert(file); aiff = file->formatSpecific; track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); if (track->hasAESData == AF_FALSE) return AF_SUCCEED; if (aiff->AESD_offset == 0) aiff->AESD_offset = af_ftell(file->fh); else af_fseek(file->fh, aiff->AESD_offset, SEEK_SET); if (af_fwrite("AESD", 4, 1, file->fh) < 1) return AF_FAIL; size = HOST_TO_BENDIAN_INT32(size); if (af_fwrite(&size, 4, 1, file->fh) < 1) return AF_FAIL; if (af_fwrite(track->aesData, 24, 1, file->fh) < 1) return AF_FAIL; return AF_SUCCEED; } static status WriteSSND (AFfilehandle file) { _Track *track; u_int32_t chunkSize, zero = 0; _AIFFInfo *aiff; assert(file); assert(file->fh); aiff = file->formatSpecific; track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); if (aiff->SSND_offset == 0) aiff->SSND_offset = af_ftell(file->fh); else af_fseek(file->fh, aiff->SSND_offset, SEEK_SET); chunkSize = _af_format_frame_size(&track->f, AF_FALSE) * track->totalfframes + 8; af_fwrite("SSND", 4, 1, file->fh); chunkSize = HOST_TO_BENDIAN_INT32(chunkSize); af_fwrite(&chunkSize, 4, 1, file->fh); /* data offset */ af_fwrite(&zero, 4, 1, file->fh); /* block size */ af_fwrite(&zero, 4, 1, file->fh); if (track->fpos_first_frame == 0) track->fpos_first_frame = af_ftell(file->fh); return AF_SUCCEED; } static status WriteINST (AFfilehandle file) { u_int32_t length; struct _INST instrumentdata; length = 20; length = HOST_TO_BENDIAN_INT32(length); instrumentdata.sustainLoopPlayMode = HOST_TO_BENDIAN_INT16(afGetLoopMode(file, AF_DEFAULT_INST, 1)); instrumentdata.sustainLoopBegin = HOST_TO_BENDIAN_INT16(afGetLoopStart(file, AF_DEFAULT_INST, 1)); instrumentdata.sustainLoopEnd = HOST_TO_BENDIAN_INT16(afGetLoopEnd(file, AF_DEFAULT_INST, 1)); instrumentdata.releaseLoopPlayMode = HOST_TO_BENDIAN_INT16(afGetLoopMode(file, AF_DEFAULT_INST, 2)); instrumentdata.releaseLoopBegin = HOST_TO_BENDIAN_INT16(afGetLoopStart(file, AF_DEFAULT_INST, 2)); instrumentdata.releaseLoopEnd = HOST_TO_BENDIAN_INT16(afGetLoopEnd(file, AF_DEFAULT_INST, 2)); af_fwrite("INST", 4, 1, file->fh); af_fwrite(&length, 4, 1, file->fh); instrumentdata.baseNote = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_BASENOTE); af_fwrite(&instrumentdata.baseNote, 1, 1, file->fh); instrumentdata.detune = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_NUMCENTS_DETUNE); af_fwrite(&instrumentdata.detune, 1, 1, file->fh); instrumentdata.lowNote = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_LONOTE); af_fwrite(&instrumentdata.lowNote, 1, 1, file->fh); instrumentdata.highNote = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_HINOTE); af_fwrite(&instrumentdata.highNote, 1, 1, file->fh); instrumentdata.lowVelocity = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_LOVELOCITY); af_fwrite(&instrumentdata.lowVelocity, 1, 1, file->fh); instrumentdata.highVelocity = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_HIVELOCITY); af_fwrite(&instrumentdata.highVelocity, 1, 1, file->fh); instrumentdata.gain = afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_NUMDBS_GAIN); instrumentdata.gain = HOST_TO_BENDIAN_INT16(instrumentdata.gain); af_fwrite(&instrumentdata.gain, 2, 1, file->fh); af_fwrite(&instrumentdata.sustainLoopPlayMode, 2, 1, file->fh); af_fwrite(&instrumentdata.sustainLoopBegin, 2, 1, file->fh); af_fwrite(&instrumentdata.sustainLoopEnd, 2, 1, file->fh); af_fwrite(&instrumentdata.releaseLoopPlayMode, 2, 1, file->fh); af_fwrite(&instrumentdata.releaseLoopBegin, 2, 1, file->fh); af_fwrite(&instrumentdata.releaseLoopEnd, 2, 1, file->fh); return AF_SUCCEED; } static status WriteMARK (AFfilehandle file) { off_t chunkStartPosition, chunkEndPosition; u_int32_t length = 0; u_int16_t numMarkers, sb; int i, *markids; _Track *track; _AIFFInfo *aiff; assert(file); aiff = file->formatSpecific; track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); if (aiff->MARK_offset == 0) aiff->MARK_offset = af_ftell(file->fh); else af_fseek(file->fh, aiff->MARK_offset, SEEK_SET); af_fwrite("MARK", 4, 1, file->fh); af_fwrite(&length, 4, 1, file->fh); chunkStartPosition = af_ftell(file->fh); numMarkers = track->markerCount; markids = _af_malloc(numMarkers * sizeof (int)); afGetMarkIDs(file, AF_DEFAULT_TRACK, markids); sb = HOST_TO_BENDIAN_INT16(numMarkers); af_fwrite(&sb, 2, 1, file->fh); for (i=0; ifh); af_fwrite(&position, 4, 1, file->fh); /* Write the strings in Pascal style. */ assert(track->markers[i].name); namelength = strlen(track->markers[i].name); af_fwrite(&namelength, 1, 1, file->fh); af_fwrite(track->markers[i].name, 1, namelength, file->fh); if ((namelength % 2) == 0) af_fwrite(&zero, 1, 1, file->fh); } chunkEndPosition = af_ftell(file->fh); length = chunkEndPosition - chunkStartPosition; #ifdef DEBUG printf(" end: %d\n", chunkEndPosition); printf(" length: %d\n", length); #endif af_fseek(file->fh, chunkStartPosition - 4, SEEK_SET); length = HOST_TO_BENDIAN_INT32(length); af_fwrite(&length, 4, 1, file->fh); af_fseek(file->fh, chunkEndPosition, SEEK_SET); return AF_SUCCEED; } /* The FVER chunk, if present, is always the first chunk in the file. */ static status WriteFVER (AFfilehandle file) { u_int32_t chunkSize, timeStamp; _AIFFInfo *aiff; assert(file->fileFormat == AF_FILE_AIFFC); aiff = file->formatSpecific; if (aiff->FVER_offset == 0) aiff->FVER_offset = af_ftell(file->fh); else af_fseek(file->fh, aiff->FVER_offset, SEEK_SET); af_fwrite("FVER", 4, 1, file->fh); chunkSize = 4; chunkSize = HOST_TO_BENDIAN_INT32(chunkSize); af_fwrite(&chunkSize, 4, 1, file->fh); timeStamp = AIFCVersion1; timeStamp = HOST_TO_BENDIAN_INT32(timeStamp); af_fwrite(&timeStamp, 4, 1, file->fh); return AF_SUCCEED; } /* WriteMiscellaneous writes all the miscellaneous data chunks in a file handle structure to an AIFF or AIFF-C file. */ static status WriteMiscellaneous (AFfilehandle file) { _AIFFInfo *aiff; int i; aiff = (_AIFFInfo *) file->formatSpecific; if (aiff->miscellaneousPosition == 0) aiff->miscellaneousPosition = af_ftell(file->fh); else af_fseek(file->fh, aiff->miscellaneousPosition, SEEK_SET); for (i=0; imiscellaneousCount; i++) { _Miscellaneous *misc = &file->miscellaneous[i]; u_int32_t chunkType, chunkSize; u_int8_t padByte = 0; #ifdef DEBUG printf("WriteMiscellaneous: %d, type %d\n", i, misc->type); #endif switch (misc->type) { case AF_MISC_NAME: memcpy(&chunkType, "NAME", 4); break; case AF_MISC_AUTH: memcpy(&chunkType, "AUTH", 4); break; case AF_MISC_COPY: memcpy(&chunkType, "(c) ", 4); break; case AF_MISC_ANNO: memcpy(&chunkType, "ANNO", 4); break; case AF_MISC_MIDI: memcpy(&chunkType, "MIDI", 4); break; case AF_MISC_APPL: memcpy(&chunkType, "APPL", 4); break; } chunkSize = HOST_TO_BENDIAN_INT32(misc->size); af_fwrite(&chunkType, 4, 1, file->fh); af_fwrite(&chunkSize, 4, 1, file->fh); /* Write the miscellaneous buffer and then a pad byte if necessary. If the buffer is null, skip the space for now. */ if (misc->buffer != NULL) af_fwrite(misc->buffer, misc->size, 1, file->fh); else af_fseek(file->fh, misc->size, SEEK_CUR); if (misc->size % 2 != 0) af_fwrite(&padByte, 1, 1, file->fh); } return AF_SUCCEED; }