1 /*
2
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19 // QUIKWRIT.C QuickTime file writing
20 // Rex E. Bradford
21
22 /*
23 * $Source: r:/prj/lib/src/afile/RCS/quikwrit.c $
24 * $Revision: 1.6 $
25 * $Author: rex $
26 * $Date: 1994/10/18 16:01:53 $
27 * $Log: quikwrit.c $
28 * Revision 1.6 1994/10/18 16:01:53 rex
29 * Got 16-bit depth working
30 *
31 * Revision 1.5 1994/10/04 20:42:01 rex
32 * Fixed bugs related to variable-timed frames, put in basic support
33 * for 16-bit depth frames, but doesn't work because rgb bitorder unknown
34 *
35 * Revision 1.4 1994/10/03 19:43:38 rex
36 * Modified to respect varying frame rate
37 *
38 * Revision 1.3 1994/10/03 18:05:20 rex
39 * Modified QuikAddVideoSample() to handle bitmaps where w != row
40 *
41 * Revision 1.2 1994/09/30 17:00:18 rex
42 * Made it actually work
43 *
44 * Revision 1.1 1994/09/29 10:36:52 rex
45 * Initial revision
46 *
47 *
48 */
49
50 #include <ctype.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54
55 #include "lg.h"
56 #include "quiktime.h"
57
58 // Handy macros
59
60 #define FIX8(v) ((v) >> 8)
61 #define DEFAULT_MACTIME 2855653576
62 #define DEFAULT_TIMESCALE 600
63 #define FRAME_ALLOC_INCR 128
64 #define MDHD_TIMESCALE 30
65
66 // Internal prototypes
67
68 void QuikStartSubChunk(QTM *pqtm, FILE *fp, QT_Ctype ctype, void *data, long len);
69 void QuikEndSubChunk(QTM *pqtm, FILE *fp);
70 void QuikWriteMVHD(QTM *pqtm, FILE *fp, ulong timeTot);
71 void QuikWriteVideoTrack(QTM *pqtm, FILE *fp, ulong timeTot);
72 void WriteChunkWithString(FILE *fp, QT_Ctype ctype, void *data, long len, char *str);
73 ulong QuikComputeTotalTime(QTM *pqtm);
74 long QuikSampleLenMsec(fix t, fix tlast);
75 fix QuikSampleLenMsecBackToFix(long msec);
76
77 // -------------------------------------------------------------
78 // MOVIE WRITING
79 // -------------------------------------------------------------
80 //
81 // QuikCreateMovie() creates a quiktime movie ready for writing.
82
QuikCreateMovie(QTM * pqtm,FILE * fp)83 void QuikCreateMovie(QTM *pqtm, FILE *fp) {
84 memset(pqtm, 0, sizeof(QTM));
85
86 QuikStartSubChunk(pqtm, fp, QT_MDAT, NULL, 0);
87 }
88 /*
89 // -------------------------------------------------------------
90 //
91 // QuikSetPal() sets the video track's palette
92
93 void QuikSetPal(QTM *pqtm, uchar *pal)
94 {
95 if (pqtm->pVideoTrack == NULL)
96 pqtm->pVideoTrack = &pqtm->track[pqtm->numTracks++];
97
98 pqtm->pVideoTrack->palette = Malloc(768);
99 memcpy(pqtm->pVideoTrack->palette, pal, 768);
100 }
101 */
102 // -------------------------------------------------------------
103 //
104 // QuikAddVideoSample() adds a video sample
105
QuikAddVideoSample(QTM * pqtm,FILE * fp,grs_bitmap * pbm,fix time)106 void QuikAddVideoSample(QTM *pqtm, FILE *fp, grs_bitmap *pbm, fix time) {
107 MovieTrack *ptrack;
108 uchar *p;
109 uchar *ppal;
110 uchar *pbuff;
111 uchar *pd;
112 uchar *prgb;
113 int x, y;
114 ushort color16;
115
116 // Can't handle anything but 8-bit raw
117
118 if (pbm->type != BMT_FLAT8) {
119 printf("QuikAddVideoSample: only uncompressed bitmaps allowed\n");
120 return;
121 }
122
123 // Write out frame bits in 8-bit of 16-bit mode
124
125 if (pqtm->depth16) {
126 if ((pqtm->pVideoTrack == NULL) || (pqtm->pVideoTrack->palette == NULL)) {
127 printf("QuikAddVideoSample: need pal to convert to 16-bits!\n");
128 return;
129 }
130 ppal = pqtm->pVideoTrack->palette;
131 pbuff = (uchar *)malloc(pbm->w * sizeof(ushort));
132 for (y = 0, p = pbm->bits; y < pbm->h; y++, p += (pbm->row - pbm->w)) {
133 for (x = 0, pd = pbuff; x < pbm->w; x++) {
134 prgb = &ppal[((ushort)*p++) * 3];
135 color16 = (ushort)(*prgb++ >> 3) << 10;
136 color16 |= (ushort)(*prgb++ >> 3) << 5;
137 color16 |= (ushort)(*prgb++ >> 3);
138 *pd++ = color16 >> 8;
139 *pd++ = color16 & 0xFF;
140 }
141 fwrite(pbuff, pbm->w * sizeof(ushort), 1, fp);
142 }
143 free(pbuff);
144 } else {
145 if (pbm->w == pbm->row)
146 fwrite(pbm->bits, (long)pbm->w * (long)pbm->h, 1, fp);
147 else {
148 for (y = 0, p = pbm->bits; y < pbm->h; y++, p += pbm->row)
149 fwrite(p, pbm->w, 1, fp);
150 }
151 }
152
153 // If video track not assigned, assign it
154
155 if (pqtm->pVideoTrack == NULL)
156 pqtm->pVideoTrack = &pqtm->track[pqtm->numTracks++];
157
158 ptrack = pqtm->pVideoTrack;
159
160 // If video track not set up, set it up
161
162 if (pqtm->compTypeQT == 0) {
163 pqtm->compTypeQT = MAKE4('r', 'a', 'w', ' ');
164 pqtm->frameWidth = pbm->w;
165 pqtm->frameHeight = pbm->h;
166
167 ptrack->qt_tkhd.flags[2] = 0x0F;
168 ptrack->qt_tkhd.createTime = DEFAULT_MACTIME;
169 ptrack->qt_tkhd.modTime = DEFAULT_MACTIME;
170 ptrack->qt_tkhd.trackId = (ptrack - &pqtm->track[0]) + 1;
171 ptrack->qt_tkhd.matrix[0] = FIX_UNIT;
172 ptrack->qt_tkhd.matrix[4] = FIX_UNIT;
173 ptrack->qt_tkhd.matrix[8] = fix_make(16384, 0);
174 ptrack->qt_tkhd.trackWidth = fix_make(pbm->w, 0);
175 ptrack->qt_tkhd.trackHeight = fix_make(pbm->h, 0);
176
177 ptrack->qt_mdhd.createTime = DEFAULT_MACTIME;
178 ptrack->qt_mdhd.modTime = DEFAULT_MACTIME;
179
180 ptrack->qt_stsd.base.numEntries = 1;
181 if (pqtm->depth16)
182 ptrack->qt_stsd.idesc.descSize = 86;
183 else
184 ptrack->qt_stsd.idesc.descSize = 2142;
185 ptrack->qt_stsd.idesc.dataFormat = MAKE4('r', 'a', 'w', ' ');
186 ptrack->qt_stsd.idesc.dataRefIndex = 1;
187 ptrack->qt_stsd.idesc.version = 1;
188 ptrack->qt_stsd.idesc.revLevel = 1;
189 ptrack->qt_stsd.idesc.vendor = MAKE4('a', 'p', 'p', 'l');
190 ptrack->qt_stsd.idesc.temporalQuality = 0;
191 ptrack->qt_stsd.idesc.spatialQuality = fix_make(0, 65536 / 50);
192 ptrack->qt_stsd.idesc.width = pbm->w;
193 ptrack->qt_stsd.idesc.height = pbm->h;
194 ptrack->qt_stsd.idesc.hRes = fix_make(72, 0);
195 ptrack->qt_stsd.idesc.vRes = fix_make(72, 0);
196 ptrack->qt_stsd.idesc.frameCount = 1;
197 strcpy((char *)ptrack->qt_stsd.idesc.name, (char *)"None");
198 if (pqtm->depth16) {
199 ptrack->qt_stsd.idesc.depth = 16;
200 ptrack->qt_stsd.idesc.clutId = -1;
201 } else {
202 ptrack->qt_stsd.idesc.depth = 8;
203 if (ptrack->palette)
204 ptrack->qt_stsd.idesc.clutId = 0;
205 else
206 ptrack->qt_stsd.idesc.clutId = 8;
207 }
208
209 ptrack->numSamplesAlloced = FRAME_ALLOC_INCR;
210 ptrack->pSampleTime = (fix *)malloc(ptrack->numSamplesAlloced * sizeof(fix));
211 }
212
213 // Grow sample if need to
214
215 ptrack->pSampleTime[ptrack->numSamps++] = time;
216 if (ptrack->numSamps >= ptrack->numSamplesAlloced) {
217 ptrack->numSamplesAlloced += FRAME_ALLOC_INCR;
218 ptrack->pSampleTime = (fix *)realloc(ptrack->pSampleTime, ptrack->numSamplesAlloced * sizeof(fix));
219 }
220 }
221
222 // -------------------------------------------------------------
223 //
224 // QuikWriteMovieAndClose() writes out movie and closes file
225
QuikWriteMovieAndClose(QTM * pqtm,FILE * fp)226 void QuikWriteMovieAndClose(QTM *pqtm, FILE *fp) {
227 ulong timeTot;
228
229 // Close 'mdat' chunk
230
231 QuikEndSubChunk(pqtm, fp);
232
233 // Write 'moov' chunk, which has everything else
234
235 QuikStartSubChunk(pqtm, fp, QT_MOOV, NULL, 0);
236 timeTot = QuikComputeTotalTime(pqtm);
237 QuikWriteMVHD(pqtm, fp, timeTot);
238 QuikWriteVideoTrack(pqtm, fp, timeTot);
239
240 // Unpop stacked chunk lengths
241
242 while (pqtm->indexOffsetStack > 0)
243 QuikEndSubChunk(pqtm, fp);
244
245 // Close file and clean up
246
247 fclose(fp);
248 QuikFreeMovie(pqtm);
249 memset(pqtm, 0, sizeof(QTM));
250 }
251
252 // -------------------------------------------------------------
253 // INTERNAL ROUTINES
254 // -------------------------------------------------------------
255 //
256 // QuikStartSubChunk() starts a new subchunk.
257
QuikStartSubChunk(QTM * pqtm,FILE * fp,QT_Ctype ctype,void * data,long len)258 void QuikStartSubChunk(QTM *pqtm, FILE *fp, QT_Ctype ctype, void *data, long len) {
259 if (pqtm->indexOffsetStack >= QTM_MAX_CHUNK_NESTING) {
260 printf("QuikStartSubChunk: nesting too deep\n");
261 return;
262 }
263
264 pqtm->offsetStack[pqtm->indexOffsetStack++] = ftell(fp);
265
266 QuikWriteChunk(fp, ctype, data, len);
267 }
268
269 // -------------------------------------------------------------
270 //
271 // QuikEndSubChunk() ends a subchunk.
272
QuikEndSubChunk(QTM * pqtm,FILE * fp)273 void QuikEndSubChunk(QTM *pqtm, FILE *fp) {
274 long currPos, length;
275
276 if (pqtm->indexOffsetStack == 0) {
277 printf("QuikEndSubChunk: chunk nesting underflow\n");
278 return;
279 }
280
281 pqtm->indexOffsetStack--;
282
283 currPos = ftell(fp);
284 fseek(fp, pqtm->offsetStack[pqtm->indexOffsetStack], SEEK_SET);
285 length = currPos - ftell(fp);
286 QuikWriteChunkLength(fp, length);
287 fseek(fp, currPos, SEEK_SET);
288 }
289
290 // -------------------------------------------------------------
291 //
292 // QuikWriteMVHD() writes MVHD chunk
293
QuikWriteMVHD(QTM * pqtm,FILE * fp,ulong timeTot)294 void QuikWriteMVHD(QTM *pqtm, FILE *fp, ulong timeTot) {
295 pqtm->qt_mvhd.createTime = DEFAULT_MACTIME;
296 pqtm->qt_mvhd.modTime = DEFAULT_MACTIME;
297 pqtm->qt_mvhd.timeScale = DEFAULT_TIMESCALE;
298 if (pqtm->pVideoTrack)
299 pqtm->qt_mvhd.duration = timeTot;
300 pqtm->qt_mvhd.preferredRate = FIX_UNIT;
301 pqtm->qt_mvhd.preferredVol = FIX8(FIX_UNIT);
302 pqtm->qt_mvhd.matrix[0] = FIX_UNIT;
303 pqtm->qt_mvhd.matrix[4] = FIX_UNIT;
304 pqtm->qt_mvhd.matrix[8] = fix_make(16384, 0);
305 pqtm->qt_mvhd.nextTrackId = 1 + (pqtm->pVideoTrack != NULL) + (pqtm->pAudioTrack != NULL);
306
307 QuikWriteChunk(fp, QT_MVHD, &pqtm->qt_mvhd, sizeof(pqtm->qt_mvhd));
308 }
309
310 // -------------------------------------------------------------
311 //
312 // QuikWriteVideoTrack() writes video track out.
313
QuikWriteVideoTrack(QTM * pqtm,FILE * fp,ulong timeTot)314 void QuikWriteVideoTrack(QTM *pqtm, FILE *fp, ulong timeTot) {
315 MovieTrack *ptrack;
316 uchar *pStsd;
317 int i, ilast;
318 ushort *ppw;
319 uchar *ppr;
320 ushort r, g, b;
321 long len;
322 ulong offset;
323 long tlen, tlenLast;
324 fix tlast;
325 long sampSize;
326 QTS_ELST elst;
327 QTS_HDLR hdlr;
328 QTS_VMHD vmhd;
329 uchar dref[20];
330
331 // If no video track, don't write it obviously
332
333 ptrack = pqtm->pVideoTrack;
334 if (ptrack == NULL)
335 return;
336
337 // Write TRAK chunk hdr and TKHD chunk
338
339 QuikStartSubChunk(pqtm, fp, QT_TRAK, NULL, 0);
340 ptrack->qt_tkhd.duration = timeTot;
341 QuikWriteChunk(fp, QT_TKHD, &ptrack->qt_tkhd, sizeof(QTS_TKHD));
342
343 // Write EDTS chunk hdr and ELST chunk
344
345 QuikStartSubChunk(pqtm, fp, QT_EDTS, NULL, 0);
346 memset(&elst, 0, sizeof(elst));
347 elst.numEntries = 1;
348 elst.editList[0].trackDuration = timeTot;
349 elst.editList[0].mediaRate = FIX_UNIT;
350 QuikStartSubChunk(pqtm, fp, QT_ELST, &elst, sizeof(elst));
351 QuikEndSubChunk(pqtm, fp);
352 QuikEndSubChunk(pqtm, fp);
353
354 // Write MDIA chunk hdr and MDHD chunk
355
356 QuikStartSubChunk(pqtm, fp, QT_MDIA, NULL, 0);
357 ptrack->qt_mdhd.timeScale = MDHD_TIMESCALE;
358 ptrack->qt_mdhd.duration = (timeTot * MDHD_TIMESCALE) / DEFAULT_TIMESCALE;
359 QuikWriteChunk(fp, QT_MDHD, &ptrack->qt_mdhd, sizeof(QTS_MDHD));
360
361 // Write HDLR chunk
362
363 memset(&hdlr, 0, sizeof(hdlr));
364 hdlr.compType = MAKE4('m', 'h', 'l', 'r');
365 hdlr.compSubtype = MAKE4('v', 'i', 'd', 'e');
366 hdlr.compManufacturer = MAKE4('a', 'p', 'p', 'l');
367 hdlr.compFlags = 0x40000000;
368 if (pqtm->depth16)
369 hdlr.compFlagsMask = 0x20072;
370 else
371 hdlr.compFlagsMask = 0x10011;
372 WriteChunkWithString(fp, QT_HDLR, &hdlr, sizeof(hdlr), "Apple Video Media Handler");
373
374 // Write MINF chunk hdr and VMHD chunk
375
376 QuikStartSubChunk(pqtm, fp, QT_MINF, NULL, 0);
377 memset(&vmhd, 0, sizeof(vmhd));
378 vmhd.flags[2] = 1;
379 vmhd.graphicsMode = 0x40;
380 vmhd.opColor[0] = 0x8000;
381 vmhd.opColor[1] = 0x8000;
382 vmhd.opColor[2] = 0x8000;
383 QuikWriteChunk(fp, QT_VMHD, &vmhd, sizeof(vmhd));
384
385 // Write HDLR chunk
386
387 memset(&hdlr, 0, sizeof(hdlr));
388 hdlr.compType = MAKE4('d', 'h', 'l', 'r');
389 hdlr.compSubtype = MAKE4('a', 'l', 'i', 's');
390 hdlr.compManufacturer = MAKE4('a', 'p', 'p', 'l');
391 if (pqtm->depth16) {
392 hdlr.compFlags = 0xC0000000;
393 hdlr.compFlagsMask = 0x1005D;
394 } else {
395 hdlr.compFlags = 0x40000000;
396 hdlr.compFlagsMask = 0x10016;
397 }
398 WriteChunkWithString(fp, QT_HDLR, &hdlr, sizeof(hdlr), "Apple Alias Data Handler");
399
400 // Write DINF chunk hdr and DREF chunk
401
402 QuikStartSubChunk(pqtm, fp, QT_DINF, NULL, 0);
403 memset(dref, 0, sizeof(dref));
404 dref[7] = 1;
405 dref[11] = 0x0C;
406 dref[12] = 'a';
407 dref[13] = 'l';
408 dref[14] = 'i';
409 dref[15] = 's';
410 dref[19] = 1;
411 QuikWriteChunk(fp, QT_DREF, dref, sizeof(dref));
412 QuikEndSubChunk(pqtm, fp);
413
414 // Write STBL chunk hdr and STSD chunk
415
416 QuikStartSubChunk(pqtm, fp, QT_STBL, NULL, 0);
417 pStsd = (uchar *)malloc(sizeof(ptrack->qt_stsd) + 0x0800 + 8);
418 memcpy(pStsd, &ptrack->qt_stsd, sizeof(ptrack->qt_stsd));
419 if (ptrack->palette && !pqtm->depth16) {
420 ppw = (ushort *)(pStsd + sizeof(QTS_STSD_Base) + sizeof(QT_ImageDesc) + 8);
421 *(ppw - 1) = 0xFF00;
422 ppr = ptrack->palette;
423 for (i = 0; i < 256; i++) {
424 *ppw++ = i << 8;
425 r = *ppr++;
426 *ppw++ = (r << 8) | r;
427 g = *ppr++;
428 *ppw++ = (g << 8) | g;
429 b = *ppr++;
430 *ppw++ = (b << 8) | b;
431 }
432 }
433 QuikWriteChunk(fp, QT_STSD, pStsd, sizeof(ptrack->qt_stsd) + 0x0800 + 8);
434 free(pStsd);
435
436 // Write STTS chunk
437
438 ptrack->qt_stts = (QTS_STTS *)malloc(sizeof(QTS_STTS) + (sizeof(QT_Time2Samp) * ptrack->numSamps));
439 tlenLast = 0;
440 tlast = 0;
441 ilast = 1;
442 for (i = 1; i < ptrack->numSamps; i++) {
443 tlen = QuikSampleLenMsec(ptrack->pSampleTime[i], tlast);
444 if (tlen != tlenLast) {
445 if (i != 1) {
446 ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].count = i - ilast;
447 ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].duration = tlenLast;
448 ptrack->qt_stts->numEntries++;
449 ilast = i;
450 }
451 tlenLast = tlen;
452 }
453 tlast += QuikSampleLenMsecBackToFix(tlen);
454 }
455 ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].count = (i - ilast) + 1;
456 ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].duration = tlen;
457 ptrack->qt_stts->numEntries++;
458 QuikWriteChunk(fp, QT_STTS, ptrack->qt_stts,
459 sizeof(QTS_STTS) + (sizeof(QT_Time2Samp) * (ptrack->qt_stts->numEntries - 1)));
460
461 // Write STSC chunk
462
463 len = sizeof(QTS_STSC);
464 ptrack->qt_stsc = (QTS_STSC *)malloc(len);
465 ptrack->qt_stsc->numEntries = 1;
466 ptrack->qt_stsc->samp2chunk[0].firstChunk = 1;
467 ptrack->qt_stsc->samp2chunk[0].sampsPerChunk = 1;
468 ptrack->qt_stsc->samp2chunk[0].sampDescId = 1;
469 QuikWriteChunk(fp, QT_STSC, ptrack->qt_stsc, len);
470
471 // Write STSZ chunk
472
473 len = sizeof(QTS_STSZ) - sizeof(ulong); // don't include sampSizeTab[]
474 ptrack->qt_stsz = (QTS_STSZ *)malloc(len);
475 sampSize = (long)pqtm->frameWidth * (long)pqtm->frameHeight;
476 if (pqtm->depth16)
477 sampSize *= 2;
478 ptrack->qt_stsz->sampSize = sampSize;
479 ptrack->qt_stsz->numEntries = ptrack->numSamps;
480 QuikWriteChunk(fp, QT_STSZ, ptrack->qt_stsz, len);
481
482 // Write STCO chunk
483
484 len = sizeof(QTS_STCO) + ((ptrack->numSamps - 1) * sizeof(ulong));
485 ptrack->qt_stco = (QTS_STCO *)malloc(len);
486 ptrack->qt_stco->numEntries = ptrack->numSamps;
487 offset = sizeof(QT_ChunkHdr);
488 for (i = 0; i < ptrack->numSamps; i++) {
489 ptrack->qt_stco->offset[i] = offset;
490 offset += sampSize;
491 }
492 QuikWriteChunk(fp, QT_STCO, ptrack->qt_stco, len);
493 }
494
495 // -------------------------------------------------------------
496 //
497 // SetPascalString() puts C string into PStr field.
498
SetPascalString(PStr * pstr,char * str)499 static void SetPascalString(PStr *pstr, char *str) {
500 pstr->len = strlen(str);
501 memcpy(pstr->str, str, pstr->len);
502 }
503
504 // -------------------------------------------------------------
505 //
506 // WriteChunkWithString() writes a var-length data chunk with trailing PStr.
507
WriteChunkWithString(FILE * fp,QT_Ctype ctype,void * data,long len,char * str)508 static void WriteChunkWithString(FILE *fp, QT_Ctype ctype, void *data, long len, char *str) {
509 char buff[128];
510
511 memcpy(buff, data, len - sizeof(PStr));
512 SetPascalString((PStr *)(buff + len - sizeof(PStr)), str);
513 QuikWriteChunk(fp, ctype, buff, (len - sizeof(PStr)) + strlen(str) + 1);
514 }
515
516 // ---------------------------------------------------------------
517 //
518 // QuikComputeTotalTime() computes total time of movie.
519
QuikComputeTotalTime(QTM * pqtm)520 ulong QuikComputeTotalTime(QTM *pqtm) {
521 MovieTrack *ptrack;
522 fix fixtime;
523
524 ptrack = pqtm->pVideoTrack;
525 if ((ptrack == NULL) || (ptrack->pSampleTime == NULL))
526 return (0);
527
528 fixtime = ptrack->pSampleTime[ptrack->numSamps - 1] +
529 (ptrack->pSampleTime[ptrack->numSamps - 1] - ptrack->pSampleTime[ptrack->numSamps - 2]);
530 return (fix_mul(fixtime, DEFAULT_TIMESCALE));
531 }
532
533 // ---------------------------------------------------------------
534 //
535 // QuikSampleLenMsec() returns sample length in msecs.
536
QuikSampleLenMsec(fix t,fix tlast)537 long QuikSampleLenMsec(fix t, fix tlast) {
538 fix fixtime;
539 long msec;
540
541 fixtime = t - tlast;
542 #if MDHD_TIMESCALE == 1000
543 msec = ((fixtime * 125) + 0x1000) >> 13; // overflows at about 8 minutes
544 #elif MDHD_TIMESCALE == 100
545 msec = ((fixtime * 25) + 0x2000) >> 14;
546 #elif MDHD_TIMESCALE == 30
547 msec = ((fixtime * 15) + 0x4000) >> 15;
548 #else
549 msec = 0;
550 Warning(("QuikSampleLenMsec: invalid MDHD_TIMESCALE\n"));
551 #endif
552 return (msec);
553 }
554
555 // --------------------------------------------------------------
556 //
557 // QuikSampleLenMsecBackToFix() converts msec to fix
558
QuikSampleLenMsecBackToFix(long msec)559 fix QuikSampleLenMsecBackToFix(long msec) {
560 fix tfix;
561
562 #if MDHD_TIMESCALE == 1000
563 tfix = (msec << 13) / 125;
564 #elif MDHD_TIMESCALE == 100
565 tfix = (msec << 14) / 25;
566 #elif MDHD_TIMESCALE == 30
567 tfix = (msec << 15) / 15;
568 #else
569 tfix = 0;
570 Warning(("QuikSampleLenMsecBackToFix: invalid MDHD_TIMESCALE\n"));
571 #endif
572 return (tfix);
573 }
574