9f107b5f80
- fetch: Go CLI tool for searching, downloading, and extracting AKAI sampler ISOs from archive.org - scripts/extract_wavs.sh: Shell script driving akaiutil to convert samples to WAV - Dockerfile: Multi-stage build packaging akaiutil + fetch - third_party/akaiutil: Vendored akaiutil v4.6.7 source (GPLv2) - docs/akaiutil.md: Full akaiutil reference
483 lines
12 KiB
C
Executable File
483 lines
12 KiB
C
Executable File
/*
|
|
* Copyright (C) 2010,2019 Klaus Michael Indlekofer. All rights reserved.
|
|
*
|
|
* m.indlekofer@gmx.de
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
|
|
|
|
#include "commoninclude.h"
|
|
#include "akaiutil_wav.h"
|
|
|
|
|
|
|
|
int
|
|
wav_write_head(int outdes,
|
|
u_int datasize,u_int chnr,u_int samprate,u_int bitnr,
|
|
u_int extrasize)
|
|
{
|
|
struct wav_riffhead_s wavriffhead;
|
|
struct wav_chunkhead_s wavchunkhead;
|
|
struct wav_fmthead_s wavfmthead;
|
|
u_int drate;
|
|
|
|
/* create WAVE RIFF header */
|
|
bzero(&wavriffhead,sizeof(struct wav_riffhead_s));
|
|
bcopy(WAV_RIFFHEAD_RIFFSTR,wavriffhead.riffstr,4);
|
|
{
|
|
u_int fsize;
|
|
|
|
fsize=WAV_HEAD_SIZE-sizeof(struct wav_chunkhead_s)+datasize+extrasize;
|
|
wavriffhead.fsize[0]=0xff&fsize;
|
|
wavriffhead.fsize[1]=0xff&(fsize>>8);
|
|
wavriffhead.fsize[2]=0xff&(fsize>>16);
|
|
wavriffhead.fsize[3]=0xff&(fsize>>24);
|
|
}
|
|
bcopy(WAV_RIFFHEAD_WAVESTR,wavriffhead.wavestr,4);
|
|
|
|
/* write WAVE RIFF header */
|
|
if (WRITE(outdes,&wavriffhead,sizeof(struct wav_riffhead_s))!=sizeof(struct wav_riffhead_s)){
|
|
PRINTF_ERR("cannot write WAVE RIFF header\n");
|
|
return -1;
|
|
}
|
|
|
|
/* create FMT chunk header */
|
|
bcopy(WAV_CHUNKHEAD_FMTSTR,wavchunkhead.typestr,4);
|
|
{
|
|
u_int csize;
|
|
|
|
csize=sizeof(struct wav_fmthead_s);
|
|
wavchunkhead.csize[0]=0xff&csize;
|
|
wavchunkhead.csize[1]=0xff&(csize>>8);
|
|
wavchunkhead.csize[2]=0xff&(csize>>16);
|
|
wavchunkhead.csize[3]=0xff&(csize>>24);
|
|
}
|
|
/* write chunk header */
|
|
if (WRITE(outdes,&wavchunkhead,sizeof(struct wav_chunkhead_s))!=sizeof(struct wav_chunkhead_s)){
|
|
PRINTF_ERR("cannot write WAVE FMT header\n");
|
|
return -1;
|
|
}
|
|
|
|
/* set FMT header */
|
|
wavfmthead.ftag[0]=0xff&WAV_HEAD_FTAG_PCM;
|
|
wavfmthead.ftag[1]=0xff&(WAV_HEAD_FTAG_PCM>>8);
|
|
wavfmthead.chnr[0]=0xff&chnr;
|
|
wavfmthead.chnr[1]=0xff&(chnr>>8);
|
|
wavfmthead.srate[0]=0xff&samprate;
|
|
wavfmthead.srate[1]=0xff&(samprate>>8);
|
|
wavfmthead.srate[2]=0xff&(samprate>>16);
|
|
wavfmthead.srate[3]=0xff&(samprate>>24);
|
|
drate=chnr*samprate*(bitnr/8); /* chnr!!! */
|
|
wavfmthead.drate[0]=0xff&drate;
|
|
wavfmthead.drate[1]=0xff&(drate>>8);
|
|
wavfmthead.drate[2]=0xff&(drate>>16);
|
|
wavfmthead.drate[3]=0xff&(drate>>24);
|
|
wavfmthead.balign[0]=0xff&((bitnr/8)*chnr); /* chnr!!! */
|
|
wavfmthead.balign[1]=0xff&(((bitnr/8)*chnr)>>8); /* chnr!!! */
|
|
wavfmthead.bitnr[0]=0xff&bitnr;
|
|
wavfmthead.bitnr[1]=0xff&(bitnr>>8);
|
|
|
|
/* write WAVE FMT header */
|
|
if (WRITE(outdes,&wavfmthead,sizeof(struct wav_fmthead_s))!=sizeof(struct wav_fmthead_s)){
|
|
PRINTF_ERR("cannot write WAVE FMT header\n");
|
|
return -1;
|
|
}
|
|
|
|
/* create WAVE DATA chunk header */
|
|
bcopy(WAV_CHUNKHEAD_DATASTR,wavchunkhead.typestr,4);
|
|
wavchunkhead.csize[0]=0xff&datasize;
|
|
wavchunkhead.csize[1]=0xff&(datasize>>8);
|
|
wavchunkhead.csize[2]=0xff&(datasize>>16);
|
|
wavchunkhead.csize[3]=0xff&(datasize>>24);
|
|
|
|
/* write chunk header */
|
|
if (WRITE(outdes,&wavchunkhead,sizeof(struct wav_chunkhead_s))!=sizeof(struct wav_chunkhead_s)){
|
|
PRINTF_ERR("cannot write WAVE DATA header\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
wav_read_head(int indes,
|
|
u_int *bcountp,
|
|
u_int *datasizep,u_int *chnrp,u_int *sampratep,u_int *bitnrp,
|
|
u_int *extrasizep,
|
|
char **errstrp)
|
|
{
|
|
struct wav_riffhead_s wavriffhead;
|
|
struct wav_chunkhead_s wavchunkhead;
|
|
struct wav_fmthead_s wavfmthead;
|
|
u_int i;
|
|
u_int bcount;
|
|
u_int remain;
|
|
u_int csize;
|
|
u_int chnr;
|
|
u_int samprate;
|
|
u_int bitnr;
|
|
|
|
bcount=0; /* no bytes read yet */
|
|
|
|
/* read WAVE RIFF header */
|
|
if (READ(indes,(char *)&wavriffhead,sizeof(struct wav_riffhead_s))!=sizeof(struct wav_riffhead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="read WAVE RIFF header";
|
|
}
|
|
return -1;
|
|
}
|
|
bcount+=sizeof(struct wav_riffhead_s);
|
|
|
|
/* check riffstr */
|
|
for (i=0;i<4;i++){
|
|
if (WAV_RIFFHEAD_RIFFSTR[i]!=wavriffhead.riffstr[i]){
|
|
if (errstrp!=NULL){
|
|
*errstrp="no RIFF";
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* determine remaining file size */
|
|
remain=wavriffhead.fsize[0]
|
|
+(wavriffhead.fsize[1]<<8)
|
|
+(wavriffhead.fsize[2]<<16)
|
|
+(wavriffhead.fsize[3]<<24);
|
|
if (remain<sizeof(struct wav_riffhead_s)-sizeof(struct wav_chunkhead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid file size";
|
|
}
|
|
return -1;
|
|
}
|
|
remain-=sizeof(struct wav_riffhead_s)-sizeof(struct wav_chunkhead_s);
|
|
|
|
/* check wavestr */
|
|
for (i=0;i<4;i++){
|
|
if (WAV_RIFFHEAD_WAVESTR[i]!=wavriffhead.wavestr[i]){
|
|
if (errstrp!=NULL){
|
|
*errstrp="no WAVE";
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* search for fmt chunk */
|
|
for (;;){
|
|
/* read next chunk */
|
|
if (remain<sizeof(struct wav_chunkhead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid file size";
|
|
}
|
|
return -1;
|
|
}
|
|
if (READ(indes,(char *)&wavchunkhead,sizeof(struct wav_chunkhead_s))!=sizeof(struct wav_chunkhead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="read WAVE FMT header";
|
|
}
|
|
return -1;
|
|
}
|
|
bcount+=sizeof(struct wav_chunkhead_s);
|
|
remain-=sizeof(struct wav_chunkhead_s);
|
|
|
|
/* get csize */
|
|
csize=wavchunkhead.csize[0]
|
|
+(wavchunkhead.csize[1]<<8)
|
|
+(wavchunkhead.csize[2]<<16)
|
|
+(wavchunkhead.csize[3]<<24);
|
|
if (remain<csize){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid file size";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
for (i=0;i<4;i++){
|
|
if (WAV_CHUNKHEAD_FMTSTR[i]!=wavchunkhead.typestr[i]){
|
|
break; /* not fmt chunk */
|
|
}
|
|
}
|
|
if (i==4){
|
|
break; /* found fmt */
|
|
}
|
|
|
|
/* skip rest of chunk */
|
|
if (LSEEK64(indes,(OFF64_T)csize,SEEK_CUR)<0){
|
|
if (errstrp!=NULL){
|
|
*errstrp="lseek WAVE FMT header";
|
|
}
|
|
return -1;
|
|
}
|
|
bcount+=csize;
|
|
remain-=csize;
|
|
}
|
|
if (csize!=sizeof(struct wav_fmthead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid FMT csize";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* read rest of fmt chunk */
|
|
if (READ(indes,(char *)&wavfmthead,sizeof(struct wav_fmthead_s))!=sizeof(struct wav_fmthead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="read WAVE FMT header";
|
|
}
|
|
return -1;
|
|
}
|
|
bcount+=sizeof(struct wav_fmthead_s);
|
|
remain-=sizeof(struct wav_fmthead_s);
|
|
|
|
/* check ftag */
|
|
/* XXX allow only PCM */
|
|
if ((wavfmthead.ftag[0]!=(0xff&WAV_HEAD_FTAG_PCM))
|
|
||(wavfmthead.ftag[1]!=(0xff&(WAV_HEAD_FTAG_PCM>>8)))){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid ftag, must be PCM format";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* get chnr */
|
|
chnr=wavfmthead.chnr[0]
|
|
+(wavfmthead.chnr[1]<<8);
|
|
if (chnr==0){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid chnr";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* set samprate */
|
|
samprate=wavfmthead.srate[0]
|
|
+(wavfmthead.srate[1]<<8)
|
|
+(wavfmthead.srate[2]<<16)
|
|
+(wavfmthead.srate[3]<<24);
|
|
|
|
/* XXX check drate: must be chnr*samprate*(bitnr/8) */
|
|
|
|
/* get bitnr */
|
|
bitnr=wavfmthead.bitnr[0]
|
|
+(wavfmthead.bitnr[1]<<8);
|
|
if (bitnr==0){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid bitnr";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* XXX check balign: must be (bitnr/8)*chnr */
|
|
|
|
/* search for data chunk */
|
|
for (;;){
|
|
/* read next chunk */
|
|
if (remain<sizeof(struct wav_chunkhead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid file size";
|
|
}
|
|
return -1;
|
|
}
|
|
if (READ(indes,(char *)&wavchunkhead,sizeof(struct wav_chunkhead_s))!=sizeof(struct wav_chunkhead_s)){
|
|
if (errstrp!=NULL){
|
|
*errstrp="read WAVE DATA header";
|
|
}
|
|
return -1;
|
|
}
|
|
bcount+=sizeof(struct wav_chunkhead_s);
|
|
remain-=sizeof(struct wav_chunkhead_s);
|
|
|
|
/* get csize */
|
|
csize=wavchunkhead.csize[0]
|
|
+(wavchunkhead.csize[1]<<8)
|
|
+(wavchunkhead.csize[2]<<16)
|
|
+(wavchunkhead.csize[3]<<24);
|
|
if (remain<csize){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid file size";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
for (i=0;i<4;i++){
|
|
if (WAV_CHUNKHEAD_DATASTR[i]!=wavchunkhead.typestr[i]){
|
|
break; /* not data chunk */
|
|
}
|
|
}
|
|
if (i==4){
|
|
break; /* found fmt */
|
|
}
|
|
|
|
/* skip rest of chunk */
|
|
if (LSEEK64(indes,(OFF64_T)csize,SEEK_CUR)<0){
|
|
if (errstrp!=NULL){
|
|
*errstrp="lseek WAVE DATA header";
|
|
}
|
|
return -1;
|
|
}
|
|
bcount+=csize;
|
|
remain-=csize;
|
|
}
|
|
if (csize%chnr!=0){
|
|
if (errstrp!=NULL){
|
|
*errstrp="invalid DATA csize";
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (bcountp!=NULL){
|
|
*bcountp=bcount;
|
|
}
|
|
if (datasizep!=NULL){
|
|
*datasizep=csize;
|
|
}
|
|
if (chnrp!=NULL){
|
|
*chnrp=chnr;
|
|
}
|
|
if (sampratep!=NULL){
|
|
*sampratep=samprate;
|
|
}
|
|
if (bitnrp!=NULL){
|
|
*bitnrp=bitnr;
|
|
}
|
|
if (extrasizep!=NULL){
|
|
*extrasizep=remain-csize;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
int
|
|
wav_find_akaihead(int indes,u_int *bcountp,u_int *csizep,u_int remain,u_int searchtype)
|
|
{
|
|
struct wav_chunkhead_s wavchunkhead;
|
|
u_int bcount;
|
|
u_int csize;
|
|
u_int type;
|
|
u_int i;
|
|
|
|
bcount=0; /* no bytes read yet */
|
|
csize=0; /* no chunk yet */
|
|
type=WAV_AKAIHEADTYPE_NONE; /* no AKAI header chunk found yet */
|
|
|
|
/* search for AKAI header chunk */
|
|
for (;;){
|
|
/* read next chunk */
|
|
if (remain<sizeof(struct wav_chunkhead_s)){
|
|
break;
|
|
}
|
|
if (READ(indes,(char *)&wavchunkhead,sizeof(struct wav_chunkhead_s))<0){
|
|
PRINTF_ERR("cannot read WAV chunk");
|
|
return -1;
|
|
}
|
|
bcount+=sizeof(struct wav_chunkhead_s);
|
|
remain-=sizeof(struct wav_chunkhead_s);
|
|
|
|
/* get csize */
|
|
csize=wavchunkhead.csize[0]
|
|
+(wavchunkhead.csize[1]<<8)
|
|
+(wavchunkhead.csize[2]<<16)
|
|
+(wavchunkhead.csize[3]<<24);
|
|
if (remain<csize){
|
|
break;
|
|
}
|
|
|
|
if ((searchtype==WAV_AKAIHEADTYPE_NONE)||(searchtype==WAV_AKAIHEADTYPE_SAMPLE900)){
|
|
/* check if S900 sample header chunk */
|
|
for (i=0;i<4;i++){
|
|
if (WAV_CHUNKHEAD_AKAIS900SAMPLEHEADSTR[i]!=wavchunkhead.typestr[i]){
|
|
break; /* not data chunk */
|
|
}
|
|
}
|
|
if (i==4){
|
|
/* found */
|
|
type=WAV_AKAIHEADTYPE_SAMPLE900;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((searchtype==WAV_AKAIHEADTYPE_NONE)||(searchtype==WAV_AKAIHEADTYPE_SAMPLE1000)){
|
|
/* check if S1000 sample header chunk */
|
|
for (i=0;i<4;i++){
|
|
if (WAV_CHUNKHEAD_AKAIS1000SAMPLEHEADSTR[i]!=wavchunkhead.typestr[i]){
|
|
break; /* not data chunk */
|
|
}
|
|
}
|
|
if (i==4){
|
|
/* found */
|
|
type=WAV_AKAIHEADTYPE_SAMPLE1000;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((searchtype==WAV_AKAIHEADTYPE_NONE)||(searchtype==WAV_AKAIHEADTYPE_SAMPLE3000)){
|
|
/* check if S3000 sample header chunk */
|
|
for (i=0;i<4;i++){
|
|
if (WAV_CHUNKHEAD_AKAIS3000SAMPLEHEADSTR[i]!=wavchunkhead.typestr[i]){
|
|
break; /* not data chunk */
|
|
}
|
|
}
|
|
if (i==4){
|
|
/* found */
|
|
type=WAV_AKAIHEADTYPE_SAMPLE3000;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((searchtype==WAV_AKAIHEADTYPE_NONE)||(searchtype==WAV_AKAIHEADTYPE_DDTAKE)){
|
|
/* check if DD take header chunk */
|
|
for (i=0;i<4;i++){
|
|
if (WAV_CHUNKHEAD_AKAIDDTAKEHEADSTR[i]!=wavchunkhead.typestr[i]){
|
|
break; /* not data chunk */
|
|
}
|
|
}
|
|
if (i==4){
|
|
/* found */
|
|
type=WAV_AKAIHEADTYPE_DDTAKE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* skip rest of chunk */
|
|
if (LSEEK64(indes,(OFF64_T)csize,SEEK_CUR)<0){
|
|
PERROR("cannot lseek WAV chunk");
|
|
return -1;
|
|
}
|
|
bcount+=csize;
|
|
remain-=csize;
|
|
}
|
|
if (type==WAV_AKAIHEADTYPE_NONE){ /* no AKAI header chunk found? */
|
|
csize=0;
|
|
}
|
|
|
|
if (bcountp!=NULL){
|
|
*bcountp=bcount;
|
|
}
|
|
if (csizep!=NULL){
|
|
*csizep=csize;
|
|
}
|
|
return (int)type;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/* EOF */
|