split sample loading functions
This commit is contained in:
parent
4776eaed68
commit
2bf5392f7b
|
@ -526,6 +526,7 @@ src/engine/engine.cpp
|
|||
src/engine/export.cpp
|
||||
src/engine/fileOps.cpp
|
||||
src/engine/fileOpsIns.cpp
|
||||
src/engine/fileOpsSample.cpp
|
||||
src/engine/filter.cpp
|
||||
src/engine/instrument.cpp
|
||||
src/engine/macroInt.cpp
|
||||
|
|
|
@ -34,9 +34,6 @@
|
|||
#endif
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#ifdef HAVE_SNDFILE
|
||||
#include "sfWrapper.h"
|
||||
#endif
|
||||
#include <fmt/printf.h>
|
||||
|
||||
void process(void* u, float** in, float** out, int inChans, int outChans, unsigned int size) {
|
||||
|
@ -2479,492 +2476,6 @@ int DivEngine::addSamplePtr(DivSample* which) {
|
|||
return sampleCount;
|
||||
}
|
||||
|
||||
DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return NULL;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
||||
const char* pathRedux=strrchr(path,DIR_SEPARATOR);
|
||||
if (pathRedux==NULL) {
|
||||
pathRedux=path;
|
||||
} else {
|
||||
pathRedux++;
|
||||
}
|
||||
String stripPath;
|
||||
const char* pathReduxEnd=strrchr(pathRedux,'.');
|
||||
if (pathReduxEnd==NULL) {
|
||||
stripPath=pathRedux;
|
||||
} else {
|
||||
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
|
||||
stripPath+=*i;
|
||||
}
|
||||
}
|
||||
|
||||
const char* ext=strrchr(path,'.');
|
||||
if (ext!=NULL) {
|
||||
String extS;
|
||||
for (; *ext; ext++) {
|
||||
char i=*ext;
|
||||
if (i>='A' && i<='Z') {
|
||||
i+='a'-'A';
|
||||
}
|
||||
extS+=i;
|
||||
}
|
||||
if (extS==".dmc" || extS==".brr") { // read as .dmc or .brr
|
||||
size_t len=0;
|
||||
DivSample* sample=new DivSample;
|
||||
sample->name=stripPath;
|
||||
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len=ftell(f);
|
||||
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is invalid!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (extS==".dmc") {
|
||||
sample->rate=33144;
|
||||
sample->centerRate=33144;
|
||||
sample->depth=DIV_SAMPLE_DEPTH_1BIT_DPCM;
|
||||
sample->init(len*8);
|
||||
} else if (extS==".brr") {
|
||||
sample->rate=32000;
|
||||
sample->centerRate=32000;
|
||||
sample->depth=DIV_SAMPLE_DEPTH_BRR;
|
||||
sample->init(16*(len/9));
|
||||
} else {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="wait... is that right? no I don't think so...";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char* dataBuf=sample->dataDPCM;
|
||||
if (extS==".brr") {
|
||||
dataBuf=sample->dataBRR;
|
||||
if ((len%9)==2) {
|
||||
// read loop position
|
||||
unsigned short loopPos=0;
|
||||
logD("BRR file has loop position");
|
||||
if (fread(&loopPos,1,2,f)!=2) {
|
||||
logW("could not read loop position! %s",strerror(errno));
|
||||
} else {
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
loopPos=(loopPos>>8)|(loopPos<<8);
|
||||
#endif
|
||||
sample->loopStart=16*(loopPos/9);
|
||||
sample->loopEnd=sample->samples;
|
||||
sample->loop=true;
|
||||
sample->loopMode=DIV_SAMPLE_LOOP_FORWARD;
|
||||
}
|
||||
len-=2;
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="BRR sample is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
} else if ((len%9)!=0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="possibly corrupt BRR sample!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fread(dataBuf,1,len,f)==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
BUSY_END;
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_SNDFILE
|
||||
lastError="Furnace was not compiled with libsndfile!";
|
||||
return NULL;
|
||||
#else
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
memset(&si,0,sizeof(SF_INFO));
|
||||
SNDFILE* f=sfWrap.doOpen(path,SFM_READ,&si);
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
int err=sf_error(NULL);
|
||||
if (err==SF_ERR_SYSTEM) {
|
||||
lastError=fmt::sprintf("could not open file! (%s %s)",sf_error_number(err),strerror(errno));
|
||||
} else {
|
||||
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (si.frames>16777215) {
|
||||
lastError="this sample is too big! max sample size is 16777215.";
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return NULL;
|
||||
}
|
||||
void* buf=NULL;
|
||||
sf_count_t sampleLen=sizeof(short);
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
logD("sample is 8-bit unsigned");
|
||||
buf=new unsigned char[si.channels*si.frames];
|
||||
sampleLen=sizeof(unsigned char);
|
||||
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
logD("sample is 32-bit float");
|
||||
buf=new float[si.channels*si.frames];
|
||||
sampleLen=sizeof(float);
|
||||
} else {
|
||||
logD("sample is 16-bit signed");
|
||||
buf=new short[si.channels*si.frames];
|
||||
sampleLen=sizeof(short);
|
||||
}
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8 || (si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
if (sf_read_raw(f,buf,si.frames*si.channels*sampleLen)!=(si.frames*si.channels*sampleLen)) {
|
||||
logW("sample read size mismatch!");
|
||||
}
|
||||
} else {
|
||||
if (sf_read_short(f,(short*)buf,si.frames*si.channels)!=(si.frames*si.channels)) {
|
||||
logW("sample read size mismatch!");
|
||||
}
|
||||
}
|
||||
DivSample* sample=new DivSample;
|
||||
int sampleCount=(int)song.sample.size();
|
||||
sample->name=stripPath;
|
||||
|
||||
int index=0;
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
sample->depth=DIV_SAMPLE_DEPTH_8BIT;
|
||||
} else {
|
||||
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
|
||||
}
|
||||
sample->init(si.frames);
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((int)((unsigned char*)buf)[i+j])-128;
|
||||
}
|
||||
averaged/=si.channels;
|
||||
sample->data8[index++]=averaged;
|
||||
}
|
||||
delete[] (unsigned char*)buf;
|
||||
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
float averaged=0.0f;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((float*)buf)[i+j];
|
||||
}
|
||||
averaged/=si.channels;
|
||||
averaged*=32767.0;
|
||||
if (averaged<-32768.0) averaged=-32768.0;
|
||||
if (averaged>32767.0) averaged=32767.0;
|
||||
sample->data16[index++]=averaged;
|
||||
}
|
||||
delete[] (float*)buf;
|
||||
} else {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((short*)buf)[i+j];
|
||||
}
|
||||
averaged/=si.channels;
|
||||
sample->data16[index++]=averaged;
|
||||
}
|
||||
delete[] (short*)buf;
|
||||
}
|
||||
|
||||
sample->rate=si.samplerate;
|
||||
if (sample->rate<4000) sample->rate=4000;
|
||||
if (sample->rate>96000) sample->rate=96000;
|
||||
sample->centerRate=si.samplerate;
|
||||
|
||||
SF_INSTRUMENT inst;
|
||||
if (sf_command(f, SFC_GET_INSTRUMENT, &inst, sizeof(inst)) == SF_TRUE)
|
||||
{
|
||||
// There's no documentation on libsndfile detune range, but the code
|
||||
// implies -50..50. Yet when loading a file you can get a >50 value.
|
||||
if(inst.detune > 50)
|
||||
inst.detune = inst.detune - 100;
|
||||
short pitch = ((0x3c-inst.basenote)*100) + inst.detune;
|
||||
sample->centerRate=si.samplerate*pow(2.0,pitch/(12.0 * 100.0));
|
||||
if(inst.loop_count && inst.loops[0].mode >= SF_LOOP_FORWARD)
|
||||
{
|
||||
sample->loop=true;
|
||||
sample->loopMode=(DivSampleLoopMode)(inst.loops[0].mode-SF_LOOP_FORWARD);
|
||||
sample->loopStart=inst.loops[0].start;
|
||||
sample->loopEnd=inst.loops[0].end;
|
||||
if(inst.loops[0].end < (unsigned int)sampleCount)
|
||||
sampleCount=inst.loops[0].end;
|
||||
}
|
||||
else
|
||||
sample->loop=false;
|
||||
}
|
||||
|
||||
if (sample->centerRate<4000) sample->centerRate=4000;
|
||||
if (sample->centerRate>64000) sample->centerRate=64000;
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return sample;
|
||||
#endif
|
||||
}
|
||||
|
||||
DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return NULL;
|
||||
}
|
||||
if (channels<1) {
|
||||
channels=1;
|
||||
}
|
||||
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
|
||||
if (channels!=1) {
|
||||
channels=1;
|
||||
}
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
||||
const char* pathRedux=strrchr(path,DIR_SEPARATOR);
|
||||
if (pathRedux==NULL) {
|
||||
pathRedux=path;
|
||||
} else {
|
||||
pathRedux++;
|
||||
}
|
||||
String stripPath;
|
||||
const char* pathReduxEnd=strrchr(pathRedux,'.');
|
||||
if (pathReduxEnd==NULL) {
|
||||
stripPath=pathRedux;
|
||||
} else {
|
||||
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
|
||||
stripPath+=*i;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len=0;
|
||||
size_t lenDivided=0;
|
||||
DivSample* sample=new DivSample;
|
||||
sample->name=stripPath;
|
||||
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len=ftell(f);
|
||||
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is invalid!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lenDivided=len/channels;
|
||||
|
||||
unsigned int samples=lenDivided;
|
||||
switch (depth) {
|
||||
case DIV_SAMPLE_DEPTH_1BIT:
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
|
||||
samples=lenDivided*8;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_A:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
||||
case DIV_SAMPLE_DEPTH_VOX:
|
||||
samples=lenDivided*2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_8BIT:
|
||||
case DIV_SAMPLE_DEPTH_MULAW:
|
||||
samples=lenDivided;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_BRR:
|
||||
samples=16*((lenDivided+8)/9);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
samples=(lenDivided+1)/2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (samples>16777215) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="this sample is too big! max sample size is 16777215.";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sample->rate=32000;
|
||||
sample->centerRate=32000;
|
||||
sample->depth=depth;
|
||||
sample->init(samples);
|
||||
|
||||
unsigned char* buf=new unsigned char[len];
|
||||
if (fread(buf,1,len,f)==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||
delete[] buf;
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
// import sample
|
||||
size_t pos=0;
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
int accum=0;
|
||||
for (int j=0; j<channels; j++) {
|
||||
if (pos+1>=len) break;
|
||||
if (bigEndian) {
|
||||
accum+=(short)(((short)((buf[pos]<<8)|buf[pos+1]))^(unsign?0x8000:0));
|
||||
} else {
|
||||
accum+=(short)(((short)(buf[pos]|(buf[pos+1]<<8)))^(unsign?0x8000:0));
|
||||
}
|
||||
pos+=2;
|
||||
}
|
||||
accum/=channels;
|
||||
sample->data16[i]=accum;
|
||||
}
|
||||
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
int accum=0;
|
||||
for (int j=0; j<channels; j++) {
|
||||
if (pos>=len) break;
|
||||
accum+=(signed char)(buf[pos++]^(unsign?0x80:0));
|
||||
}
|
||||
accum/=channels;
|
||||
sample->data8[i]=accum;
|
||||
}
|
||||
} else {
|
||||
memcpy(sample->getCurBuf(),buf,len);
|
||||
}
|
||||
delete[] buf;
|
||||
|
||||
if (swapNibbles) {
|
||||
unsigned char* b=(unsigned char*)sample->getCurBuf();
|
||||
switch (depth) {
|
||||
case DIV_SAMPLE_DEPTH_1BIT:
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
|
||||
// reverse bit order
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(
|
||||
((b[i]&128)?1:0)|
|
||||
((b[i]&64)?2:0)|
|
||||
((b[i]&32)?4:0)|
|
||||
((b[i]&16)?8:0)|
|
||||
((b[i]&8)?16:0)|
|
||||
((b[i]&4)?32:0)|
|
||||
((b[i]&2)?64:0)|
|
||||
((b[i]&1)?128:0)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_A:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
||||
case DIV_SAMPLE_DEPTH_VOX:
|
||||
// swap nibbles
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(b[i]<<4)|(b[i]>>4);
|
||||
}
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_MULAW:
|
||||
// Namco to G.711
|
||||
// Namco: smmmmxxx
|
||||
// G.711: sxxxmmmm (^0xff)
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(((b[i]&7)<<4)|(((b[i]>>3)&15)^((b[i]&0x80)?15:0))|(b[i]&0x80))^0xff;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BUSY_END;
|
||||
return sample;
|
||||
}
|
||||
|
||||
void DivEngine::delSample(int index) {
|
||||
BUSY_BEGIN;
|
||||
sPreview.sample=-1;
|
||||
|
|
|
@ -0,0 +1,511 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "engine.h"
|
||||
#include "../ta-log.h"
|
||||
#include "../fileutils.h"
|
||||
#ifdef HAVE_SNDFILE
|
||||
#include "sfWrapper.h"
|
||||
#endif
|
||||
|
||||
DivSample* DivEngine::sampleFromFile(const char* path) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return NULL;
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
||||
const char* pathRedux=strrchr(path,DIR_SEPARATOR);
|
||||
if (pathRedux==NULL) {
|
||||
pathRedux=path;
|
||||
} else {
|
||||
pathRedux++;
|
||||
}
|
||||
String stripPath;
|
||||
const char* pathReduxEnd=strrchr(pathRedux,'.');
|
||||
if (pathReduxEnd==NULL) {
|
||||
stripPath=pathRedux;
|
||||
} else {
|
||||
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
|
||||
stripPath+=*i;
|
||||
}
|
||||
}
|
||||
|
||||
const char* ext=strrchr(path,'.');
|
||||
if (ext!=NULL) {
|
||||
String extS;
|
||||
for (; *ext; ext++) {
|
||||
char i=*ext;
|
||||
if (i>='A' && i<='Z') {
|
||||
i+='a'-'A';
|
||||
}
|
||||
extS+=i;
|
||||
}
|
||||
if (extS==".dmc" || extS==".brr") { // read as .dmc or .brr
|
||||
size_t len=0;
|
||||
DivSample* sample=new DivSample;
|
||||
sample->name=stripPath;
|
||||
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len=ftell(f);
|
||||
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is invalid!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (extS==".dmc") {
|
||||
sample->rate=33144;
|
||||
sample->centerRate=33144;
|
||||
sample->depth=DIV_SAMPLE_DEPTH_1BIT_DPCM;
|
||||
sample->init(len*8);
|
||||
} else if (extS==".brr") {
|
||||
sample->rate=32000;
|
||||
sample->centerRate=32000;
|
||||
sample->depth=DIV_SAMPLE_DEPTH_BRR;
|
||||
sample->init(16*(len/9));
|
||||
} else {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="wait... is that right? no I don't think so...";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char* dataBuf=sample->dataDPCM;
|
||||
if (extS==".brr") {
|
||||
dataBuf=sample->dataBRR;
|
||||
if ((len%9)==2) {
|
||||
// read loop position
|
||||
unsigned short loopPos=0;
|
||||
logD("BRR file has loop position");
|
||||
if (fread(&loopPos,1,2,f)!=2) {
|
||||
logW("could not read loop position! %s",strerror(errno));
|
||||
} else {
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
loopPos=(loopPos>>8)|(loopPos<<8);
|
||||
#endif
|
||||
sample->loopStart=16*(loopPos/9);
|
||||
sample->loopEnd=sample->samples;
|
||||
sample->loop=true;
|
||||
sample->loopMode=DIV_SAMPLE_LOOP_FORWARD;
|
||||
}
|
||||
len-=2;
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="BRR sample is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
} else if ((len%9)!=0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="possibly corrupt BRR sample!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fread(dataBuf,1,len,f)==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
BUSY_END;
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_SNDFILE
|
||||
lastError="Furnace was not compiled with libsndfile!";
|
||||
return NULL;
|
||||
#else
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
memset(&si,0,sizeof(SF_INFO));
|
||||
SNDFILE* f=sfWrap.doOpen(path,SFM_READ,&si);
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
int err=sf_error(NULL);
|
||||
if (err==SF_ERR_SYSTEM) {
|
||||
lastError=fmt::sprintf("could not open file! (%s %s)",sf_error_number(err),strerror(errno));
|
||||
} else {
|
||||
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (si.frames>16777215) {
|
||||
lastError="this sample is too big! max sample size is 16777215.";
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return NULL;
|
||||
}
|
||||
void* buf=NULL;
|
||||
sf_count_t sampleLen=sizeof(short);
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
logD("sample is 8-bit unsigned");
|
||||
buf=new unsigned char[si.channels*si.frames];
|
||||
sampleLen=sizeof(unsigned char);
|
||||
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
logD("sample is 32-bit float");
|
||||
buf=new float[si.channels*si.frames];
|
||||
sampleLen=sizeof(float);
|
||||
} else {
|
||||
logD("sample is 16-bit signed");
|
||||
buf=new short[si.channels*si.frames];
|
||||
sampleLen=sizeof(short);
|
||||
}
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8 || (si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
if (sf_read_raw(f,buf,si.frames*si.channels*sampleLen)!=(si.frames*si.channels*sampleLen)) {
|
||||
logW("sample read size mismatch!");
|
||||
}
|
||||
} else {
|
||||
if (sf_read_short(f,(short*)buf,si.frames*si.channels)!=(si.frames*si.channels)) {
|
||||
logW("sample read size mismatch!");
|
||||
}
|
||||
}
|
||||
DivSample* sample=new DivSample;
|
||||
int sampleCount=(int)song.sample.size();
|
||||
sample->name=stripPath;
|
||||
|
||||
int index=0;
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
sample->depth=DIV_SAMPLE_DEPTH_8BIT;
|
||||
} else {
|
||||
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
|
||||
}
|
||||
sample->init(si.frames);
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((int)((unsigned char*)buf)[i+j])-128;
|
||||
}
|
||||
averaged/=si.channels;
|
||||
sample->data8[index++]=averaged;
|
||||
}
|
||||
delete[] (unsigned char*)buf;
|
||||
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
float averaged=0.0f;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((float*)buf)[i+j];
|
||||
}
|
||||
averaged/=si.channels;
|
||||
averaged*=32767.0;
|
||||
if (averaged<-32768.0) averaged=-32768.0;
|
||||
if (averaged>32767.0) averaged=32767.0;
|
||||
sample->data16[index++]=averaged;
|
||||
}
|
||||
delete[] (float*)buf;
|
||||
} else {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((short*)buf)[i+j];
|
||||
}
|
||||
averaged/=si.channels;
|
||||
sample->data16[index++]=averaged;
|
||||
}
|
||||
delete[] (short*)buf;
|
||||
}
|
||||
|
||||
sample->rate=si.samplerate;
|
||||
if (sample->rate<4000) sample->rate=4000;
|
||||
if (sample->rate>96000) sample->rate=96000;
|
||||
sample->centerRate=si.samplerate;
|
||||
|
||||
SF_INSTRUMENT inst;
|
||||
if (sf_command(f, SFC_GET_INSTRUMENT, &inst, sizeof(inst)) == SF_TRUE)
|
||||
{
|
||||
// There's no documentation on libsndfile detune range, but the code
|
||||
// implies -50..50. Yet when loading a file you can get a >50 value.
|
||||
if(inst.detune > 50)
|
||||
inst.detune = inst.detune - 100;
|
||||
short pitch = ((0x3c-inst.basenote)*100) + inst.detune;
|
||||
sample->centerRate=si.samplerate*pow(2.0,pitch/(12.0 * 100.0));
|
||||
if(inst.loop_count && inst.loops[0].mode >= SF_LOOP_FORWARD)
|
||||
{
|
||||
sample->loop=true;
|
||||
sample->loopMode=(DivSampleLoopMode)(inst.loops[0].mode-SF_LOOP_FORWARD);
|
||||
sample->loopStart=inst.loops[0].start;
|
||||
sample->loopEnd=inst.loops[0].end;
|
||||
if(inst.loops[0].end < (unsigned int)sampleCount)
|
||||
sampleCount=inst.loops[0].end;
|
||||
}
|
||||
else
|
||||
sample->loop=false;
|
||||
}
|
||||
|
||||
if (sample->centerRate<4000) sample->centerRate=4000;
|
||||
if (sample->centerRate>64000) sample->centerRate=64000;
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return sample;
|
||||
#endif
|
||||
}
|
||||
|
||||
DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return NULL;
|
||||
}
|
||||
if (channels<1) {
|
||||
channels=1;
|
||||
}
|
||||
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
|
||||
if (channels!=1) {
|
||||
channels=1;
|
||||
}
|
||||
}
|
||||
BUSY_BEGIN;
|
||||
warnings="";
|
||||
|
||||
const char* pathRedux=strrchr(path,DIR_SEPARATOR);
|
||||
if (pathRedux==NULL) {
|
||||
pathRedux=path;
|
||||
} else {
|
||||
pathRedux++;
|
||||
}
|
||||
String stripPath;
|
||||
const char* pathReduxEnd=strrchr(pathRedux,'.');
|
||||
if (pathReduxEnd==NULL) {
|
||||
stripPath=pathRedux;
|
||||
} else {
|
||||
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
|
||||
stripPath+=*i;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len=0;
|
||||
size_t lenDivided=0;
|
||||
DivSample* sample=new DivSample;
|
||||
sample->name=stripPath;
|
||||
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len=ftell(f);
|
||||
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is empty!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is invalid!";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lenDivided=len/channels;
|
||||
|
||||
unsigned int samples=lenDivided;
|
||||
switch (depth) {
|
||||
case DIV_SAMPLE_DEPTH_1BIT:
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
|
||||
samples=lenDivided*8;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_A:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
||||
case DIV_SAMPLE_DEPTH_VOX:
|
||||
samples=lenDivided*2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_8BIT:
|
||||
case DIV_SAMPLE_DEPTH_MULAW:
|
||||
samples=lenDivided;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_BRR:
|
||||
samples=16*((lenDivided+8)/9);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
samples=(lenDivided+1)/2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (samples>16777215) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="this sample is too big! max sample size is 16777215.";
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sample->rate=32000;
|
||||
sample->centerRate=32000;
|
||||
sample->depth=depth;
|
||||
sample->init(samples);
|
||||
|
||||
unsigned char* buf=new unsigned char[len];
|
||||
if (fread(buf,1,len,f)==0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
|
||||
delete[] buf;
|
||||
delete sample;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
// import sample
|
||||
size_t pos=0;
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
int accum=0;
|
||||
for (int j=0; j<channels; j++) {
|
||||
if (pos+1>=len) break;
|
||||
if (bigEndian) {
|
||||
accum+=(short)(((short)((buf[pos]<<8)|buf[pos+1]))^(unsign?0x8000:0));
|
||||
} else {
|
||||
accum+=(short)(((short)(buf[pos]|(buf[pos+1]<<8)))^(unsign?0x8000:0));
|
||||
}
|
||||
pos+=2;
|
||||
}
|
||||
accum/=channels;
|
||||
sample->data16[i]=accum;
|
||||
}
|
||||
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
int accum=0;
|
||||
for (int j=0; j<channels; j++) {
|
||||
if (pos>=len) break;
|
||||
accum+=(signed char)(buf[pos++]^(unsign?0x80:0));
|
||||
}
|
||||
accum/=channels;
|
||||
sample->data8[i]=accum;
|
||||
}
|
||||
} else {
|
||||
memcpy(sample->getCurBuf(),buf,len);
|
||||
}
|
||||
delete[] buf;
|
||||
|
||||
if (swapNibbles) {
|
||||
unsigned char* b=(unsigned char*)sample->getCurBuf();
|
||||
switch (depth) {
|
||||
case DIV_SAMPLE_DEPTH_1BIT:
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
|
||||
// reverse bit order
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(
|
||||
((b[i]&128)?1:0)|
|
||||
((b[i]&64)?2:0)|
|
||||
((b[i]&32)?4:0)|
|
||||
((b[i]&16)?8:0)|
|
||||
((b[i]&8)?16:0)|
|
||||
((b[i]&4)?32:0)|
|
||||
((b[i]&2)?64:0)|
|
||||
((b[i]&1)?128:0)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_A:
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_B:
|
||||
case DIV_SAMPLE_DEPTH_VOX:
|
||||
// swap nibbles
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(b[i]<<4)|(b[i]>>4);
|
||||
}
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_MULAW:
|
||||
// Namco to G.711
|
||||
// Namco: smmmmxxx
|
||||
// G.711: sxxxmmmm (^0xff)
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(((b[i]&7)<<4)|(((b[i]>>3)&15)^((b[i]&0x80)?15:0))|(b[i]&0x80))^0xff;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BUSY_END;
|
||||
return sample;
|
||||
}
|
Loading…
Reference in New Issue