946 lines
22 KiB
C
946 lines
22 KiB
C
/*
|
|
* vplay.c
|
|
*
|
|
* Copyright (C) 2009-2013 by Digi International Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
* the Free Software Foundation.
|
|
*
|
|
* Description: play and record sound files
|
|
*
|
|
* Just cosmetic changes of vplay.c from OSS project:
|
|
*
|
|
* <http://www.opensound.com/oss.html>
|
|
* <http://mercurial.opensound.com/>
|
|
*
|
|
* with following original notice:
|
|
*
|
|
* vplay.c - plays and records
|
|
* CREATIVE LABS VOICE-files, Microsoft WAVE-files and raw data
|
|
*
|
|
* Autor: Michael Beck - beck@informatik.hu-berlin.de
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/soundcard.h>
|
|
#include "fmtheaders.h"
|
|
#include <netinet/in.h>
|
|
|
|
#define DEFAULT_DSP_SPEED 8000
|
|
|
|
#define RECORD 0
|
|
#define PLAY 1
|
|
#define AUDIO "/dev/dsp"
|
|
|
|
#define min(a,b) ((a) <= (b) ? (a) : (b))
|
|
#define d_printf(x) if (verbose_mode) fprintf x
|
|
|
|
#define VOC_FMT 0
|
|
#define WAVE_FMT 1
|
|
#define RAW_DATA 2
|
|
#define SND_FMT 3
|
|
|
|
/* global data */
|
|
int timelimit = 0, dsp_speed = DEFAULT_DSP_SPEED, dsp_stereo = 0;
|
|
int samplesize = 8;
|
|
int quiet_mode = 0;
|
|
int verbose_mode = 0;
|
|
int convert = 0;
|
|
int record_type = VOC_FMT;
|
|
u_long count;
|
|
int audio, abuf_size, zbuf_size;
|
|
int direction, omode;
|
|
u_char *audiobuf, *zerobuf;
|
|
char *command;
|
|
int vocminor, vocmajor; /* .VOC version */
|
|
|
|
/* defaults for playing raw data */
|
|
struct {
|
|
int timelimit, dsp_speed, dsp_stereo, samplesize;
|
|
} raw_info = {
|
|
0, DEFAULT_DSP_SPEED, 0, 8
|
|
};
|
|
|
|
/* needed prototypes */
|
|
void record_play(char *name);
|
|
void start_voc(int fd, u_long count);
|
|
void start_wave(int fd, u_long count);
|
|
void start_snd(int fd, u_long count);
|
|
void end_voc(int fd);
|
|
void end_wave_raw(int fd);
|
|
void end_snd(int fd);
|
|
u_long calc_count();
|
|
|
|
struct fmt_record {
|
|
void (*start) (int fd, u_long count);
|
|
void (*end) (int fd);
|
|
char *what;
|
|
} fmt_rec_table[] = {
|
|
{start_voc, end_voc, "VOC"},
|
|
{start_wave, end_wave_raw, "WAVE"},
|
|
{NULL, end_wave_raw, "raw data"},
|
|
{start_snd, end_snd, "SND"}
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int c, n = 0;
|
|
|
|
command = argv[0];
|
|
if (strstr(argv[0], "vrec")) {
|
|
direction = RECORD;
|
|
omode = O_RDONLY;
|
|
} else if (strstr(argv[0], "vplay")) {
|
|
direction = PLAY;
|
|
omode = O_WRONLY;
|
|
} else {
|
|
fprintf(stderr, "Error: command should be named either vrec or vplay\n");
|
|
exit(1);
|
|
}
|
|
|
|
while ((c = getopt(argc, argv, "aqs:St:b:vrwd")) != EOF)
|
|
switch (c) {
|
|
case 'S':
|
|
dsp_stereo = raw_info.dsp_stereo = 1;
|
|
break;
|
|
case 'q':
|
|
quiet_mode = 1;
|
|
break;
|
|
case 'r':
|
|
record_type = RAW_DATA;
|
|
break;
|
|
case 'v':
|
|
record_type = VOC_FMT;
|
|
break;
|
|
case 'w':
|
|
record_type = WAVE_FMT;
|
|
break;
|
|
case 'a':
|
|
record_type = SND_FMT;
|
|
break;
|
|
case 's':
|
|
dsp_speed = atoi(optarg);
|
|
if (dsp_speed < 300)
|
|
dsp_speed *= 1000;
|
|
raw_info.dsp_speed = dsp_speed;
|
|
break;
|
|
case 't':
|
|
timelimit = raw_info.timelimit = atoi(optarg);
|
|
break;
|
|
case 'b':
|
|
samplesize = raw_info.samplesize = atoi(optarg);
|
|
break;
|
|
case 'd':
|
|
verbose_mode = 1;
|
|
quiet_mode = 0;
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"Usage: %s [-qvwraS] [-t secs] [-s Hz] [-b 8|12|16] [filename]\n",
|
|
command);
|
|
exit(-1);
|
|
}
|
|
|
|
audio = open(AUDIO, omode, 0);
|
|
if (audio == -1) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
|
|
abuf_size = -1;
|
|
ioctl(audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size);
|
|
if (abuf_size < 4 || abuf_size > 65536) {
|
|
if (abuf_size == -1) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
abuf_size = 1024;
|
|
}
|
|
|
|
zbuf_size = 256;
|
|
if ((audiobuf = (u_char *) malloc(abuf_size)) == NULL ||
|
|
(zerobuf = (u_char *) malloc(zbuf_size)) == NULL) {
|
|
fprintf(stderr, "%s: unable to allocate input/output buffer\n", command);
|
|
exit(-1);
|
|
}
|
|
memset((char *)zerobuf, 128, zbuf_size);
|
|
|
|
if (optind > argc - 1)
|
|
record_play(NULL);
|
|
else
|
|
while (optind <= argc - 1) {
|
|
|
|
if (n++ > 0)
|
|
if (ioctl(audio, SNDCTL_DSP_SYNC, NULL) < 0) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
|
|
record_play(argv[optind++]);
|
|
}
|
|
close(audio);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function: test_vocfile
|
|
* Description: test, if it is a .VOC file
|
|
* Return: >= 0 if ok (this is the length of rest)
|
|
* < 0 if not
|
|
*/
|
|
int test_vocfile(void *buffer)
|
|
{
|
|
VocHeader *vp = buffer;
|
|
if (!strcmp((const char *)vp->magic, MAGIC_STRING)) {
|
|
vocminor = vp->version & 0xFF;
|
|
vocmajor = vp->version / 256;
|
|
if (vp->version != (0x1233 - vp->coded_ver))
|
|
return -2; /* coded version mismatch */
|
|
return vp->headerlen - sizeof(VocHeader); /* 0 mostly */
|
|
}
|
|
return -1; /* magic string fail */
|
|
}
|
|
|
|
/*
|
|
* Function: test_wavefile
|
|
* Description: test, if it is a .WAV file
|
|
* Return: 0 if ok (and set the speed, stereo etc.)
|
|
* < 0 if not
|
|
*/
|
|
int test_wavefile(void *buffer)
|
|
{
|
|
WaveHeader *wp = buffer;
|
|
if (wp->main_chunk == RIFF && wp->chunk_type == WAVE &&
|
|
wp->sub_chunk == FMT && wp->data_chunk == DATA) {
|
|
if (wp->format != PCM_CODE) {
|
|
fprintf(stderr, "%s: can't play not PCM-coded WAVE-files\n", command);
|
|
exit(-1);
|
|
}
|
|
if (wp->modus > 2) {
|
|
fprintf(stderr,
|
|
"%s: can't play WAVE-files with %d tracks\n",
|
|
command, wp->modus);
|
|
exit(-1);
|
|
}
|
|
dsp_stereo = (wp->modus == WAVE_STEREO) ? 1 : 0;
|
|
samplesize = wp->bit_p_spl;
|
|
dsp_speed = wp->sample_fq;
|
|
count = wp->data_length;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Function: test_sndfile
|
|
* Description: test, if it is a .SND file
|
|
* Return: 0 if ok (and set the speed, stereo etc.)
|
|
* < 0 if not
|
|
*/
|
|
int test_sndfile(void *buffer, int fd)
|
|
{
|
|
long infolen;
|
|
char *info;
|
|
SndHeader *snd = buffer;
|
|
|
|
if (snd->magic == SND_MAGIC)
|
|
convert = 0;
|
|
else {
|
|
if (htonl(snd->magic) == SND_MAGIC) {
|
|
convert = 1;
|
|
snd->dataLocation = htonl(snd->dataLocation);
|
|
snd->dataSize = htonl(snd->dataSize);
|
|
snd->dataFormat = htonl(snd->dataFormat);
|
|
snd->samplingRate = htonl(snd->samplingRate);
|
|
snd->channelCount = htonl(snd->channelCount);
|
|
} else {
|
|
/* No SoundFile */
|
|
return (-1);
|
|
}
|
|
}
|
|
switch (snd->dataFormat) {
|
|
case SND_FORMAT_LINEAR_8:
|
|
samplesize = 8;
|
|
break;
|
|
case SND_FORMAT_LINEAR_16:
|
|
samplesize = 16;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: Unsupported SND_FORMAT\n", command);
|
|
exit(-1);
|
|
}
|
|
|
|
dsp_stereo = (snd->channelCount == 2) ? 1 : 0;
|
|
dsp_speed = snd->samplingRate;
|
|
count = snd->dataSize;
|
|
|
|
/* read Info-Strings */
|
|
infolen = snd->dataLocation - sizeof(SndHeader);
|
|
info = (char *)malloc(infolen);
|
|
read(fd, info, infolen);
|
|
if (!quiet_mode)
|
|
fprintf(stderr, "SoundFile Info: %s\n", info);
|
|
free(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function: write_zeros
|
|
* Description: write zeros from zerobuf to simulate silence
|
|
*/
|
|
void write_zeros(unsigned x)
|
|
{
|
|
unsigned l;
|
|
|
|
while (x) {
|
|
l = min(x, zbuf_size);
|
|
if (write(audio, (char *)zerobuf, l) != l) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
x -= l;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: sync_dsp
|
|
* Description: (is needed if we plan to change speed, stereo ... during output)
|
|
*/
|
|
void sync_dsp(void)
|
|
{
|
|
#if 0
|
|
if (ioctl(audio, SNDCTL_DSP_SYNC, NULL) < 0) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Function: set_dsp_speed
|
|
* Description: set the speed for output
|
|
*/
|
|
void set_dsp_speed(int *dsp_speed)
|
|
{
|
|
if (ioctl(audio, SNDCTL_DSP_SPEED, dsp_speed) < 0) {
|
|
fprintf(stderr, "%s: unable to set audio speed\n", command);
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: one_channel
|
|
* Description:
|
|
*
|
|
* if to_mono:
|
|
* compress 8 bit stereo data 2:1, needed if we want play 'one track';
|
|
* this is useful, if you habe SB 1.0 - 2.0 (I have 1.0) and want
|
|
* hear the sample (in Mono)
|
|
*
|
|
* if to_8:
|
|
* compress 16 bit to 8 by using hi-byte; wave-files use signed words,
|
|
* so we need to convert it in "unsigned" sample (0x80 is now zero)
|
|
*
|
|
* WARNING: this procedure can't compress 16 bit stereo to 16 bit mono,
|
|
* because if you have a 16 (or 12) bit card you should have
|
|
* stereo (or I'm wrong ?? )
|
|
*/
|
|
u_long one_channel(u_char * buf, u_long l, char to_mono, char to_8)
|
|
{
|
|
register u_char *w = buf;
|
|
register u_char *w2 = buf;
|
|
char ofs = 0;
|
|
u_long incr = 0;
|
|
u_long c, ret;
|
|
|
|
if (to_mono)
|
|
++incr;
|
|
if (to_8) {
|
|
++incr;
|
|
++w2;
|
|
ofs = 128;
|
|
}
|
|
ret = c = l >> incr;
|
|
incr = incr << 1;
|
|
|
|
while (c--) {
|
|
*w++ = *w2 + ofs;
|
|
w2 += incr;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Function: vplay
|
|
* Description: play a .voc file
|
|
*/
|
|
void vplay(int fd, int ofs, char *name)
|
|
{
|
|
int l, real_l;
|
|
BlockType *bp;
|
|
Voice_data *vd;
|
|
Ext_Block *eb;
|
|
u_long nextblock, in_buffer;
|
|
u_char *data = audiobuf;
|
|
char was_extended = 0, output = 0;
|
|
u_short *sp, repeat = 0;
|
|
u_long silence;
|
|
int filepos = 0;
|
|
char one_chn = 0;
|
|
|
|
#define COUNT(x) nextblock -= x; in_buffer -=x ;data += x
|
|
|
|
/* first SYNC the dsp */
|
|
sync_dsp();
|
|
|
|
if (!quiet_mode)
|
|
fprintf(stderr, "Playing Creative Labs Voice file ...\n");
|
|
|
|
/* first we waste the rest of header, ugly but we don't need seek */
|
|
while (ofs > abuf_size) {
|
|
read(fd, (char *)audiobuf, abuf_size);
|
|
ofs -= abuf_size;
|
|
}
|
|
if (ofs)
|
|
read(fd, audiobuf, ofs);
|
|
|
|
/* .voc files are 8 bit (now) */
|
|
samplesize = VOC_SAMPLESIZE;
|
|
ioctl(audio, SNDCTL_DSP_SAMPLESIZE, &samplesize);
|
|
if (samplesize != VOC_SAMPLESIZE) {
|
|
fprintf(stderr, "%s: unable to set 8 bit sample size!\n", command);
|
|
exit(-1);
|
|
}
|
|
/* and there are MONO by default */
|
|
dsp_stereo = MODE_MONO;
|
|
ioctl(audio, SNDCTL_DSP_STEREO, &dsp_stereo);
|
|
|
|
in_buffer = nextblock = 0;
|
|
while (1) {
|
|
Fill_the_buffer: /* need this for repeat */
|
|
if (in_buffer < 32) {
|
|
/* move the rest of buffer to pos 0 and fill the audiobuf up */
|
|
if (in_buffer)
|
|
memcpy((char *)audiobuf, data, in_buffer);
|
|
data = audiobuf;
|
|
if ((l =
|
|
read(fd, (char *)audiobuf + in_buffer, abuf_size - in_buffer)) > 0)
|
|
in_buffer += l;
|
|
else if (!in_buffer) {
|
|
/* the file is truncated, so simulate 'Terminator'
|
|
and reduce the datablock for save landing */
|
|
nextblock = audiobuf[0] = 0;
|
|
if (l == -1) {
|
|
perror(name);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
while (!nextblock) { /* this is a new block */
|
|
bp = (BlockType *) data;
|
|
COUNT(sizeof(BlockType));
|
|
nextblock = DATALEN(bp);
|
|
if (output && !quiet_mode)
|
|
fprintf(stderr, "\n"); /* write /n after ASCII-out */
|
|
output = 0;
|
|
switch (bp->type) {
|
|
case 0:
|
|
d_printf((stderr, "Terminator\n"));
|
|
return; /* VOC-file stop */
|
|
case 1:
|
|
vd = (Voice_data *) data;
|
|
COUNT(sizeof(Voice_data));
|
|
/* we need a SYNC, before we can set new SPEED, STEREO ... */
|
|
sync_dsp();
|
|
|
|
if (!was_extended) {
|
|
dsp_speed = (int)(vd->tc);
|
|
dsp_speed = 1000000 / (256 - dsp_speed);
|
|
d_printf((stderr, "Voice data %d Hz\n", dsp_speed));
|
|
if (vd->pack) { /* /dev/dsp can't it */
|
|
fprintf(stderr,
|
|
"%s: can't play packed .voc files\n",
|
|
command);
|
|
return;
|
|
}
|
|
if (dsp_stereo) { /* if we are in Stereo-Mode, switch back */
|
|
dsp_stereo = MODE_MONO;
|
|
ioctl(audio, SNDCTL_DSP_STEREO, &dsp_stereo);
|
|
}
|
|
} else { /* there was extended block */
|
|
if (one_chn) /* if one Stereo fails, why test another ? */
|
|
dsp_stereo = MODE_MONO;
|
|
else if (dsp_stereo) { /* want Stereo */
|
|
/* shit, my MACRO dosn't work here */
|
|
#ifdef SOUND_VERSION
|
|
if (ioctl
|
|
(audio, SNDCTL_DSP_STEREO,
|
|
&dsp_stereo) < 0) {
|
|
#else
|
|
if (dsp_stereo !=
|
|
ioctl(audio,
|
|
SNDCTL_DSP_STEREO, dsp_stereo)) {
|
|
#endif
|
|
dsp_stereo = MODE_MONO;
|
|
fprintf(stderr,
|
|
"%s: can't play in Stereo; playing only one channel\n",
|
|
command);
|
|
one_chn = 1;
|
|
}
|
|
}
|
|
was_extended = 0;
|
|
}
|
|
set_dsp_speed(&dsp_speed);
|
|
break;
|
|
case 2: /* nothing to do, pure data */
|
|
d_printf((stderr, "Voice continuation\n"));
|
|
break;
|
|
case 3: /* a silence block, no data, only a count */
|
|
sp = (u_short *) data;
|
|
COUNT(sizeof(u_short));
|
|
dsp_speed = (int)(*data);
|
|
COUNT(1);
|
|
dsp_speed = 1000000 / (256 - dsp_speed);
|
|
sync_dsp();
|
|
set_dsp_speed(&dsp_speed);
|
|
silence = (((u_long) * sp) * 1000) / dsp_speed;
|
|
d_printf((stderr, "Silence for %ld ms\n", silence));
|
|
write_zeros(*sp);
|
|
break;
|
|
case 4: /* a marker for syncronisation, no effect */
|
|
sp = (u_short *) data;
|
|
COUNT(sizeof(u_short));
|
|
d_printf((stderr, "Marker %d\n", *sp));
|
|
break;
|
|
case 5: /* ASCII text, we copy to stderr */
|
|
output = 1;
|
|
d_printf((stderr, "ASCII - text :\n"));
|
|
break;
|
|
case 6: /* repeat marker, says repeatcount */
|
|
/* my specs don't say it: maybe this can be recursive, but
|
|
I don't think somebody use it */
|
|
repeat = *(u_short *) data;
|
|
COUNT(sizeof(u_short));
|
|
d_printf((stderr, "Repeat loop %d times\n", repeat));
|
|
if (filepos >= 0) /* if < 0, one seek fails, why test another */
|
|
if ((filepos = lseek(fd, 0, 1)) < 0) {
|
|
fprintf(stderr,
|
|
"%s: can't play loops; %s isn't seekable\n",
|
|
command, name);
|
|
repeat = 0;
|
|
} else
|
|
filepos -= in_buffer; /* set filepos after repeat */
|
|
else
|
|
repeat = 0;
|
|
break;
|
|
case 7: /* ok, lets repeat that be rewinding tape */
|
|
if (repeat) {
|
|
if (repeat != 0xFFFF) {
|
|
d_printf((stderr, "Repeat loop %d\n", repeat));
|
|
--repeat;
|
|
} else
|
|
d_printf((stderr, "Neverending loop\n"));
|
|
lseek(fd, filepos, 0);
|
|
in_buffer = 0; /* clear the buffer */
|
|
goto Fill_the_buffer;
|
|
} else
|
|
d_printf((stderr, "End repeat loop\n"));
|
|
break;
|
|
case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
|
|
was_extended = 1;
|
|
eb = (Ext_Block *) data;
|
|
COUNT(sizeof(Ext_Block));
|
|
dsp_speed = (int)(eb->tc);
|
|
dsp_speed = 256000000L / (65536 - dsp_speed);
|
|
dsp_stereo = eb->mode;
|
|
if (dsp_stereo == MODE_STEREO)
|
|
dsp_speed = dsp_speed >> 1;
|
|
if (eb->pack) { /* /dev/dsp can't it */
|
|
fprintf(stderr,
|
|
"%s: can't play packed .voc files\n", command);
|
|
return;
|
|
}
|
|
d_printf((stderr, "Extended block %s %d Hz\n",
|
|
(eb->mode ? "Stereo" : "Mono"), dsp_speed));
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"%s: unknown blocktype %d. terminate.\n",
|
|
command, bp->type);
|
|
return;
|
|
} /* switch (bp->type) */
|
|
} /* while (! nextblock) */
|
|
/* put nextblock data bytes to dsp */
|
|
l = min(in_buffer, nextblock);
|
|
if (l) {
|
|
if (output && !quiet_mode)
|
|
write(2, data, l); /* to stderr */
|
|
else {
|
|
real_l = one_chn ? one_channel(data, l, one_chn, 0) : l;
|
|
if (write(audio, data, real_l) != real_l) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
}
|
|
COUNT(l);
|
|
}
|
|
} /* while(1) */
|
|
}
|
|
|
|
/*
|
|
* Function: init_raw_data
|
|
* Description: set the globals for playing raw data
|
|
*/
|
|
void init_raw_data(void)
|
|
{
|
|
timelimit = raw_info.timelimit;
|
|
dsp_speed = raw_info.dsp_speed;
|
|
dsp_stereo = raw_info.dsp_stereo;
|
|
samplesize = raw_info.samplesize;
|
|
}
|
|
|
|
/*
|
|
* Function: calc_count
|
|
* Description: calculate the data count to read from/to dsp
|
|
*/
|
|
u_long calc_count(void)
|
|
{
|
|
u_long count;
|
|
|
|
if (!timelimit)
|
|
count = 0x7fffffff;
|
|
else {
|
|
count = timelimit * dsp_speed;
|
|
if (dsp_stereo)
|
|
count *= 2;
|
|
if (samplesize != 8)
|
|
count *= 2;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* Function: start_voc
|
|
* Description: write a VOC header
|
|
*/
|
|
void start_voc(int fd, u_long cnt)
|
|
{
|
|
VocHeader vh;
|
|
BlockType bt;
|
|
Voice_data vd;
|
|
Ext_Block eb;
|
|
|
|
strcpy((char *)vh.magic, MAGIC_STRING);
|
|
vh.headerlen = sizeof(VocHeader);
|
|
vh.version = ACTUAL_VERSION;
|
|
vh.coded_ver = 0x1233 - ACTUAL_VERSION;
|
|
|
|
write(fd, &vh, sizeof(VocHeader));
|
|
|
|
if (dsp_stereo) {
|
|
/* write a extended block */
|
|
bt.type = 8;
|
|
bt.datalen = 4;
|
|
bt.datalen_m = bt.datalen_h = 0;
|
|
write(fd, &bt, sizeof(BlockType));
|
|
eb.tc = (u_short) (65536 - 256000000L / (dsp_speed << 1));
|
|
eb.pack = 0;
|
|
eb.mode = 1;
|
|
write(fd, &eb, sizeof(Ext_Block));
|
|
}
|
|
bt.type = 1;
|
|
cnt += sizeof(Voice_data); /* Voice_data block follows */
|
|
bt.datalen = (u_char) (cnt & 0xFF);
|
|
bt.datalen_m = (u_char) ((cnt & 0xFF00) >> 8);
|
|
bt.datalen_h = (u_char) ((cnt & 0xFF0000) >> 16);
|
|
write(fd, &bt, sizeof(BlockType));
|
|
vd.tc = (u_char) (256 - (1000000 / dsp_speed));
|
|
vd.pack = 0;
|
|
write(fd, &vd, sizeof(Voice_data));
|
|
}
|
|
|
|
/*
|
|
* Function: start_wave
|
|
* Description: write a WAVE header
|
|
*/
|
|
void start_wave(int fd, u_long cnt)
|
|
{
|
|
WaveHeader wh;
|
|
|
|
wh.main_chunk = RIFF;
|
|
wh.length = cnt + sizeof(WaveHeader) - 8;
|
|
wh.chunk_type = WAVE;
|
|
wh.sub_chunk = FMT;
|
|
wh.sc_len = 16;
|
|
wh.format = PCM_CODE;
|
|
wh.modus = dsp_stereo ? 2 : 1;
|
|
wh.sample_fq = dsp_speed;
|
|
wh.byte_p_spl = ((samplesize == 8) ? 1 : 2) * (dsp_stereo ? 2 : 1);
|
|
wh.byte_p_sec = dsp_speed * wh.modus * wh.byte_p_spl;
|
|
wh.bit_p_spl = samplesize;
|
|
wh.data_chunk = DATA;
|
|
wh.data_length = cnt;
|
|
write(fd, &wh, sizeof(WaveHeader));
|
|
}
|
|
|
|
/*
|
|
* Function: end_voc
|
|
* Description: close VOC
|
|
*/
|
|
void end_voc(int fd)
|
|
{
|
|
char dummy = 0; /* Write a Terminator */
|
|
write(fd, &dummy, 1);
|
|
if (fd != 1)
|
|
close(fd);
|
|
}
|
|
|
|
/*
|
|
* Function: end_wave_raw
|
|
* Description: close WAV, RAW
|
|
*/
|
|
void end_wave_raw(int fd)
|
|
{ /* only close output */
|
|
if (fd != 1)
|
|
close(fd);
|
|
}
|
|
|
|
/*
|
|
* Function: start_snd
|
|
* Description: write a SND header
|
|
*/
|
|
void start_snd(int fd, u_long count)
|
|
{
|
|
SndHeader snd;
|
|
char *sndinfo = "Recorded by vrec\000";
|
|
|
|
snd.magic = SND_MAGIC;
|
|
snd.dataLocation = sizeof(SndHeader) + strlen(sndinfo);
|
|
snd.dataSize = count;
|
|
switch (samplesize) {
|
|
case 8:
|
|
snd.dataFormat = SND_FORMAT_LINEAR_8;
|
|
break;
|
|
case 16:
|
|
snd.dataFormat = SND_FORMAT_LINEAR_16;
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"%d bit: unsupported sample size for NeXt sound file!\n", samplesize);
|
|
exit(-1);
|
|
}
|
|
snd.samplingRate = dsp_speed;
|
|
snd.channelCount = dsp_stereo ? 2 : 1;
|
|
|
|
write(fd, &snd, sizeof(SndHeader));
|
|
write(fd, sndinfo, strlen(sndinfo));
|
|
}
|
|
|
|
/*
|
|
* Function: end_snd
|
|
* Description: close SND
|
|
*/
|
|
void end_snd(int fd)
|
|
{
|
|
if (fd != 1)
|
|
close(fd);
|
|
}
|
|
|
|
/*
|
|
* Function: recplay
|
|
* Description: play/record raw data. This proc handles WAVE files and recording
|
|
* VOCs (as one block)
|
|
*/
|
|
void recplay(int fd, int loaded, u_long count, int rtype, char *name)
|
|
{
|
|
int l, real_l;
|
|
u_long c;
|
|
char one_chn = 0;
|
|
char to_8 = 0;
|
|
int tmps;
|
|
|
|
sync_dsp();
|
|
tmps = samplesize;
|
|
ioctl(audio, SNDCTL_DSP_SAMPLESIZE, &tmps);
|
|
if (tmps != samplesize) {
|
|
fprintf(stderr, "%s: unable to set %d bit sample size", command, samplesize);
|
|
if (samplesize == 16) {
|
|
samplesize = 8;
|
|
ioctl(audio, SNDCTL_DSP_SAMPLESIZE, &samplesize);
|
|
if (samplesize != 8) {
|
|
fprintf(stderr,
|
|
"%s: unable to set 8 bit sample size!\n", command);
|
|
exit(-1);
|
|
}
|
|
fprintf(stderr, "; playing 8 bit\n");
|
|
to_8 = 1;
|
|
} else {
|
|
fprintf(stderr, "\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
#ifdef SOUND_VERSION
|
|
if (ioctl(audio, SNDCTL_DSP_STEREO, &dsp_stereo) < 0) {
|
|
#else
|
|
if (dsp_stereo != ioctl(audio, SNDCTL_DSP_STEREO, dsp_stereo)) {
|
|
#endif
|
|
if (direction == PLAY) {
|
|
fprintf(stderr,
|
|
"%s: can't play in Stereo; playing only one channel\n",
|
|
command);
|
|
dsp_stereo = MODE_MONO;
|
|
one_chn = 1;
|
|
} else {
|
|
fprintf(stderr, "%s: can't record in Stereo\n", command);
|
|
exit(-1);
|
|
}
|
|
}
|
|
set_dsp_speed(&dsp_speed);
|
|
if (!quiet_mode) {
|
|
fprintf(stderr, "%s %s : ",
|
|
(direction == PLAY) ? "Playing" : "Recording",
|
|
fmt_rec_table[rtype].what);
|
|
if (samplesize != 8)
|
|
fprintf(stderr, "%d bit, ", samplesize);
|
|
fprintf(stderr, "Speed %d Hz ", dsp_speed);
|
|
fprintf(stderr, "%d bits ", samplesize);
|
|
fprintf(stderr, "%s ...\n", dsp_stereo ? "Stereo" : "Mono");
|
|
}
|
|
|
|
if (direction == PLAY) {
|
|
while (count) {
|
|
c = count;
|
|
|
|
if (c > abuf_size)
|
|
c = abuf_size;
|
|
|
|
if ((l = read(fd, (char *)audiobuf + loaded, c - loaded)) > 0) {
|
|
l += loaded;
|
|
loaded = 0; /* correct the count; ugly but ... */
|
|
real_l = (one_chn
|
|
|| to_8) ? one_channel(audiobuf, l,
|
|
one_chn, to_8) : l;
|
|
|
|
/* change byte order if necessary */
|
|
if (convert && (samplesize == 16)) {
|
|
long i;
|
|
|
|
for (i = 0; i < real_l; i += 2)
|
|
*((short *)(audiobuf + i)) =
|
|
htons(*((short *)(audiobuf + i)));
|
|
}
|
|
|
|
if (write(audio, (char *)audiobuf, real_l) != real_l) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
count -= l;
|
|
} else {
|
|
if (l == -1) {
|
|
perror(name);
|
|
exit(-1);
|
|
}
|
|
count = 0; /* Stop */
|
|
}
|
|
} /* while (count) */
|
|
} else { /* we are recording */
|
|
|
|
while (count) {
|
|
c = count;
|
|
if (c > abuf_size)
|
|
c = abuf_size;
|
|
|
|
if ((l = read(audio, (char *)audiobuf, c)) > 0) {
|
|
if (write(fd, (char *)audiobuf, l) != l) {
|
|
perror(name);
|
|
exit(-1);
|
|
}
|
|
count -= l;
|
|
}
|
|
|
|
if (l == -1) {
|
|
perror(AUDIO);
|
|
exit(-1);
|
|
}
|
|
} /* While count */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: record_play
|
|
* Description: play or record a file
|
|
*/
|
|
void record_play(char *name)
|
|
{
|
|
int fd, ofs;
|
|
|
|
if (direction == PLAY) {
|
|
|
|
if (!name) {
|
|
fd = 0;
|
|
name = "stdin";
|
|
} else if ((fd = open(name, O_RDONLY, 0)) == -1) {
|
|
perror(name);
|
|
exit(-1);
|
|
}
|
|
/* Read the smallest header first, then the
|
|
missing bytes for the next, etc. */
|
|
|
|
/* read SND-header */
|
|
read(fd, (char *)audiobuf, sizeof(SndHeader));
|
|
if (test_sndfile(audiobuf, fd) >= 0)
|
|
recplay(fd, 0, count, SND_FMT, name);
|
|
|
|
else {
|
|
/* read VOC-Header */
|
|
read(fd, (char *)audiobuf + sizeof(SndHeader),
|
|
sizeof(VocHeader) - sizeof(SndHeader));
|
|
if ((ofs = test_vocfile(audiobuf)) >= 0)
|
|
vplay(fd, ofs, name);
|
|
|
|
else {
|
|
/* read bytes for WAVE-header */
|
|
read(fd, (char *)audiobuf + sizeof(VocHeader),
|
|
sizeof(WaveHeader) - sizeof(VocHeader));
|
|
if (test_wavefile(audiobuf) >= 0)
|
|
recplay(fd, 0, count, WAVE_FMT, name);
|
|
else {
|
|
/* should be raw data */
|
|
init_raw_data();
|
|
count = calc_count();
|
|
recplay(fd, sizeof(WaveHeader), count, RAW_DATA, name);
|
|
}
|
|
}
|
|
}
|
|
if (fd != 0)
|
|
close(fd);
|
|
} else { /* recording ... */
|
|
if (!name) {
|
|
fd = 1;
|
|
name = "stdout";
|
|
} else {
|
|
if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
|
|
perror(name);
|
|
exit(-1);
|
|
}
|
|
}
|
|
count = calc_count() & 0xFFFFFFFE;
|
|
/* WAVE-file should be even (I'm not sure), but wasting one byte
|
|
isn't a problem (this can only be in 8 bit mono) */
|
|
if (fmt_rec_table[record_type].start)
|
|
fmt_rec_table[record_type].start(fd, count);
|
|
recplay(fd, 0, count, record_type, name);
|
|
fmt_rec_table[record_type].end(fd);
|
|
}
|
|
}
|