diff options
Diffstat (limited to 'cmd/ossplay/ossplay_wparser.c')
-rw-r--r-- | cmd/ossplay/ossplay_wparser.c | 358 |
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; +} |