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
1041 lines
28 KiB
C
Executable File
1041 lines
28 KiB
C
Executable File
/*
|
|
* Copyright (C) 2012,2018,2019,2020,2021 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_io.h"
|
|
#include "akaiutil.h"
|
|
#include "akaiutil_take.h"
|
|
#include "akaiutil_wav.h"
|
|
|
|
|
|
|
|
int
|
|
akai_ddtake_info(struct part_s *pp,u_int ti,int verbose)
|
|
{
|
|
char namebuf[DIRNAMEBUF_LEN+1]; /* +1 for '\0' */
|
|
struct akai_ddtake_s *tp;
|
|
u_int cstarts,cstarte;
|
|
u_int csizes,csizee;
|
|
u_int scount;
|
|
u_int srate;
|
|
double vspeed;
|
|
|
|
if ((pp==NULL)||(!pp->valid)){
|
|
return -1;
|
|
}
|
|
if (pp->type!=PART_TYPE_DD){
|
|
return -1;
|
|
}
|
|
|
|
if (ti>=AKAI_DDTAKE_MAXNUM){
|
|
if (verbose){
|
|
PRINTF_ERR("invalid take index\n");
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
tp=&pp->head.dd.take[ti];
|
|
|
|
if (tp->stat==AKAI_DDTAKESTAT_FREE){ /* free? */
|
|
if (verbose){
|
|
PRINTF_ERR("take not found\n");
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* clusters */
|
|
cstarts=(tp->cstarts[1]<<8)
|
|
+tp->cstarts[0];
|
|
cstarte=(tp->cstarte[1]<<8)
|
|
+tp->cstarte[0];
|
|
|
|
/* determine csizes (sample clusters) and csizee (envelope clusters) */
|
|
if (cstarts!=0){ /* sample not empty? */
|
|
csizes=akai_count_ddfatchain(pp,cstarts);
|
|
}else{
|
|
csizes=0;
|
|
}
|
|
if (cstarte!=0){ /* envelope not empty? */
|
|
csizee=akai_count_ddfatchain(pp,cstarte);
|
|
}else{
|
|
csizee=0;
|
|
}
|
|
|
|
/* used sample count */
|
|
scount=(tp->wend[3]<<24)
|
|
+(tp->wend[2]<<16)
|
|
+(tp->wend[1]<<8)
|
|
+tp->wend[0]
|
|
-(tp->wstart[3]<<24)
|
|
-(tp->wstart[2]<<16)
|
|
-(tp->wstart[1]<<8)
|
|
-tp->wstart[0];
|
|
/* XXX no check if scount is compatible with csizes */
|
|
|
|
/* samplerate */
|
|
srate=(tp->srate[1]<<8)
|
|
+tp->srate[0];
|
|
|
|
/* name */
|
|
akai2ascii_name(tp->name,namebuf,0); /* 0: not S900 */
|
|
|
|
if (verbose){
|
|
PRINTF_OUT("%s%s\n",namebuf,AKAI_DDTAKE_FNAMEEND);
|
|
PRINTF_OUT("tnr: %u\n",ti+1);
|
|
PRINTF_OUT("tname: \"%s\"\n",namebuf);
|
|
PRINTF_OUT("cstarts: cluster 0x%04x\n",cstarts);
|
|
PRINTF_OUT("cstarte: cluster 0x%04x\n",cstarte);
|
|
PRINTF_OUT("csizes: 0x%04x clusters (%9u bytes)\n",
|
|
csizes,
|
|
csizes*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE);
|
|
PRINTF_OUT("csizee: 0x%04x clusters (%9u bytes)\n",
|
|
csizee,
|
|
csizee*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE);
|
|
PRINTF_OUT("scount: 0x%08x (%9u)\n",scount,scount);
|
|
PRINTF_OUT("stype: %s\n",(tp->stype==AKAI_DDTAKESTYPE_MONO)?"MONO":"STEREO");
|
|
PRINTF_OUT("srate: %uHz\n",srate);
|
|
vspeed=((double)((((int)(char)tp->vspeed[1])<<8)+((int)(u_char)tp->vspeed[0])))/256.0; /* in % */
|
|
PRINTF_OUT("vspeed: %+06.2lf%%\n",vspeed);
|
|
PRINTF_OUT("finerate: %+05ippm\n",(((int)(char)tp->finerate[1])<<8)+((int)(u_char)tp->finerate[0]));
|
|
PRINTF_OUT("wstart: 0x%02x%02x%02x%02x\n",tp->wstart[3],tp->wstart[2],tp->wstart[1],tp->wstart[0]);
|
|
PRINTF_OUT("wend: 0x%02x%02x%02x%02x\n",tp->wend[3],tp->wend[2],tp->wend[1],tp->wend[0]);
|
|
PRINTF_OUT("wstartm: 0x%02x%02x%02x%02x\n",tp->wstartm[3],tp->wstartm[2],tp->wstartm[1],tp->wstartm[0]);
|
|
PRINTF_OUT("wendm: 0x%02x%02x%02x%02x\n",tp->wendm[3],tp->wendm[2],tp->wendm[1],tp->wendm[0]);
|
|
PRINTF_OUT("fadein: %ums\n",(tp->fadein[1]<<8)+tp->fadein[0]);
|
|
PRINTF_OUT("fadeout: %ums\n",(tp->fadeout[1]<<8)+tp->fadeout[0]);
|
|
PRINTF_OUT("stlvl: %u\n",tp->stlvl);
|
|
PRINTF_OUT("pan: %+i\n",(int)(char)tp->pan);
|
|
PRINTF_OUT("stmix: %s\n",tp->stmix?"ON":"OFF");
|
|
PRINTF_OUT("midich: %u\n",tp->midich1+1);
|
|
PRINTF_OUT("midinote: %u\n",tp->midinote);
|
|
PRINTF_OUT("startm: ");
|
|
switch (tp->startm){
|
|
case 0x00:
|
|
PRINTF_OUT("IMMEDIATE\n");
|
|
break;
|
|
case 0x01:
|
|
PRINTF_OUT("MIDI FOOTSW\n");
|
|
break;
|
|
case 0x02:
|
|
PRINTF_OUT("MIDI NOTE\n");
|
|
break;
|
|
case 0x03:
|
|
PRINTF_OUT("M.NOTE+DEL\n");
|
|
break;
|
|
case 0x04:
|
|
PRINTF_OUT("START SONG\n");
|
|
break;
|
|
default:
|
|
PRINTF_OUT("\?\?\?\n");
|
|
break;
|
|
}
|
|
PRINTF_OUT("deemph: %s\n",tp->deemph?"ON":"OFF");
|
|
PRINTF_OUT("predel: %u\n",(tp->predel[1]<<8)+tp->predel[0]);
|
|
PRINTF_OUT("outlvl: %u\n",tp->outlvl);
|
|
PRINTF_OUT("outch: ");
|
|
switch (tp->outch){
|
|
case 0x00:
|
|
PRINTF_OUT("OFF\n");
|
|
break;
|
|
case 0x01:
|
|
PRINTF_OUT("1/2\n");
|
|
break;
|
|
case 0x02:
|
|
PRINTF_OUT("3/4\n");
|
|
break;
|
|
case 0x03:
|
|
PRINTF_OUT("5/6\n");
|
|
break;
|
|
case 0x04:
|
|
PRINTF_OUT("7/8\n");
|
|
break;
|
|
default:
|
|
PRINTF_OUT("\?\?\?\n");
|
|
break;
|
|
}
|
|
PRINTF_OUT("fxbus: ");
|
|
switch (tp->fxbus){
|
|
case 0x00:
|
|
PRINTF_OUT("OFF\n");
|
|
break;
|
|
case 0x01:
|
|
PRINTF_OUT("FX1\n");
|
|
break;
|
|
case 0x02:
|
|
PRINTF_OUT("FX2\n");
|
|
break;
|
|
case 0x03:
|
|
PRINTF_OUT("RV3\n");
|
|
break;
|
|
case 0x04:
|
|
PRINTF_OUT("RV4\n");
|
|
break;
|
|
default:
|
|
PRINTF_OUT("\?\?\?\n");
|
|
break;
|
|
}
|
|
PRINTF_OUT("sendlvl: %u\n",tp->sendlvl);
|
|
}else{
|
|
PRINTF_OUT("%3u %-12s 0x%04x 0x%04x 0x%04x 0x%04x %9u %5u %s\n",
|
|
ti+1,namebuf,
|
|
cstarts,cstarte,
|
|
csizes,csizee,
|
|
scount,
|
|
srate,
|
|
(tp->stype==AKAI_DDTAKESTYPE_MONO)?"MONO ":"STEREO");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
akai_import_take(int inpfd,struct part_s *pp,struct akai_ddtake_s *tp,u_int ti,u_int csizes,u_int csizee)
|
|
{
|
|
u_int cstarts,cstarte;
|
|
u_int samplesize;
|
|
u_int envsiz;
|
|
u_char *envbuf;
|
|
u_int bsize;
|
|
int ret;
|
|
|
|
if ((pp==NULL)||(!pp->valid)||(pp->fat==NULL)){
|
|
return -1;
|
|
}
|
|
if (pp->type!=PART_TYPE_DD){
|
|
return -1;
|
|
}
|
|
|
|
if (tp==NULL){
|
|
return -1;
|
|
}
|
|
|
|
if (ti>=AKAI_DDTAKE_MAXNUM){
|
|
return -1;
|
|
}
|
|
|
|
/* Note: don't check if name already used, create new DD take */
|
|
|
|
ret=-1; /* no success so far */
|
|
envsiz=0;
|
|
envbuf=NULL; /* not allocated yet */
|
|
|
|
/* total number of bytes for sample */
|
|
samplesize=csizes*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE;
|
|
|
|
if ((csizee==0) /* no envelope in file? */
|
|
&&(samplesize>0)){ /* and sample not empty? */
|
|
/* new envelope */
|
|
envsiz=((samplesize/2)+AKAI_DDTAKE_ENVBLKSIZW-1)/AKAI_DDTAKE_ENVBLKSIZW; /* number of bytes for envelope, round up (Note: /2 for 16bit per sample word) */
|
|
csizee=(envsiz+AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE-1)/(AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE); /* in clusters, round up */
|
|
/* allocate envelope buffer */
|
|
if ((envbuf=(u_char *)malloc(envsiz))==NULL){
|
|
PRINTF_ERR("cannot allocate envelope buffer\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
}
|
|
|
|
if ((csizes+csizee)*AKAI_DDPART_CBLKS>pp->bfree){
|
|
PRINTF_ERR("not enough space left\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
/* allocate space for take */
|
|
if (csizes>0){
|
|
/* sample */
|
|
#if 1
|
|
if (akai_allocate_ddfatchain(pp,csizes,&cstarts,1)<0){ /* 1: not necessarily contiguous */
|
|
#else
|
|
if (akai_allocate_ddfatchain(pp,csizes,&cstarts,csizes)<0){ /* contiguous */
|
|
#endif
|
|
PRINTF_ERR("cannot allocate FAT chain for sample\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
}else{
|
|
cstarts=0; /* empty sample */
|
|
}
|
|
if (csizee>0){
|
|
/* envelope */
|
|
#if 1
|
|
if (akai_allocate_ddfatchain(pp,csizee,&cstarte,1)<0){ /* 1: not necessarily contiguous */
|
|
#else
|
|
if (akai_allocate_ddfatchain(pp,csizee,&cstarte,csizee)<0){ /* contiguous */
|
|
#endif
|
|
PRINTF_ERR("cannot allocate FAT chain for envelope\n");
|
|
if (csizes>0){
|
|
/* free FAT at cstarts */
|
|
akai_free_ddfatchain(pp,cstarts,1); /* XXX ignore error */
|
|
goto akai_import_take_exit;
|
|
}
|
|
}
|
|
}else{
|
|
cstarte=0; /* empty envelope */
|
|
}
|
|
|
|
/* fundamental settings of DD take */
|
|
tp->stat=AKAI_DDTAKESTAT_USED; /* used */
|
|
/* set cstarts and cstarte in DD take header */
|
|
tp->cstarts[1]=0xff&(cstarts>>8);
|
|
tp->cstarts[0]=0xff&cstarts;
|
|
tp->cstarte[1]=0xff&(cstarte>>8);
|
|
tp->cstarte[0]=0xff&cstarte;
|
|
/* XXX keep name */
|
|
/* copy DD take header into directory entry */
|
|
bcopy(tp,&pp->head.dd.take[ti],sizeof(struct akai_ddtake_s));
|
|
/* write partition header */
|
|
if (akai_io_blks(pp,(u_char *)&pp->head.dd,
|
|
0,
|
|
AKAI_DDPARTHEAD_BLKS,
|
|
1,IO_BLKS_WRITE)<0){ /* 1: allocate cache if possible */
|
|
PRINTF_ERR("cannot write partition header\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
|
|
if (csizes>0){
|
|
/* sample */
|
|
if (akai_import_ddfatchain(pp,cstarts,0,samplesize,inpfd,NULL)<0){
|
|
PRINTF_ERR("cannot import DD take\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
}
|
|
|
|
if (csizee>0){
|
|
/* envelope */
|
|
if (envbuf!=NULL){ /* no envelope in file? (see above) */
|
|
/* calculate envelope from sample */
|
|
if (akai_take_setenv(pp,cstarts,samplesize,envbuf,envsiz)<0){
|
|
goto akai_import_take_exit;
|
|
}
|
|
/* write envelope to DD take */
|
|
if (akai_import_ddfatchain(pp,cstarte,0,envsiz,-1,envbuf)<0){
|
|
PRINTF_ERR("cannot save envelope\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
}else{
|
|
/* import envelope from file */
|
|
bsize=csizee*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE;
|
|
if (akai_import_ddfatchain(pp,cstarte,0,bsize,inpfd,NULL)<0){
|
|
PRINTF_ERR("cannot import DD take\n");
|
|
goto akai_import_take_exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret=0; /* success */
|
|
|
|
akai_import_take_exit:
|
|
if (envbuf!=NULL){
|
|
free(envbuf);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
akai_export_take(int outfd,struct part_s *pp,struct akai_ddtake_s *tp,u_int csizes,u_int csizee,u_int cstarts,u_int cstarte)
|
|
{
|
|
u_int bsize;
|
|
|
|
if ((pp==NULL)||(!pp->valid)||(pp->fat==NULL)){
|
|
return -1;
|
|
}
|
|
if (pp->type!=PART_TYPE_DD){
|
|
return -1;
|
|
}
|
|
|
|
if (tp!=NULL){
|
|
/* export DD take header */
|
|
if (WRITE(outfd,tp,sizeof(struct akai_ddtake_s))!=(int)sizeof(struct akai_ddtake_s)){
|
|
PRINTF_ERR("cannot export DD take header\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (csizes>0){
|
|
/* sample */
|
|
bsize=csizes*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE; /* in bytes */
|
|
if (akai_export_ddfatchain(pp,cstarts,0,bsize,outfd,NULL)<0){
|
|
PRINTF_ERR("cannot export DD take sample\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (csizee>0){
|
|
/* envelope */
|
|
bsize=csizee*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE; /* in bytes */
|
|
if (akai_export_ddfatchain(pp,cstarte,0,bsize,outfd,NULL)<0){
|
|
PRINTF_ERR("cannot export DD take envelope\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
akai_take2wav(struct part_s *pp,u_int ti,int wavfd,u_int *sizep,char **wavnamep,int what)
|
|
{
|
|
/* Note: static for multiple calls with different what */
|
|
static u_int cstarts;
|
|
static u_int samplestart;
|
|
static u_int samplesize;
|
|
static u_int samplerate;
|
|
static u_int samplechnr;
|
|
static int ret;
|
|
static char wavname[AKAI_NAME_LEN+4+1]; /* name (ASCII), +4 for ".<type>", +1 for '\0' */
|
|
static u_int nlen;
|
|
|
|
if ((pp==NULL)||(!pp->valid)||(pp->fat==NULL)){
|
|
return -1;
|
|
}
|
|
if (pp->type!=PART_TYPE_DD){
|
|
return -1;
|
|
}
|
|
|
|
if (ti>=AKAI_DDTAKE_MAXNUM){
|
|
return -1;
|
|
}
|
|
|
|
ret=-1; /* no success so far */
|
|
|
|
if (what&TAKE2WAV_CHECK){
|
|
|
|
if (pp->head.dd.take[ti].stat==AKAI_DDTAKESTAT_FREE){ /* free? */
|
|
return -1;
|
|
}
|
|
|
|
/* sample start cluster */
|
|
cstarts=(pp->head.dd.take[ti].cstarts[1]<<8)
|
|
+pp->head.dd.take[ti].cstarts[0];
|
|
if (cstarts!=0){ /* sample not empty? */
|
|
#if 1
|
|
/* Note: words are 16bit */
|
|
/* start word */
|
|
samplestart=(pp->head.dd.take[ti].wstart[3]<<24)
|
|
+(pp->head.dd.take[ti].wstart[2]<<16)
|
|
+(pp->head.dd.take[ti].wstart[1]<<8)
|
|
+pp->head.dd.take[ti].wstart[0];
|
|
/* end word */
|
|
samplesize=(pp->head.dd.take[ti].wend[3]<<24)
|
|
+(pp->head.dd.take[ti].wend[2]<<16)
|
|
+(pp->head.dd.take[ti].wend[1]<<8)
|
|
+pp->head.dd.take[ti].wend[0];
|
|
if (samplesize<samplestart){ /* end<start? */
|
|
return -1;
|
|
}
|
|
samplesize-=samplestart; /* size in words */
|
|
samplestart*=2; /* *2 for 16bit per sample word */
|
|
samplesize*=2; /* *2 for 16bit per sample word */
|
|
/* XXX check samplestart and samplesize */
|
|
#else
|
|
/* all clusters */
|
|
samplestart=0;
|
|
samplesize=akai_count_ddfatchain(pp,cstarts)*AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE; /* in bytes */
|
|
#endif
|
|
}else{
|
|
samplestart=0;
|
|
samplesize=0;
|
|
}
|
|
/* Note: samplesize==0 is allowed here */
|
|
|
|
/* take parameters */
|
|
samplerate=(pp->head.dd.take[ti].srate[1]<<8)
|
|
+pp->head.dd.take[ti].srate[0];
|
|
samplechnr=(pp->head.dd.take[ti].stype==AKAI_DDTAKESTYPE_MONO)?1:2;
|
|
|
|
#ifdef DEBUG
|
|
PRINTF_OUT("samplestart: %15u bytes\n",samplestart);
|
|
PRINTF_OUT("samplesize: %15u bytes\n",samplesize);
|
|
PRINTF_OUT("samplerate: %15u Hz\n",samplerate);
|
|
PRINTF_OUT("samplechnr: %15u\n",samplechnr);
|
|
#endif
|
|
|
|
if (sizep!=NULL){
|
|
/* WAV file size */
|
|
*sizep=WAV_HEAD_SIZE+samplesize;
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
*sizep+=sizeof(struct wav_chunkhead_s)+sizeof(struct akai_ddtake_s); /* DD take header chunk (see below) */
|
|
#endif
|
|
}
|
|
|
|
/* create WAV name */
|
|
akai2ascii_name(pp->head.dd.take[ti].name,wavname,0); /* 0: not S900 */
|
|
nlen=(u_int)strlen(wavname);
|
|
bcopy(".wav",wavname+nlen,5);
|
|
|
|
if (wavnamep!=NULL){
|
|
/* pointer to name (wavname must be static!) */
|
|
*wavnamep=wavname;
|
|
}
|
|
}
|
|
|
|
if (what&TAKE2WAV_EXPORT){
|
|
|
|
if (what&TAKE2WAV_CREATE){
|
|
/* create WAV file */
|
|
if ((wavfd=OPEN(wavname,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,0666))<0){
|
|
PERROR("create WAV");
|
|
goto akai_take2wav_exit;
|
|
}
|
|
}
|
|
|
|
/* write WAV header */
|
|
if (wav_write_head(wavfd,
|
|
samplesize,samplechnr,samplerate,16, /* 16: 16bit */
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
sizeof(struct wav_chunkhead_s)+sizeof(struct akai_ddtake_s) /* DD take header chunk (see below) */
|
|
#else
|
|
0
|
|
#endif
|
|
)<0){
|
|
PRINTF_ERR("cannot write WAV header\n");
|
|
goto akai_take2wav_exit;
|
|
}
|
|
|
|
if (samplesize>0){
|
|
/* Note: no sample format conversion necessary */
|
|
/* export sample */
|
|
if (akai_export_ddfatchain(pp,cstarts,samplestart,samplesize,wavfd,NULL)<0){
|
|
PERROR("write WAV samples");
|
|
goto akai_take2wav_exit;
|
|
}
|
|
}
|
|
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
{
|
|
struct wav_chunkhead_s wavchunkhead;
|
|
struct akai_ddtake_s t;
|
|
u_int hdrsize;
|
|
u_int csizes;
|
|
|
|
/* create DD take header chunk */
|
|
bcopy(WAV_CHUNKHEAD_AKAIDDTAKEHEADSTR,wavchunkhead.typestr,4);
|
|
hdrsize=sizeof(struct akai_ddtake_s);
|
|
wavchunkhead.csize[0]=0xff&hdrsize;
|
|
wavchunkhead.csize[1]=0xff&(hdrsize>>8);
|
|
wavchunkhead.csize[2]=0xff&(hdrsize>>16);
|
|
wavchunkhead.csize[3]=0xff&(hdrsize>>24);
|
|
/* write WAV chunk header */
|
|
if (WRITE(wavfd,(u_char *)&wavchunkhead,sizeof(struct wav_chunkhead_s))!=(int)sizeof(struct wav_chunkhead_s)){
|
|
PERROR("write WAV chunk");
|
|
goto akai_take2wav_exit;
|
|
}
|
|
/* get DD take header from directory entry */
|
|
bcopy((u_char *)&pp->head.dd.take[ti],&t,hdrsize);
|
|
/* determine csizes (sample clusters) and csizee (envelope clusters) */
|
|
csizes=akai_count_ddfatchain(pp,cstarts);
|
|
/* Note: no envelope in WAV file -> csizee=0 */
|
|
/* XXX use cstarts/cstarte fields in DD take header for csizes/csizee */
|
|
t.cstarts[1]=0xff&(csizes>>8);
|
|
t.cstarts[0]=0xff&csizes;
|
|
t.cstarte[1]=0x00;
|
|
t.cstarte[0]=0x00;
|
|
/* write DD take header */
|
|
if (WRITE(wavfd,(u_char *)&t,hdrsize)!=(int)hdrsize){
|
|
PERROR("write WAV chunk");
|
|
goto akai_take2wav_exit;
|
|
}
|
|
}
|
|
#endif
|
|
#if 1
|
|
PRINTF_OUT("DD take exported to WAV\n");
|
|
#endif
|
|
}
|
|
|
|
ret=0; /* success */
|
|
|
|
akai_take2wav_exit:
|
|
if (what&TAKE2WAV_CREATE){
|
|
if (wavfd>=0){
|
|
CLOSE(wavfd);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
akai_wav2take(int wavfd,char *wavname,struct part_s *pp,u_int ti,u_int *bcountp,int what)
|
|
{
|
|
/* Note: static for multiple calls with different what */
|
|
static struct akai_ddtake_s t;
|
|
static u_int samplerate;
|
|
static u_int samplesize;
|
|
static u_int wavchnr;
|
|
static u_int wavbitnr;
|
|
static u_int wavsamplesize;
|
|
static u_int wavsamplesizealloc;
|
|
static u_int wavsamplecount;
|
|
static u_char *wavbuf;
|
|
static char *errstrp;
|
|
static u_int nlen;
|
|
static char name[AKAI_NAME_LEN+1]; /* name (ASCII), +1 for '\0' */
|
|
static u_int cstarts,cstarte;
|
|
static u_int csizes,csizee;
|
|
static u_int envsiz;
|
|
static u_char *envbuf;
|
|
static u_int bcount;
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
static u_int extrasize;
|
|
static int wavakaiheadfound;
|
|
#endif
|
|
static u_int i,j;
|
|
static int ret;
|
|
|
|
if (bcountp!=NULL){
|
|
*bcountp=0; /* no bytes read yet */
|
|
}
|
|
|
|
if ((pp==NULL)||(!pp->valid)||(pp->fat==NULL)){
|
|
return -1;
|
|
}
|
|
if (pp->type!=PART_TYPE_DD){
|
|
return -1;
|
|
}
|
|
|
|
if (ti>=AKAI_DDTAKE_MAXNUM){
|
|
return -1;
|
|
}
|
|
|
|
/* check WAV name */
|
|
if (wavname==NULL){
|
|
return -1;
|
|
}
|
|
nlen=(u_int)strlen(wavname);
|
|
if ((nlen<4)||(strncasecmp(wavname+nlen-4,".wav",4)!=0)){
|
|
/* not a WAV file */
|
|
return 1; /* no error */
|
|
}
|
|
nlen-=4; /* remove suffix */
|
|
|
|
/* Note: don't check if name already used, create new DD take */
|
|
|
|
ret=-1; /* no success so far */
|
|
bcount=0; /* no bytes read yet */
|
|
envbuf=NULL; /* not allocated yet */
|
|
wavbuf=NULL; /* not allocated yet */
|
|
|
|
if (what&WAV2TAKE_OPEN){
|
|
/* open external WAV file */
|
|
if ((wavfd=akai_openreadonly_extfile(wavname))<0){
|
|
PERROR("open WAV");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
}
|
|
|
|
/* read and parse WAV header */
|
|
if (wav_read_head(wavfd,&bcount,
|
|
&wavsamplesize,&wavchnr,&samplerate,&wavbitnr,
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
&extrasize,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
&errstrp)<0){
|
|
if (errstrp!=NULL){
|
|
PRINTF_ERR("%s\n",errstrp);
|
|
}
|
|
/* error, don't know how many bytes read */
|
|
goto akai_wav2take_exit;
|
|
}
|
|
|
|
/* check parameters */
|
|
if ((wavchnr!=1)&&(wavchnr!=2)){
|
|
PRINTF_ERR("invalid number of channels, must be mono or stereo\n");
|
|
/* unknown or unsupported */
|
|
ret=1; /* no error */
|
|
goto akai_wav2take_exit;
|
|
}
|
|
if ((wavbitnr!=8)&&(wavbitnr!=16)&&(wavbitnr!=24)&&(wavbitnr!=32)){
|
|
PRINTF_ERR("invalid WAV format, must be 8bit or 16bit or 24bit or 32bit PCM\n");
|
|
/* unknown or unsupported */
|
|
ret=1; /* no error */
|
|
goto akai_wav2take_exit;
|
|
}
|
|
|
|
/* number of samples in WAV file */
|
|
/* Note: sum over all channels */
|
|
wavsamplecount=wavsamplesize/(wavbitnr/8);
|
|
#if 0
|
|
if (wavsamplecount==0){
|
|
PRINTF_ERR("no samples in WAV file\n");
|
|
ret=1; /* no error */
|
|
goto akai_wav2take_exit;
|
|
}
|
|
#endif /* else: Note: wavesamplecount==0 is allowed here */
|
|
|
|
if (wavsamplecount>0){
|
|
/* determine csizes (sample clusters) and csizee (envelope clusters) */
|
|
samplesize=wavsamplecount*2; /* 16bit per sample word in DD take */
|
|
csizes=(samplesize+AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE-1)/(AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE); /* in clusters, round up */
|
|
envsiz=((samplesize/2)+AKAI_DDTAKE_ENVBLKSIZW-1)/AKAI_DDTAKE_ENVBLKSIZW; /* number of bytes for envelope, round up (Note: /2 for 16bit per sample word) */
|
|
csizee=(envsiz+AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE-1)/(AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE); /* in clusters, round up */
|
|
|
|
/* allocate envelope buffer */
|
|
if ((envbuf=(u_char *)malloc(envsiz))==NULL){
|
|
PRINTF_ERR("cannot allocate envelope buffer\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
|
|
/* allocate space for take */
|
|
#if 1
|
|
if (akai_allocate_ddfatchain(pp,csizes,&cstarts,1)<0){ /* 1: not necessarily contiguous */
|
|
#else
|
|
if (akai_allocate_ddfatchain(pp,csizes,&cstarts,csizes)<0){ /* contiguous */
|
|
#endif
|
|
PRINTF_ERR("cannot allocate FAT chain for sample\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
#if 1
|
|
if (akai_allocate_ddfatchain(pp,csizee,&cstarte,1)<0){ /* 1: not necessarily contiguous */
|
|
#else
|
|
if (akai_allocate_ddfatchain(pp,csizee,&cstarte,csizee)<0){ /* contiguous */
|
|
#endif
|
|
PRINTF_ERR("cannot allocate FAT chain for envelope\n");
|
|
/* free FAT at cstarts */
|
|
akai_free_ddfatchain(pp,cstarts,1); /* XXX ignore error */
|
|
goto akai_wav2take_exit;
|
|
}
|
|
}else{
|
|
samplesize=0;
|
|
csizes=0;
|
|
envsiz=0;
|
|
csizee=0;
|
|
cstarts=0; /* no sample */
|
|
cstarte=0; /* no envelope */
|
|
}
|
|
|
|
if (csizes>0){
|
|
/* read WAV sample and write sample to DD take */
|
|
#if 1
|
|
if (wavbitnr==16){
|
|
/* Note: no sample format conversion necessary */
|
|
/* copy sample from WAV file to DD take */
|
|
if (akai_import_ddfatchain(pp,cstarts,0,samplesize,wavfd,NULL)<0){
|
|
PRINTF_ERR("cannot import DD take\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
bcount+=samplesize;
|
|
}else
|
|
#endif
|
|
{
|
|
/* allocate WAV sample buffer */
|
|
/* buffer for at least 16bit */
|
|
if (wavbitnr>16){
|
|
wavsamplesizealloc=wavsamplecount*(wavbitnr/8);
|
|
}else{
|
|
wavsamplesizealloc=wavsamplecount*(16/8);
|
|
}
|
|
wavbuf=(u_char *)malloc(wavsamplesizealloc);
|
|
if (wavbuf==NULL){
|
|
PRINTF_ERR("cannot allocate WAV buffer\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
|
|
/* read WAV sample to memory */
|
|
if (READ(wavfd,wavbuf,wavsamplesize)!=(int)wavsamplesize){
|
|
PRINTF_ERR("cannot read sample\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
bcount+=wavsamplesize;
|
|
if (wavbitnr==8){
|
|
/* convert 8bit WAV sample format into 16bit WAV sample format */
|
|
/* Note: write pointer must not overtake read pointer => address sequence must be downwards */
|
|
for (i=0;i<wavsamplecount;i++){
|
|
j=wavsamplecount-1-i;
|
|
wavbuf[j*2+1]=0x80^wavbuf[j]; /* toggle sign bit */
|
|
wavbuf[j*2+0]=0x00;
|
|
}
|
|
}else if (wavbitnr==24){
|
|
/* convert 24bit WAV sample format into 16bit WAV sample format */
|
|
/* Note: write pointer must not overtake read pointer => address sequence must be upwards */
|
|
for (i=0;i<wavsamplecount;i++){
|
|
/* Note: copy upper 16 bits, discard lower 8 bits */
|
|
wavbuf[i*2+0]=wavbuf[i*3+1];
|
|
wavbuf[i*2+1]=wavbuf[i*3+2];
|
|
}
|
|
}else if (wavbitnr==32){
|
|
/* convert 32bit WAV sample format into 16bit WAV sample format */
|
|
/* Note: write pointer must not overtake read pointer => address sequence must be upwards */
|
|
for (i=0;i<wavsamplecount;i++){
|
|
/* Note: copy upper 16 bits, discard lower 16 bits */
|
|
wavbuf[i*2+0]=wavbuf[i*4+2];
|
|
wavbuf[i*2+1]=wavbuf[i*4+3];
|
|
}
|
|
}
|
|
|
|
/* write sample to DD take */
|
|
if (akai_import_ddfatchain(pp,cstarts,0,samplesize,-1,wavbuf)<0){
|
|
PRINTF_ERR("cannot import DD take\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (csizee>0){
|
|
/* Note: no envelope in WAV file */
|
|
/* calculate envelope from sample */
|
|
if (akai_take_setenv(pp,cstarts,samplesize,envbuf,envsiz)<0){
|
|
goto akai_wav2take_exit;
|
|
}
|
|
/* write envelope to DD take */
|
|
if (akai_import_ddfatchain(pp,cstarte,0,envsiz,-1,envbuf)<0){
|
|
PRINTF_ERR("cannot save envelope\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
}
|
|
|
|
#ifndef WAV_AKAIHEAD_DISABLE
|
|
/* check for DD take header chunk in WAV file */
|
|
{
|
|
int wavakaiheadtype;
|
|
u_int wavakaiheadsize;
|
|
u_int bc;
|
|
|
|
wavakaiheadtype=wav_find_akaihead(wavfd,&bc,&wavakaiheadsize,extrasize,WAV_AKAIHEADTYPE_DDTAKE);
|
|
if (wavakaiheadtype<0){
|
|
goto akai_wav2take_exit;
|
|
}
|
|
bcount+=bc;
|
|
|
|
if ((wavakaiheadtype==(int)WAV_AKAIHEADTYPE_DDTAKE)&&(wavakaiheadsize==sizeof(struct akai_ddtake_s))){
|
|
/* found matching DD take header chunk */
|
|
wavakaiheadfound=1;
|
|
}else{
|
|
wavakaiheadfound=0;
|
|
}
|
|
}
|
|
|
|
if (wavakaiheadfound){
|
|
/* read DD take header */
|
|
if (READ(wavfd,(u_char *)&t,sizeof(struct akai_ddtake_s))!=(int)sizeof(struct akai_ddtake_s)){
|
|
PRINTF_ERR("cannot read DD take header\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
bcount+=sizeof(struct akai_ddtake_s);
|
|
#if 1
|
|
PRINTF_OUT("DD take header imported from WAV\n");
|
|
#endif
|
|
/* Note: ignore cstarts/cstarte fields in DD take header */
|
|
/* Note: keep name in DD take header */
|
|
/* Note: must overwrite some settings in DD take header (see below) */
|
|
}else
|
|
#endif
|
|
{
|
|
u_int wstart,wend;
|
|
u_int wstartm,wendm;
|
|
|
|
/* create DD take header */
|
|
bzero((void *)&t,sizeof(struct akai_ddtake_s));
|
|
/* default parameter settings */
|
|
wstart=0;
|
|
wend=samplesize/2; /* /2 for 16bit per sample word */
|
|
wstartm=wstart;
|
|
wendm=wend;
|
|
t.wstart[3]=0xff&(wstart>>24);
|
|
t.wstart[2]=0xff&(wstart>>16);
|
|
t.wstart[1]=0xff&(wstart>>8);
|
|
t.wstart[0]=0xff&wstart;
|
|
t.wend[3]=0xff&(wend>>24);
|
|
t.wend[2]=0xff&(wend>>16);
|
|
t.wend[1]=0xff&(wend>>8);
|
|
t.wend[0]=0xff&wend;
|
|
t.wstartm[3]=0xff&(wstartm>>24);
|
|
t.wstartm[2]=0xff&(wstartm>>16);
|
|
t.wstartm[1]=0xff&(wstartm>>8);
|
|
t.wstartm[0]=0xff&wstartm;
|
|
t.wendm[3]=0xff&(wendm>>24);
|
|
t.wendm[2]=0xff&(wendm>>16);
|
|
t.wendm[1]=0xff&(wendm>>8);
|
|
t.wendm[0]=0xff&wendm;
|
|
t.fadein[1]=0xff&(10>>8); /* XXX */
|
|
t.fadein[0]=0xff&10; /* XXX */
|
|
t.fadeout[1]=0xff&(50>>8); /* XXX */
|
|
t.fadeout[0]=0xff&50; /* XXX */
|
|
t.stlvl=99; /* XXX */
|
|
t.stmix=0x01; /* XXX */
|
|
t.midich1=16-1; /* XXX */
|
|
t.midinote=60; /* XXX */
|
|
t.startm=0x03; /* XXX */
|
|
t.predel[1]=0xff&(500>>8); /* XXX */
|
|
t.predel[0]=0xff&500; /* XXX */
|
|
t.outlvl=50; /* XXX */
|
|
t.sendlvl=25; /* XXX */
|
|
/* derive DD take name from WAV file name */
|
|
if (nlen>AKAI_NAME_LEN){
|
|
nlen=AKAI_NAME_LEN;
|
|
}
|
|
bcopy(wavname,name,nlen);
|
|
name[nlen]='\0';
|
|
ascii2akai_name(name,t.name,0); /* 0: not S900 */
|
|
}
|
|
/* fundamental settings of DD take */
|
|
t.stat=AKAI_DDTAKESTAT_USED; /* used */
|
|
/* set cstarts and cstarte in DD take header */
|
|
t.cstarts[1]=0xff&(cstarts>>8);
|
|
t.cstarts[0]=0xff&cstarts;
|
|
t.cstarte[1]=0xff&(cstarte>>8);
|
|
t.cstarte[0]=0xff&cstarte;
|
|
t.stype=(wavchnr==2)?AKAI_DDTAKESTYPE_STEREO:AKAI_DDTAKESTYPE_MONO;
|
|
t.srate[1]=0xff&(samplerate>>8);
|
|
t.srate[0]=0xff&samplerate;
|
|
|
|
/* copy DD take header into directory entry */
|
|
bcopy(&t,&pp->head.dd.take[ti],sizeof(struct akai_ddtake_s));
|
|
/* write partition header */
|
|
if (akai_io_blks(pp,(u_char *)&pp->head.dd,
|
|
0,
|
|
AKAI_DDPARTHEAD_BLKS,
|
|
1,IO_BLKS_WRITE)<0){ /* 1: allocate cache if possible */
|
|
PRINTF_ERR("cannot write partition header\n");
|
|
goto akai_wav2take_exit;
|
|
}
|
|
|
|
#if 1
|
|
PRINTF_OUT("DD take imported from WAV\n");
|
|
#endif
|
|
|
|
ret=0; /* success */
|
|
|
|
akai_wav2take_exit:
|
|
if (what&WAV2TAKE_OPEN){
|
|
if (wavfd>=0){
|
|
CLOSE(wavfd);
|
|
}
|
|
}
|
|
if (bcountp!=NULL){
|
|
*bcountp=bcount;
|
|
}
|
|
if (envbuf!=NULL){
|
|
free(envbuf);
|
|
}
|
|
if (wavbuf!=NULL){
|
|
free(wavbuf);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/* set envelope for take */
|
|
int
|
|
akai_take_setenv(struct part_s *pp,u_int cstarts,u_int samplesize,u_char *envbuf,u_int envsiz)
|
|
{
|
|
static u_char logabstab[0x100]; /* must be static */
|
|
static int logabstabready=0; /* must be static */
|
|
u_char l,lmax;
|
|
u_int i,j;
|
|
u_int ba,bremain,bchunk,la,lchunk;
|
|
static u_char sbuf[AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE]; /* 1 cluster */
|
|
|
|
if (!logabstabready){ /* logabstab not initialized yet? */
|
|
double lconst;
|
|
|
|
/* initialize logabstab */
|
|
lconst=((double)(AKAI_DDTAKE_ENVMAXVAL-1))/log((double)0x80);
|
|
logabstab[0x00]=0;
|
|
for (i=0x01;i<=0x7f;i++){
|
|
logabstab[i]=((u_char)(lconst*log((double)i)))+1;
|
|
}
|
|
logabstab[0x80]=AKAI_DDTAKE_ENVMAXVAL;
|
|
for (i=0x81;i<=0xff;i++){
|
|
logabstab[i]=logabstab[0x100-i];
|
|
}
|
|
|
|
logabstabready=1; /* logabstab is initialized */
|
|
}
|
|
|
|
if ((pp==NULL)||(!pp->valid)||(pp->fat==NULL)){
|
|
return -1;
|
|
}
|
|
if (pp->type!=PART_TYPE_DD){
|
|
return -1;
|
|
}
|
|
|
|
if (envbuf==NULL){
|
|
return -1;
|
|
}
|
|
|
|
/* zero envbuf */
|
|
bzero(envbuf,envsiz);
|
|
|
|
if (samplesize==0){
|
|
return 0;
|
|
}
|
|
|
|
/* calculate envelope of samples */
|
|
ba=0;
|
|
bremain=samplesize;
|
|
i=0;
|
|
while ((bremain>0)&&(i<envsiz)){
|
|
/* cluster */
|
|
bchunk=AKAI_DDPART_CBLKS*AKAI_HD_BLOCKSIZE;
|
|
if (bchunk>bremain){
|
|
bchunk=bremain;
|
|
}
|
|
|
|
/* read samples from take */
|
|
if (akai_export_ddfatchain(pp,cstarts,ba,bchunk,-1,sbuf)<0){
|
|
PRINTF_ERR("cannot read take\n");
|
|
#if 1
|
|
break;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
la=0;
|
|
while ((bchunk>0)&&(i<envsiz)){
|
|
/* envelope block */
|
|
lchunk=AKAI_DDTAKE_ENVBLKSIZW<<1; /* *2 for 16bit per sample word */
|
|
if (lchunk>bchunk){
|
|
lchunk=bchunk;
|
|
}
|
|
/* determine peak level within envelope block */
|
|
lmax=0;
|
|
for (j=0;j<lchunk;j+=2){ /* 2 for 16bit per sample word */
|
|
l=logabstab[sbuf[la+j+1]]; /* logabs of MSB */
|
|
if (l>lmax){
|
|
lmax=l;
|
|
}
|
|
}
|
|
envbuf[i]=lmax;
|
|
|
|
ba+=lchunk;
|
|
bremain-=lchunk;
|
|
la+=lchunk;
|
|
bchunk-=lchunk;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|