source: trunk/third/audiofile/libaudiofile/aiffwrite.c @ 17099

Revision 17099, 14.4 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17098, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2        Audio File Library
3        Copyright (C) 1998-2000, Michael Pruett <michael@68k.org>
4        Copyright (C) 2000-2001, Silicon Graphics, Inc.
5
6        This library is free software; you can redistribute it and/or
7        modify it under the terms of the GNU Library General Public
8        License as published by the Free Software Foundation; either
9        version 2 of the License, or (at your option) any later version.
10
11        This library is distributed in the hope that it will be useful,
12        but WITHOUT ANY WARRANTY; without even the implied warranty of
13        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14        Library General Public License for more details.
15
16        You should have received a copy of the GNU Library General Public
17        License along with this library; if not, write to the
18        Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19        Boston, MA  02111-1307  USA.
20*/
21
22/*
23        aiffwrite.c
24
25        This file contains routines for writing AIFF and AIFF-C format
26        sound files.
27*/
28
29#include <assert.h>
30#include <sys/types.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include "extended.h"
35#include "afinternal.h"
36#include "audiofile.h"
37#include "aiff.h"
38#include "byteorder.h"
39#include "util.h"
40#include "setup.h"
41
42status _af_aiff_update (AFfilehandle file);
43
44static status WriteCOMM (AFfilehandle file);
45static status WriteSSND (AFfilehandle file);
46static status WriteMARK (AFfilehandle file);
47static status WriteINST (AFfilehandle file);
48static status WriteFVER (AFfilehandle file);
49static status WriteAESD (AFfilehandle file);
50static status WriteMiscellaneous (AFfilehandle file);
51
52static _AIFFInfo *aiffinfo_new (void)
53{
54        _AIFFInfo       *aiff = _af_malloc(sizeof (_AIFFInfo));
55
56        aiff->miscellaneousPosition = 0;
57        aiff->FVER_offset = 0;
58        aiff->COMM_offset = 0;
59        aiff->MARK_offset = 0;
60        aiff->INST_offset = 0;
61        aiff->AESD_offset = 0;
62        aiff->SSND_offset = 0;
63
64        return aiff;
65}
66
67status _af_aiff_write_init (AFfilesetup setup, AFfilehandle file)
68{
69        u_int32_t       fileSize = HOST_TO_BENDIAN_INT32(0);
70
71        assert(file);
72        assert(file->fileFormat == AF_FILE_AIFF ||
73                file->fileFormat == AF_FILE_AIFFC);
74
75        if (_af_filesetup_make_handle(setup, file) == AF_FAIL)
76                return AF_FAIL;
77
78        file->formatSpecific = aiffinfo_new();
79
80        af_fwrite("FORM", 4, 1, file->fh);
81        af_fwrite(&fileSize, 4, 1, file->fh);
82
83        if (file->fileFormat == AF_FILE_AIFF)
84                af_fwrite("AIFF", 4, 1, file->fh);
85        else if (file->fileFormat == AF_FILE_AIFFC)
86                af_fwrite("AIFC", 4, 1, file->fh);
87
88        if (file->fileFormat == AF_FILE_AIFFC)
89                WriteFVER(file);
90
91        WriteCOMM(file);
92        WriteMARK(file);
93        WriteINST(file);
94        WriteAESD(file);
95        WriteMiscellaneous(file);
96        WriteSSND(file);
97
98        return AF_SUCCEED;
99}
100
101status _af_aiff_update (AFfilehandle file)
102{
103        _Track          *track;
104        u_int32_t       length;
105
106        assert(file);
107
108        track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK);
109
110#ifdef DEBUG
111        printf("_af_aiff_update called.\n");
112#endif
113
114        /* Get the length of the file. */
115        length = af_flength(file->fh);
116        length -= 8;
117        length = HOST_TO_BENDIAN_INT32(length);
118
119        /* Set the length of the FORM chunk. */
120        af_fseek(file->fh, 4, SEEK_SET);
121        af_fwrite(&length, 4, 1, file->fh);
122
123        if (file->fileFormat == AF_FILE_AIFFC)
124                WriteFVER(file);
125
126        WriteCOMM(file);
127        WriteMARK(file);
128        WriteINST(file);
129        WriteAESD(file);
130        WriteMiscellaneous(file);
131        WriteSSND(file);
132
133        return AF_SUCCEED;
134}
135
136static status WriteCOMM (const AFfilehandle file)
137{
138        _Track          *track;
139        u_int32_t       chunkSize;
140        _AIFFInfo       *aiff;
141        bool            isAIFFC;
142
143        u_int16_t       sb;
144        u_int32_t       lb;
145        unsigned char   eb[10];
146
147        u_int8_t        compressionTag[4];
148        /* Pascal strings can occupy only 255 bytes (+ a size byte). */
149        char            compressionName[256];
150
151        isAIFFC = file->fileFormat == AF_FILE_AIFFC;
152
153        aiff = file->formatSpecific;
154
155        /*
156                If COMM_offset hasn't been set yet, set it to the
157                current offset.
158        */
159        if (aiff->COMM_offset == 0)
160                aiff->COMM_offset = af_ftell(file->fh);
161        else
162                af_fseek(file->fh, aiff->COMM_offset, SEEK_SET);
163
164        track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK);
165
166        if (isAIFFC)
167        {
168                if (track->f.compressionType == AF_COMPRESSION_NONE)
169                {
170                        if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP)
171                        {
172                                memcpy(compressionTag, "NONE", 4);
173                                strcpy(compressionName, "not compressed");
174                        }
175                        else if (track->f.sampleFormat == AF_SAMPFMT_FLOAT)
176                        {
177                                memcpy(compressionTag, "fl32", 4);
178                                strcpy(compressionName, "32-bit Floating Point");
179                        }
180                        else if (track->f.sampleFormat == AF_SAMPFMT_DOUBLE)
181                        {
182                                memcpy(compressionTag, "fl64", 4);
183                                strcpy(compressionName, "64-bit Floating Point");
184                        }
185                        /*
186                                We disallow unsigned sample data for
187                                AIFF files in _af_aiff_complete_setup,
188                                so the next condition should never be
189                                satisfied.
190                        */
191                        else if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED)
192                        {
193                                _af_error(AF_BAD_SAMPFMT,
194                                        "AIFF/AIFF-C format does not support unsigned data");
195                                assert(0);
196                                return AF_FAIL;
197                        }
198                }
199                else if (track->f.compressionType == AF_COMPRESSION_G711_ULAW)
200                {
201                        memcpy(compressionTag, "ulaw", 4);
202                        strcpy(compressionName, "CCITT G.711 u-law");
203                }
204                else if (track->f.compressionType == AF_COMPRESSION_G711_ALAW)
205                {
206                        memcpy(compressionTag, "alaw", 4);
207                        strcpy(compressionName, "CCITT G.711 A-law");
208                }
209        }
210
211        af_fwrite("COMM", 4, 1, file->fh);
212
213        /*
214                For AIFF-C files, the length of the COMM chunk is 22
215                plus the length of the compression name plus the size
216                byte.  If the length of the data is an odd number of
217                bytes, add a zero pad byte at the end, but don't
218                include the pad byte in the chunk's size.
219        */
220        if (isAIFFC)
221                chunkSize = 22 + strlen(compressionName) + 1;
222        else
223                chunkSize = 18;
224        chunkSize = HOST_TO_BENDIAN_INT32(chunkSize);
225        af_fwrite(&chunkSize, 4, 1, file->fh);
226
227        /* number of channels, 2 bytes */
228        sb = HOST_TO_BENDIAN_INT16(track->f.channelCount);
229        af_fwrite(&sb, 2, 1, file->fh);
230
231        /* number of sample frames, 4 bytes */
232        lb = HOST_TO_BENDIAN_INT32(track->totalfframes);
233        af_fwrite(&lb, 4, 1, file->fh);
234
235        /* sample size, 2 bytes */
236        sb = HOST_TO_BENDIAN_INT16(track->f.sampleWidth);
237        af_fwrite(&sb, 2, 1, file->fh);
238
239        /* sample rate, 10 bytes */
240        ConvertToIeeeExtended(track->f.sampleRate, eb);
241        af_fwrite(eb, 10, 1, file->fh);
242
243        if (file->fileFormat == AF_FILE_AIFFC)
244        {
245                u_int8_t        sizeByte, zero = 0;
246
247                af_fwrite(compressionTag, 4, 1, file->fh);
248
249                sizeByte = strlen(compressionName);
250
251                af_fwrite(&sizeByte, 1, 1, file->fh);
252                af_fwrite(compressionName, sizeByte, 1, file->fh);
253
254                /*
255                        If sizeByte is even, then 1+sizeByte
256                        (the length of the string) is odd.  Add an
257                        extra byte to make the chunk's extent even
258                        (even though the chunk's size may be odd).
259                */
260                if ((sizeByte % 2) == 0)
261                        af_fwrite(&zero, 1, 1, file->fh);
262        }
263
264        return AF_SUCCEED;
265}
266
267/*
268        The AESD chunk contains information pertinent to audio recording
269        devices.
270*/
271static status WriteAESD (const AFfilehandle file)
272{
273        _Track          *track;
274        u_int32_t       size = 24;
275        _AIFFInfo       *aiff;
276
277        assert(file);
278
279        aiff = file->formatSpecific;
280
281        track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK);
282
283        if (track->hasAESData == AF_FALSE)
284                return AF_SUCCEED;
285
286        if (aiff->AESD_offset == 0)
287                aiff->AESD_offset = af_ftell(file->fh);
288        else
289                af_fseek(file->fh, aiff->AESD_offset, SEEK_SET);
290
291        if (af_fwrite("AESD", 4, 1, file->fh) < 1)
292                return AF_FAIL;
293
294        size = HOST_TO_BENDIAN_INT32(size);
295
296        if (af_fwrite(&size, 4, 1, file->fh) < 1)
297                return AF_FAIL;
298
299        if (af_fwrite(track->aesData, 24, 1, file->fh) < 1)
300                return AF_FAIL;
301
302        return AF_SUCCEED;
303}
304
305static status WriteSSND (AFfilehandle file)
306{
307        _Track          *track;
308        u_int32_t       chunkSize, zero = 0;
309        _AIFFInfo       *aiff;
310
311        assert(file);
312        assert(file->fh);
313
314        aiff = file->formatSpecific;
315
316        track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK);
317
318        if (aiff->SSND_offset == 0)
319                aiff->SSND_offset = af_ftell(file->fh);
320        else
321                af_fseek(file->fh, aiff->SSND_offset, SEEK_SET);
322
323        chunkSize = _af_format_frame_size(&track->f, AF_FALSE) *
324                track->totalfframes + 8;
325
326        af_fwrite("SSND", 4, 1, file->fh);
327        chunkSize = HOST_TO_BENDIAN_INT32(chunkSize);
328        af_fwrite(&chunkSize, 4, 1, file->fh);
329
330        /* data offset */
331        af_fwrite(&zero, 4, 1, file->fh);
332        /* block size */
333        af_fwrite(&zero, 4, 1, file->fh);
334
335        if (track->fpos_first_frame == 0)
336                track->fpos_first_frame = af_ftell(file->fh);
337
338        return AF_SUCCEED;
339}
340
341static status WriteINST (AFfilehandle file)
342{
343        u_int32_t       length;
344        struct _INST    instrumentdata;
345
346        length = 20;
347        length = HOST_TO_BENDIAN_INT32(length);
348
349        instrumentdata.sustainLoopPlayMode =
350                HOST_TO_BENDIAN_INT16(afGetLoopMode(file, AF_DEFAULT_INST, 1));
351        instrumentdata.sustainLoopBegin =
352                HOST_TO_BENDIAN_INT16(afGetLoopStart(file, AF_DEFAULT_INST, 1));
353        instrumentdata.sustainLoopEnd =
354                HOST_TO_BENDIAN_INT16(afGetLoopEnd(file, AF_DEFAULT_INST, 1));
355
356        instrumentdata.releaseLoopPlayMode =
357                HOST_TO_BENDIAN_INT16(afGetLoopMode(file, AF_DEFAULT_INST, 2));
358        instrumentdata.releaseLoopBegin =
359                HOST_TO_BENDIAN_INT16(afGetLoopStart(file, AF_DEFAULT_INST, 2));
360        instrumentdata.releaseLoopEnd =
361                HOST_TO_BENDIAN_INT16(afGetLoopEnd(file, AF_DEFAULT_INST, 2));
362
363        af_fwrite("INST", 4, 1, file->fh);
364        af_fwrite(&length, 4, 1, file->fh);
365
366        instrumentdata.baseNote =
367                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_BASENOTE);
368        af_fwrite(&instrumentdata.baseNote, 1, 1, file->fh);
369        instrumentdata.detune =
370                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_NUMCENTS_DETUNE);
371        af_fwrite(&instrumentdata.detune, 1, 1, file->fh);
372        instrumentdata.lowNote =
373                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_LONOTE);
374        af_fwrite(&instrumentdata.lowNote, 1, 1, file->fh);
375        instrumentdata.highNote =
376                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_HINOTE);
377        af_fwrite(&instrumentdata.highNote, 1, 1, file->fh);
378        instrumentdata.lowVelocity =
379                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_LOVELOCITY);
380        af_fwrite(&instrumentdata.lowVelocity, 1, 1, file->fh);
381        instrumentdata.highVelocity =
382                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_HIVELOCITY);
383        af_fwrite(&instrumentdata.highVelocity, 1, 1, file->fh);
384
385        instrumentdata.gain =
386                afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_NUMDBS_GAIN);
387        instrumentdata.gain = HOST_TO_BENDIAN_INT16(instrumentdata.gain);
388        af_fwrite(&instrumentdata.gain, 2, 1, file->fh);
389
390        af_fwrite(&instrumentdata.sustainLoopPlayMode, 2, 1, file->fh);
391        af_fwrite(&instrumentdata.sustainLoopBegin, 2, 1, file->fh);
392        af_fwrite(&instrumentdata.sustainLoopEnd, 2, 1, file->fh);
393
394        af_fwrite(&instrumentdata.releaseLoopPlayMode, 2, 1, file->fh);
395        af_fwrite(&instrumentdata.releaseLoopBegin, 2, 1, file->fh);
396        af_fwrite(&instrumentdata.releaseLoopEnd, 2, 1, file->fh);
397
398        return AF_SUCCEED;
399}
400
401static status WriteMARK (AFfilehandle file)
402{
403        off_t           chunkStartPosition, chunkEndPosition;
404        u_int32_t       length = 0;
405        u_int16_t       numMarkers, sb;
406        int             i, *markids;
407        _AIFFInfo       *aiff;
408
409        assert(file);
410
411        numMarkers = afGetMarkIDs(file, AF_DEFAULT_TRACK, NULL);
412        if (numMarkers == 0)
413                return AF_SUCCEED;
414
415        aiff = file->formatSpecific;
416
417        if (aiff->MARK_offset == 0)
418                aiff->MARK_offset = af_ftell(file->fh);
419        else
420                af_fseek(file->fh, aiff->MARK_offset, SEEK_SET);
421
422        af_fwrite("MARK", 4, 1, file->fh);
423        af_fwrite(&length, 4, 1, file->fh);
424
425        chunkStartPosition = af_ftell(file->fh);
426
427        markids = _af_calloc(numMarkers, sizeof (int));
428        assert(markids);
429        afGetMarkIDs(file, AF_DEFAULT_TRACK, markids);
430
431        sb = HOST_TO_BENDIAN_INT16(numMarkers);
432        af_fwrite(&sb, 2, 1, file->fh);
433
434        for (i=0; i<numMarkers; i++)
435        {
436                u_int8_t        namelength, zero = 0;
437                u_int16_t       id;
438                u_int32_t       position;
439                char            *name;
440
441                id = markids[i];
442                position = afGetMarkPosition(file, AF_DEFAULT_TRACK, markids[i]);
443
444                id = HOST_TO_BENDIAN_INT16(id);
445                position = HOST_TO_BENDIAN_INT32(position);
446
447                af_fwrite(&id, 2, 1, file->fh);
448                af_fwrite(&position, 4, 1, file->fh);
449
450                name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]);
451                assert(name);
452                namelength = strlen(name);
453
454                /* Write the name as a Pascal-style string. */
455                af_fwrite(&namelength, 1, 1, file->fh);
456                af_fwrite(name, 1, namelength, file->fh);
457
458                /*
459                        We need a pad byte if the length of the
460                        Pascal-style string (including the size byte)
461                        is odd, i.e. if namelength + 1 % 2 == 1.
462                */
463                if ((namelength % 2) == 0)
464                        af_fwrite(&zero, 1, 1, file->fh);
465        }
466
467        free(markids);
468
469        chunkEndPosition = af_ftell(file->fh);
470        length = chunkEndPosition - chunkStartPosition;
471
472#ifdef DEBUG
473        printf(" end: %d\n", chunkEndPosition);
474        printf(" length: %d\n", length);
475#endif
476
477        af_fseek(file->fh, chunkStartPosition - 4, SEEK_SET);
478
479        length = HOST_TO_BENDIAN_INT32(length);
480        af_fwrite(&length, 4, 1, file->fh);
481        af_fseek(file->fh, chunkEndPosition, SEEK_SET);
482
483        return AF_SUCCEED;
484}
485
486/*
487        The FVER chunk, if present, is always the first chunk in the file.
488*/
489static status WriteFVER (AFfilehandle file)
490{
491        u_int32_t       chunkSize, timeStamp;
492        _AIFFInfo       *aiff;
493
494        assert(file->fileFormat == AF_FILE_AIFFC);
495
496        aiff = file->formatSpecific;
497
498        if (aiff->FVER_offset == 0)
499                aiff->FVER_offset = af_ftell(file->fh);
500        else
501                af_fseek(file->fh, aiff->FVER_offset, SEEK_SET);
502
503        af_fwrite("FVER", 4, 1, file->fh);
504
505        chunkSize = 4;
506        chunkSize = HOST_TO_BENDIAN_INT32(chunkSize);
507        af_fwrite(&chunkSize, 4, 1, file->fh);
508
509        timeStamp = AIFCVersion1;
510        timeStamp = HOST_TO_BENDIAN_INT32(timeStamp);
511        af_fwrite(&timeStamp, 4, 1, file->fh);
512
513        return AF_SUCCEED;
514}
515
516/*
517        WriteMiscellaneous writes all the miscellaneous data chunks in a
518        file handle structure to an AIFF or AIFF-C file.
519*/
520static status WriteMiscellaneous (AFfilehandle file)
521{
522        _AIFFInfo       *aiff;
523        int             i;
524
525        aiff = (_AIFFInfo *) file->formatSpecific;
526
527        if (aiff->miscellaneousPosition == 0)
528                aiff->miscellaneousPosition = af_ftell(file->fh);
529        else
530                af_fseek(file->fh, aiff->miscellaneousPosition, SEEK_SET);
531
532        for (i=0; i<file->miscellaneousCount; i++)
533        {
534                _Miscellaneous  *misc = &file->miscellaneous[i];
535                u_int32_t       chunkType, chunkSize;
536                u_int8_t        padByte = 0;
537
538#ifdef DEBUG
539                printf("WriteMiscellaneous: %d, type %d\n", i, misc->type);
540#endif
541
542                switch (misc->type)
543                {
544                        case AF_MISC_NAME:
545                                memcpy(&chunkType, "NAME", 4); break;
546                        case AF_MISC_AUTH:
547                                memcpy(&chunkType, "AUTH", 4); break;
548                        case AF_MISC_COPY:
549                                memcpy(&chunkType, "(c) ", 4); break;
550                        case AF_MISC_ANNO:
551                                memcpy(&chunkType, "ANNO", 4); break;
552                        case AF_MISC_MIDI:
553                                memcpy(&chunkType, "MIDI", 4); break;
554                        case AF_MISC_APPL:
555                                memcpy(&chunkType, "APPL", 4); break;
556                }
557
558                chunkSize = HOST_TO_BENDIAN_INT32(misc->size);
559
560                af_fwrite(&chunkType, 4, 1, file->fh);
561                af_fwrite(&chunkSize, 4, 1, file->fh);
562                /*
563                        Write the miscellaneous buffer and then a pad byte
564                        if necessary.  If the buffer is null, skip the space
565                        for now.
566                */
567                if (misc->buffer != NULL)
568                        af_fwrite(misc->buffer, misc->size, 1, file->fh);
569                else
570                        af_fseek(file->fh, misc->size, SEEK_CUR);
571
572                if (misc->size % 2 != 0)
573                        af_fwrite(&padByte, 1, 1, file->fh);
574        }
575
576        return AF_SUCCEED;
577}
Note: See TracBrowser for help on using the repository browser.