From 2bf5392f7b2700c340e800298cb03fe2b91f636e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Aug 2023 14:56:10 -0500 Subject: [PATCH] split sample loading functions --- CMakeLists.txt | 1 + src/engine/engine.cpp | 489 --------------------------------- src/engine/fileOpsSample.cpp | 511 +++++++++++++++++++++++++++++++++++ 3 files changed, 512 insertions(+), 489 deletions(-) create mode 100644 src/engine/fileOpsSample.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 30e2c123e..7e5d3afe6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b490880ed..fceb62e67 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -34,9 +34,6 @@ #endif #include #include -#ifdef HAVE_SNDFILE -#include "sfWrapper.h" -#endif #include 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; idata8[index++]=averaged; - } - delete[] (unsigned char*)buf; - } else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) { - for (int i=0; i32767.0) averaged=32767.0; - sample->data16[index++]=averaged; - } - delete[] (float*)buf; - } else { - for (int i=0; idata16[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=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=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; igetCurBufLen(); 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; igetCurBufLen(); 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; igetCurBufLen(); 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; diff --git a/src/engine/fileOpsSample.cpp b/src/engine/fileOpsSample.cpp new file mode 100644 index 000000000..4d4b3a0e8 --- /dev/null +++ b/src/engine/fileOpsSample.cpp @@ -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; idata8[index++]=averaged; + } + delete[] (unsigned char*)buf; + } else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) { + for (int i=0; i32767.0) averaged=32767.0; + sample->data16[index++]=averaged; + } + delete[] (float*)buf; + } else { + for (int i=0; idata16[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=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=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; igetCurBufLen(); 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; igetCurBufLen(); 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; igetCurBufLen(); 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; +}