summaryrefslogtreecommitdiff
path: root/cmd/ossplay/ossplay_wparser.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ossplay/ossplay_wparser.c')
-rw-r--r--cmd/ossplay/ossplay_wparser.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/cmd/ossplay/ossplay_wparser.c b/cmd/ossplay/ossplay_wparser.c
new file mode 100644
index 0000000..98cb4b5
--- /dev/null
+++ b/cmd/ossplay/ossplay_wparser.c
@@ -0,0 +1,358 @@
+/*
+ * Purpose: File header write routines for OSSRecord.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <soundcard.h>
+#include <errno.h>
+#include "ossplay_wparser.h"
+
+#pragma pack(1)
+
+typedef struct {
+ char main_chunk[4];
+ uint32 length;
+ char chunk_type[4];
+
+ char sub_chunk[4];
+ uint32 sc_len;
+ uint16 format;
+ uint16 modus;
+ uint32 sample_fq;
+ uint32 byte_p_sec;
+ uint16 block_align;
+ uint16 bit_p_spl;
+
+ char data_chunk[4];
+ uint32 data_length;
+}
+WaveHeader;
+
+typedef struct {
+ char magic[4];
+ uint32 offset;
+ uint32 filelen;
+ uint32 fmt;
+ uint32 speed;
+ uint32 channels;
+ /*
+ * Some old specs say the field below is optional. Others say that this field
+ * is mandatory, and must be at least 4 bytes long (SoX prints out a warning).
+ * We'll put a nice "Made by OSSRecord" comment and shut SoX up.
+ */
+ char comment[18];
+}
+AuHeader;
+
+typedef struct {
+ char main_chunk[4];
+ uint32 length;
+ char chunk_type[4];
+
+ char sub_chunk[4];
+ uint32 comm_len;
+ uint16 channels;
+ uint32 num_frames;
+ uint16 bits;
+ unsigned char speed[10];
+ char data_chunk[4];
+ uint32 data_length;
+ uint32 offset;
+ uint32 blocksize;
+}
+AiffHeader;
+
+typedef struct {
+ char magic[4];
+ uint16 version;
+ uint16 flags;
+
+ char desc_chunk[4];
+ uint32 desc_length_hi, desc_length_lo;
+ unsigned char speed[8];
+ char format[4];
+ uint32 format_flags;
+ uint32 bytes_per_packet;
+ uint32 frames_per_packet;
+ uint32 channels;
+ uint32 bits;
+
+ char data_chunk[4];
+ uint32 length_hi, length_lo;
+ uint32 edit_count;
+}
+CafHeader;
+
+#pragma pack()
+
+extern FILE *wave_fp;
+
+static uint32
+bswap (uint32 x)
+{
+ uint32 y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 3;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b++ = *a--;
+ *b++ = *a--;
+ *b = *a;
+
+ return y;
+}
+
+static uint16
+bswaps (uint16 x)
+{
+ uint16 y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 1;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b = *a;
+
+ return y;
+}
+
+int
+write_head (FILE * wave_fp, fctypes_t type, big_t datalimit,
+ int format, int channels, int speed)
+{
+ uint32 dl = datalimit;
+
+ if ((speed <= 0) || (channels <= 0) || (format <= 0)) return -1;
+
+ if (datalimit > U32_MAX) dl = U32_MAX;
+
+ switch (type)
+ {
+ case WAVE_FILE: {
+ WaveHeader wh;
+ int bits = format2bits (format);
+
+ if (datalimit > U32_MAX - sizeof (WaveHeader))
+ print_msg (WARNM, "Data size exceeds file format limit!\n");
+ if ((datalimit == 0) || (datalimit > U32_MAX - sizeof (WaveHeader)))
+ dl = U32_MAX - (U32_MAX % 2) - sizeof (WaveHeader);
+ switch (format) {
+ case AFMT_U8:
+ case AFMT_S16_LE:
+ case AFMT_S24_PACKED:
+ case AFMT_S32_LE:
+ wh.format = LE_SH (1);
+ break;
+ case AFMT_A_LAW:
+ wh.format = LE_SH (6);
+ break;
+ case AFMT_MU_LAW:
+ wh.format = LE_SH (7);
+ break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by WAV writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ memcpy ((char *) &wh.main_chunk, "RIFF", 4);
+ wh.length = LE_INT (dl + sizeof (WaveHeader) - 8);
+ if (dl % 2) dl--;
+ memcpy ((char *) &wh.chunk_type, "WAVE", 4);
+ memcpy ((char *) &wh.sub_chunk, "fmt ", 4);
+ wh.sc_len = LE_INT (16);
+ wh.modus = LE_SH (channels);
+ wh.sample_fq = LE_INT (speed);
+ wh.block_align = LE_SH (channels * bits / 8);
+ wh.byte_p_sec = LE_INT (speed * channels * bits / 8);
+ wh.bit_p_spl = LE_SH (bits);
+ memcpy ((char *) &wh.data_chunk, "data", 4);
+ wh.data_length = LE_INT (dl);
+ if (fwrite (&wh, sizeof (WaveHeader), 1, wave_fp) == 0) return E_ENCODE;
+ } break;
+ case AU_FILE: {
+ AuHeader ah;
+
+ if ((datalimit == 0) || (datalimit > U32_MAX)) dl = 0xffffffff;
+ memcpy ((char *) &ah.magic, ".snd", 4);
+ ah.offset = BE_INT (sizeof (AuHeader));
+ ah.filelen = BE_INT (dl);
+ switch (format)
+ {
+ case AFMT_MU_LAW: ah.fmt = BE_INT (1); break;
+ case AFMT_S8: ah.fmt = BE_INT (2); break;
+ case AFMT_S16_BE: ah.fmt = BE_INT (3); break;
+ case AFMT_S24_PACKED_BE: ah.fmt = BE_INT (4); break;
+ case AFMT_S32_BE: ah.fmt = BE_INT (5); break;
+ case AFMT_A_LAW: ah.fmt = BE_INT (27); break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by AU writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+ ah.speed = BE_INT (speed);
+ ah.channels = BE_INT (channels);
+ memcpy ((char *) &ah.comment, "Made by OSSRecord", 18);
+ if (fwrite (&ah, sizeof (AuHeader), 1, wave_fp) == 0) return -1;
+ } break;
+ case CAF_FILE: {
+ CafHeader cfh;
+ int bits = format2bits (format);
+ uint32 i;
+
+ memcpy ((char *)&cfh.magic, "caff", 4);
+ cfh.version = BE_SH(1);
+ cfh.flags = 0;
+
+ memcpy ((char *)&cfh.desc_chunk, "desc", 4);
+ cfh.desc_length_hi = 0;
+ cfh.desc_length_lo = BE_INT (32);
+ cfh.format_flags = 0;
+ switch (format) {
+ case AFMT_S16_LE:
+ case AFMT_S24_PACKED_LE:
+ case AFMT_S32_LE: cfh.format_flags = BE_INT (2);
+ case AFMT_S8:
+ case AFMT_S16_BE:
+ case AFMT_S24_PACKED_BE:
+ case AFMT_S32_BE:
+ memcpy ((char *)&cfh.format, "lpcm", 4);
+ break;
+ case AFMT_MU_LAW:
+ memcpy ((char *)&cfh.format, "ulaw", 4);
+ break;
+ case AFMT_A_LAW:
+ memcpy ((char *)&cfh.format, "alaw", 4);
+ break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by CAF writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+ cfh.frames_per_packet = BE_INT (1);
+ cfh.bytes_per_packet = BE_INT (bits / 8 * channels);
+ cfh.channels = BE_INT (channels);
+ cfh.bits = BE_INT (bits);
+
+ /* The method used here is good enough for sane values */
+ memset (cfh.speed, 0, sizeof (cfh.speed));
+ i = 0;
+ while ((1L << (i + 1)) <= speed) i++;
+ cfh.speed[0] = 64; cfh.speed[1] = (i-1) << 4;
+ i = (speed - (1 << i)) << (32-i);
+ cfh.speed[5] = i & 240;
+ cfh.speed[4] = (i >> 4) & 255;
+ cfh.speed[3] = (i >> 12) & 255;
+ cfh.speed[2] = (i >> 20) & 255;
+ cfh.speed[1] |= (i >> 28) & 15;
+
+ memcpy ((char *)&cfh.data_chunk, "data", 4);
+ if ((datalimit == 0) || (datalimit > U32_MAX)) {
+ cfh.length_lo = 0xffffffff;
+ cfh.length_hi = 0xffffffff;
+ } else {
+ cfh.length_lo = BE_INT (datalimit);
+ cfh.length_hi = 0;
+ }
+ cfh.edit_count = 0;
+ if (fwrite (&cfh, sizeof (CafHeader), 1, wave_fp) == 0)
+ return E_ENCODE;
+ } break;
+ case AIFF_FILE: {
+ AiffHeader afh;
+ int bits = format2bits (format);
+ uint32 i;
+
+ if (datalimit > U32_MAX - sizeof (AiffHeader))
+ print_msg (WARNM, "Data size exceeds file format limit!\n");
+ if ((datalimit == 0) || (datalimit > U32_MAX - sizeof (AiffHeader)))
+ dl = U32_MAX - (U32_MAX % 2) - sizeof (AiffHeader);
+ memcpy ((char *) &afh.main_chunk, "FORM", 4);
+ afh.length = BE_INT (dl + sizeof (AiffHeader) - 8);
+ if (dl % 2) dl--;
+ memcpy ((char *) &afh.chunk_type, "AIFF", 4);
+ memcpy ((char *) &afh.sub_chunk, "COMM", 4);
+ afh.comm_len = BE_INT (18);
+ afh.channels = BE_SH (channels);
+ afh.num_frames = BE_INT (dl / bits);
+ afh.bits = BE_SH (bits);
+ switch (format)
+ {
+ case AFMT_S8:
+ case AFMT_S16_BE:
+ case AFMT_S24_PACKED_BE:
+ case AFMT_S32_BE:
+ break;
+ default:
+ print_msg (ERRORM,
+ "%s sample format not supported by AIFF writer!\n",
+ sample_format_name (format));
+ return E_FORMAT_UNSUPPORTED;
+ }
+
+ /* The method used here is good enough for sane values */
+ memset (afh.speed, 0, sizeof (afh.speed));
+ i = 0;
+ while ((1L << (i + 1)) <= speed) i++;
+ afh.speed[0] = 64; afh.speed[1] = i-1;
+ i = (speed - (1 << i)) << (31-i);
+ afh.speed[5] = i & 255;
+ afh.speed[4] = (i >> 8) & 255;
+ afh.speed[3] = (i >> 16) & 255;
+ afh.speed[2] = ((i >> 24) & 127) + 128;
+
+ memcpy ((char *) &afh.data_chunk, "SSND", 4);
+ afh.data_length = BE_INT (dl);
+ afh.offset = BE_SH (0);
+ afh.blocksize = BE_SH (0);
+ if (fwrite (&afh, sizeof (AiffHeader), 1, wave_fp) == 0)
+ return E_ENCODE;
+ } break;
+ case RAW_FILE:
+ default: return 0;
+ }
+ return 0;
+}
+
+int
+finalize_head (FILE * wave_fp, fctypes_t type, big_t datalimit,
+ int format, int channels, int speed)
+{
+ if ((IS_IFF_FILE (type)) && (datalimit % 2))
+ {
+ /*
+ * All chunks must have an even length in an IFF file,
+ * so we have to add a pad byte in this case.
+ * Since we always write the data chunk last, we can
+ * just append it to end of file.
+ */
+ char flag = 0;
+
+ fseek (wave_fp, 0, SEEK_END);
+ if (fwrite (&flag , 1, 1, wave_fp) == 0)
+ print_msg (WARNM, "Couldn't add padding byte to SSND chunk!\n");
+ }
+ if ((type != RAW_FILE) && (fseek (wave_fp, 0, SEEK_SET) == -1)) return E_ENCODE;
+ write_head (wave_fp, type, datalimit, format, channels, speed);
+ return 0;
+}