parent
e6567e8112
commit
fac4b3d4ba
@ -1,418 +0,0 @@ |
|||||||
/* libSoX SampleVision file format handler.
|
|
||||||
* Output is always in little-endian (80x86/VAX) order. |
|
||||||
* |
|
||||||
* Derived from: libSoX skeleton handler file. |
|
||||||
* |
|
||||||
* Add: Loop point verbose info. It's a start, anyway. |
|
||||||
*/ |
|
||||||
|
|
||||||
/*
|
|
||||||
* June 30, 1992 |
|
||||||
* Copyright 1992 Leigh Smith And Sundry Contributors |
|
||||||
* This source code is freely redistributable and may be used for |
|
||||||
* any purpose. This copyright notice must be maintained. |
|
||||||
* Leigh Smith And Sundry Contributors are not responsible for |
|
||||||
* the consequences of using this software. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "sox_i.h" |
|
||||||
#include <string.h> |
|
||||||
#include <errno.h> |
|
||||||
|
|
||||||
#define NAMELEN 30 /* Size of Samplevision name */ |
|
||||||
#define COMMENTLEN 60 /* Size of Samplevision comment, not shared */ |
|
||||||
#define MIDI_UNITY 60 /* MIDI note number to play sample at unity */ |
|
||||||
#define MARKERLEN (size_t)10 /* Size of Marker name */ |
|
||||||
|
|
||||||
/* The header preceeding the sample data */ |
|
||||||
struct smpheader { |
|
||||||
char Id[18]; /* File identifier */ |
|
||||||
char version[4]; /* File version */ |
|
||||||
char comments[COMMENTLEN]; /* User comments */ |
|
||||||
char name[NAMELEN + 1]; /* Sample Name, left justified */ |
|
||||||
}; |
|
||||||
#define HEADERSIZE (sizeof(struct smpheader) - 1) /* -1 for name's \0 */ |
|
||||||
|
|
||||||
/* Samplevision loop definition structure */ |
|
||||||
struct loop { |
|
||||||
uint32_t start; /* Sample count into sample data, not byte count */ |
|
||||||
uint32_t end; /* end point */ |
|
||||||
unsigned char type; /* 0 = loop off, 1 = forward, 2 = forw/back */ |
|
||||||
unsigned short count; /* No of times to loop */ |
|
||||||
}; |
|
||||||
|
|
||||||
/* Samplevision marker definition structure */ |
|
||||||
struct marker { |
|
||||||
char name[MARKERLEN + 1]; /* Ascii Marker name */ |
|
||||||
uint32_t position; /* Sample Number, not byte number */ |
|
||||||
}; |
|
||||||
|
|
||||||
/* The trailer following the sample data */ |
|
||||||
struct smptrailer { |
|
||||||
struct loop loops[8]; /* loops */ |
|
||||||
struct marker markers[8]; /* markers */ |
|
||||||
int8_t MIDInote; /* for unity pitch playback */ |
|
||||||
uint32_t rate; /* in hertz */ |
|
||||||
uint32_t SMPTEoffset; /* in subframes - huh? */ |
|
||||||
uint32_t CycleSize; /* sample count in one cycle of the */ |
|
||||||
/* sampled sound -1 if unknown */ |
|
||||||
}; |
|
||||||
|
|
||||||
/* Private data for SMP file */ |
|
||||||
typedef struct { |
|
||||||
uint64_t NoOfSamps; /* Sample data count in words */ |
|
||||||
uint64_t dataStart; |
|
||||||
/* comment memory resides in private data because it's small */ |
|
||||||
char comment[COMMENTLEN + NAMELEN + 3]; |
|
||||||
} priv_t; |
|
||||||
|
|
||||||
static char const *SVmagic = "SOUND SAMPLE DATA ", *SVvers = "2.1 "; |
|
||||||
|
|
||||||
/*
|
|
||||||
* Read the SampleVision trailer structure. |
|
||||||
* Returns 1 if everything was read ok, 0 if there was an error. |
|
||||||
*/ |
|
||||||
static int readtrailer(sox_format_t * ft, struct smptrailer *trailer) |
|
||||||
{ |
|
||||||
int i; |
|
||||||
uint16_t trash16; |
|
||||||
|
|
||||||
lsx_readw(ft, &trash16); /* read reserved word */ |
|
||||||
for(i = 0; i < 8; i++) { /* read the 8 loops */ |
|
||||||
lsx_readdw(ft, &(trailer->loops[i].start)); |
|
||||||
ft->oob.loops[i].start = trailer->loops[i].start; |
|
||||||
lsx_readdw(ft, &(trailer->loops[i].end)); |
|
||||||
ft->oob.loops[i].length = |
|
||||||
trailer->loops[i].end - trailer->loops[i].start; |
|
||||||
lsx_readb(ft, &(trailer->loops[i].type)); |
|
||||||
ft->oob.loops[i].type = trailer->loops[i].type; |
|
||||||
lsx_readw(ft, &(trailer->loops[i].count)); |
|
||||||
ft->oob.loops[i].count = trailer->loops[i].count; |
|
||||||
} |
|
||||||
for(i = 0; i < 8; i++) { /* read the 8 markers */ |
|
||||||
if (lsx_readbuf(ft, trailer->markers[i].name, MARKERLEN) != MARKERLEN) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EHDR,"EOF in SMP"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
trailer->markers[i].name[MARKERLEN] = 0; |
|
||||||
lsx_readdw(ft, &(trailer->markers[i].position)); |
|
||||||
} |
|
||||||
lsx_readsb(ft, &(trailer->MIDInote)); |
|
||||||
lsx_readdw(ft, &(trailer->rate)); |
|
||||||
lsx_readdw(ft, &(trailer->SMPTEoffset)); |
|
||||||
lsx_readdw(ft, &(trailer->CycleSize)); |
|
||||||
return(SOX_SUCCESS); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* set the trailer data - loops and markers, to reasonably benign values |
|
||||||
*/ |
|
||||||
static void settrailer(sox_format_t * ft, struct smptrailer *trailer, sox_rate_t rate) |
|
||||||
{ |
|
||||||
int i; |
|
||||||
|
|
||||||
for(i = 0; i < 8; i++) { /* copy the 8 loops */ |
|
||||||
if (ft->oob.loops[i].type != 0) { |
|
||||||
trailer->loops[i].start = ft->oob.loops[i].start > UINT_MAX |
|
||||||
? UINT_MAX |
|
||||||
: ft->oob.loops[i].start; |
|
||||||
/* to mark it as not set */ |
|
||||||
trailer->loops[i].end = ft->oob.loops[i].start + ft->oob.loops[i].length > UINT_MAX |
|
||||||
? UINT_MAX |
|
||||||
: ft->oob.loops[i].start + ft->oob.loops[i].length; |
|
||||||
trailer->loops[i].type = ft->oob.loops[i].type; |
|
||||||
trailer->loops[i].count = ft->oob.loops[i].count; |
|
||||||
} else { |
|
||||||
/* set first loop start as FFFFFFFF */ |
|
||||||
trailer->loops[i].start = ~0u; |
|
||||||
/* to mark it as not set */ |
|
||||||
trailer->loops[i].end = 0; |
|
||||||
trailer->loops[i].type = 0; |
|
||||||
trailer->loops[i].count = 0; |
|
||||||
} |
|
||||||
} |
|
||||||
for(i = 0; i < 8; i++) { /* write the 8 markers */ |
|
||||||
strcpy(trailer->markers[i].name, " "); |
|
||||||
trailer->markers[i].position = ~0u; |
|
||||||
} |
|
||||||
trailer->MIDInote = MIDI_UNITY; /* Unity play back */ |
|
||||||
trailer->rate = rate; |
|
||||||
trailer->SMPTEoffset = 0; |
|
||||||
trailer->CycleSize = ~0u; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* Write the SampleVision trailer structure. |
|
||||||
* Returns 1 if everything was written ok, 0 if there was an error. |
|
||||||
*/ |
|
||||||
static int writetrailer(sox_format_t * ft, struct smptrailer *trailer) |
|
||||||
{ |
|
||||||
int i; |
|
||||||
|
|
||||||
lsx_writew(ft, 0); /* write the reserved word */ |
|
||||||
for(i = 0; i < 8; i++) { /* write the 8 loops */ |
|
||||||
lsx_writedw(ft, trailer->loops[i].start); |
|
||||||
lsx_writedw(ft, trailer->loops[i].end); |
|
||||||
lsx_writeb(ft, trailer->loops[i].type); |
|
||||||
lsx_writew(ft, trailer->loops[i].count); |
|
||||||
} |
|
||||||
for(i = 0; i < 8; i++) { /* write the 8 markers */ |
|
||||||
if (lsx_writes(ft, trailer->markers[i].name) == SOX_EOF) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EHDR,"EOF in SMP"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
lsx_writedw(ft, trailer->markers[i].position); |
|
||||||
} |
|
||||||
lsx_writeb(ft, (uint8_t)(trailer->MIDInote)); |
|
||||||
lsx_writedw(ft, trailer->rate); |
|
||||||
lsx_writedw(ft, trailer->SMPTEoffset); |
|
||||||
lsx_writedw(ft, trailer->CycleSize); |
|
||||||
return(SOX_SUCCESS); |
|
||||||
} |
|
||||||
|
|
||||||
static int sox_smpseek(sox_format_t * ft, uint64_t offset) |
|
||||||
{ |
|
||||||
uint64_t new_offset; |
|
||||||
size_t channel_block, alignment; |
|
||||||
priv_t * smp = (priv_t *) ft->priv; |
|
||||||
|
|
||||||
new_offset = offset * (ft->encoding.bits_per_sample >> 3); |
|
||||||
/* Make sure request aligns to a channel block (ie left+right) */ |
|
||||||
channel_block = ft->signal.channels * (ft->encoding.bits_per_sample >> 3); |
|
||||||
alignment = new_offset % channel_block; |
|
||||||
/* Most common mistaken is to compute something like
|
|
||||||
* "skip everthing upto and including this sample" so |
|
||||||
* advance to next sample block in this case. |
|
||||||
*/ |
|
||||||
if (alignment != 0) |
|
||||||
new_offset += (channel_block - alignment); |
|
||||||
new_offset += smp->dataStart; |
|
||||||
|
|
||||||
ft->sox_errno = lsx_seeki(ft, (off_t)new_offset, SEEK_SET); |
|
||||||
|
|
||||||
if( ft->sox_errno == SOX_SUCCESS ) |
|
||||||
smp->NoOfSamps = ft->signal.length - (new_offset / (ft->encoding.bits_per_sample >> 3)); |
|
||||||
|
|
||||||
return(ft->sox_errno); |
|
||||||
} |
|
||||||
/*
|
|
||||||
* Do anything required before you start reading samples. |
|
||||||
* Read file header. |
|
||||||
* Find out sampling rate, |
|
||||||
* size and encoding of samples, |
|
||||||
* mono/stereo/quad. |
|
||||||
*/ |
|
||||||
static int sox_smpstartread(sox_format_t * ft) |
|
||||||
{ |
|
||||||
priv_t * smp = (priv_t *) ft->priv; |
|
||||||
int namelen, commentlen; |
|
||||||
off_t samplestart; |
|
||||||
size_t i; |
|
||||||
unsigned dw; |
|
||||||
struct smpheader header; |
|
||||||
struct smptrailer trailer; |
|
||||||
|
|
||||||
/* If you need to seek around the input file. */ |
|
||||||
if (! ft->seekable) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EOF,"SMP input file must be a file, not a pipe"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
|
|
||||||
/* Read SampleVision header */ |
|
||||||
if (lsx_readbuf(ft, &header, HEADERSIZE) != HEADERSIZE) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EHDR,"unexpected EOF in SMP header"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
if (strncmp(header.Id, SVmagic, (size_t)17) != 0) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EHDR,"SMP header does not begin with magic word %s", SVmagic); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
if (strncmp(header.version, SVvers, (size_t)4) != 0) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EHDR,"SMP header is not version %s", SVvers); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
|
|
||||||
/* Format the sample name and comments to a single comment */ |
|
||||||
/* string. We decrement the counters till we encounter non */ |
|
||||||
/* padding space chars, so the *lengths* are low by one */ |
|
||||||
for (namelen = NAMELEN-1; |
|
||||||
namelen >= 0 && header.name[namelen] == ' '; namelen--) |
|
||||||
; |
|
||||||
for (commentlen = COMMENTLEN-1; |
|
||||||
commentlen >= 0 && header.comments[commentlen] == ' '; commentlen--) |
|
||||||
; |
|
||||||
sprintf(smp->comment, "%.*s: %.*s", namelen+1, header.name, |
|
||||||
commentlen+1, header.comments); |
|
||||||
sox_append_comments(&ft->oob.comments, smp->comment); |
|
||||||
|
|
||||||
/* Extract out the sample size (always intel format) */ |
|
||||||
lsx_readdw(ft, &dw); |
|
||||||
smp->NoOfSamps = dw; |
|
||||||
/* mark the start of the sample data */ |
|
||||||
samplestart = lsx_tell(ft); |
|
||||||
|
|
||||||
/* seek from the current position (the start of sample data) by */ |
|
||||||
/* NoOfSamps * sizeof(int16_t) */ |
|
||||||
if (lsx_seeki(ft, (off_t)(smp->NoOfSamps * 2), 1) == -1) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,errno,"SMP unable to seek to trailer"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
if (readtrailer(ft, &trailer)) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EHDR,"unexpected EOF in SMP trailer"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
|
|
||||||
/* seek back to the beginning of the data */ |
|
||||||
if (lsx_seeki(ft, (off_t)samplestart, 0) == -1) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,errno,"SMP unable to seek back to start of sample data"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
|
|
||||||
ft->signal.rate = (int) trailer.rate; |
|
||||||
ft->encoding.bits_per_sample = 16; |
|
||||||
ft->encoding.encoding = SOX_ENCODING_SIGN2; |
|
||||||
ft->signal.channels = 1; |
|
||||||
smp->dataStart = samplestart; |
|
||||||
ft->signal.length = smp->NoOfSamps; |
|
||||||
|
|
||||||
lsx_report("SampleVision trailer:"); |
|
||||||
for(i = 0; i < 8; i++) if (1 || trailer.loops[i].count) { |
|
||||||
lsx_report("Loop %lu: start: %6d", (unsigned long)i, trailer.loops[i].start); |
|
||||||
lsx_report(" end: %6d", trailer.loops[i].end); |
|
||||||
lsx_report(" count: %6d", trailer.loops[i].count); |
|
||||||
switch(trailer.loops[i].type) { |
|
||||||
case 0: lsx_report("type: off"); break; |
|
||||||
case 1: lsx_report("type: forward"); break; |
|
||||||
case 2: lsx_report("type: forward/backward"); break; |
|
||||||
} |
|
||||||
} |
|
||||||
lsx_report("MIDI Note number: %d", trailer.MIDInote); |
|
||||||
|
|
||||||
ft->oob.instr.nloops = 0; |
|
||||||
for(i = 0; i < 8; i++) |
|
||||||
if (trailer.loops[i].type) |
|
||||||
ft->oob.instr.nloops++; |
|
||||||
for(i = 0; i < ft->oob.instr.nloops; i++) { |
|
||||||
ft->oob.loops[i].type = trailer.loops[i].type; |
|
||||||
ft->oob.loops[i].count = trailer.loops[i].count; |
|
||||||
ft->oob.loops[i].start = trailer.loops[i].start; |
|
||||||
ft->oob.loops[i].length = trailer.loops[i].end |
|
||||||
- trailer.loops[i].start; |
|
||||||
} |
|
||||||
ft->oob.instr.MIDIlow = ft->oob.instr.MIDIhi = |
|
||||||
ft->oob.instr.MIDInote = trailer.MIDInote; |
|
||||||
if (ft->oob.instr.nloops > 0) |
|
||||||
ft->oob.instr.loopmode = SOX_LOOP_8; |
|
||||||
else |
|
||||||
ft->oob.instr.loopmode = SOX_LOOP_NONE; |
|
||||||
|
|
||||||
return(SOX_SUCCESS); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* Read up to len samples from file. |
|
||||||
* Convert to signed longs. |
|
||||||
* Place in buf[]. |
|
||||||
* Return number of samples read. |
|
||||||
*/ |
|
||||||
static size_t sox_smpread(sox_format_t * ft, sox_sample_t *buf, size_t len) |
|
||||||
{ |
|
||||||
priv_t * smp = (priv_t *) ft->priv; |
|
||||||
unsigned short datum; |
|
||||||
size_t done = 0; |
|
||||||
|
|
||||||
for(; done < len && smp->NoOfSamps; done++, smp->NoOfSamps--) { |
|
||||||
lsx_readw(ft, &datum); |
|
||||||
/* scale signed up to long's range */ |
|
||||||
*buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(datum,); |
|
||||||
} |
|
||||||
return done; |
|
||||||
} |
|
||||||
|
|
||||||
static int sox_smpstartwrite(sox_format_t * ft) |
|
||||||
{ |
|
||||||
priv_t * smp = (priv_t *) ft->priv; |
|
||||||
struct smpheader header; |
|
||||||
char * comment = lsx_cat_comments(ft->oob.comments); |
|
||||||
|
|
||||||
/* If you have to seek around the output file */ |
|
||||||
if (! ft->seekable) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,SOX_EOF,"Output .smp file must be a file, not a pipe"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
|
|
||||||
memcpy(header.Id, SVmagic, sizeof(header.Id)); |
|
||||||
memcpy(header.version, SVvers, sizeof(header.version)); |
|
||||||
sprintf(header.comments, "%-*s", COMMENTLEN - 1, "Converted using Sox."); |
|
||||||
sprintf(header.name, "%-*.*s", NAMELEN, NAMELEN, comment); |
|
||||||
free(comment); |
|
||||||
|
|
||||||
/* Write file header */ |
|
||||||
if(lsx_writebuf(ft, &header, HEADERSIZE) != HEADERSIZE) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,errno,"SMP: Can't write header completely"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
lsx_writedw(ft, 0); /* write as zero length for now, update later */ |
|
||||||
smp->NoOfSamps = 0; |
|
||||||
|
|
||||||
return(SOX_SUCCESS); |
|
||||||
} |
|
||||||
|
|
||||||
static size_t sox_smpwrite(sox_format_t * ft, const sox_sample_t *buf, size_t len) |
|
||||||
{ |
|
||||||
priv_t * smp = (priv_t *) ft->priv; |
|
||||||
int datum; |
|
||||||
size_t done = 0; |
|
||||||
|
|
||||||
while(done < len) { |
|
||||||
SOX_SAMPLE_LOCALS; |
|
||||||
datum = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips); |
|
||||||
lsx_writew(ft, (uint16_t)datum); |
|
||||||
smp->NoOfSamps++; |
|
||||||
done++; |
|
||||||
} |
|
||||||
|
|
||||||
return(done); |
|
||||||
} |
|
||||||
|
|
||||||
static int sox_smpstopwrite(sox_format_t * ft) |
|
||||||
{ |
|
||||||
priv_t * smp = (priv_t *) ft->priv; |
|
||||||
struct smptrailer trailer; |
|
||||||
|
|
||||||
/* Assign the trailer data */ |
|
||||||
settrailer(ft, &trailer, ft->signal.rate); |
|
||||||
writetrailer(ft, &trailer); |
|
||||||
if (lsx_seeki(ft, (off_t)112, 0) == -1) |
|
||||||
{ |
|
||||||
lsx_fail_errno(ft,errno,"SMP unable to seek back to save size"); |
|
||||||
return(SOX_EOF); |
|
||||||
} |
|
||||||
lsx_writedw(ft, smp->NoOfSamps > UINT_MAX ? UINT_MAX : (unsigned)smp->NoOfSamps); |
|
||||||
|
|
||||||
return(SOX_SUCCESS); |
|
||||||
} |
|
||||||
|
|
||||||
LSX_FORMAT_HANDLER(smp) |
|
||||||
{ |
|
||||||
static char const * const names[] = {"smp", NULL}; |
|
||||||
static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0}; |
|
||||||
static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE, |
|
||||||
"Turtle Beach SampleVision", names, SOX_FILE_LIT_END | SOX_FILE_MONO, |
|
||||||
sox_smpstartread, sox_smpread, NULL, |
|
||||||
sox_smpstartwrite, sox_smpwrite, sox_smpstopwrite, |
|
||||||
sox_smpseek, write_encodings, NULL, sizeof(priv_t) |
|
||||||
}; |
|
||||||
return &handler; |
|
||||||
} |
|
Loading…
Reference in new issue