diff --git a/CMakeLists.txt b/CMakeLists.txt index 9484a602a..5312692a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -451,6 +451,10 @@ src/engine/platform/rf5c68.cpp src/engine/platform/dummy.cpp ) +if (USE_SNDFILE) + list(APPEND ENGINE_SOURCES src/engine/sfWrapper.cpp) +endif() + if (WIN32) list(APPEND ENGINE_SOURCES src/utfutils.cpp) list(APPEND ENGINE_SOURCES src/engine/winStuff.cpp) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3c61269bf..47833ca90 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -37,7 +37,7 @@ #endif #include #ifdef HAVE_SNDFILE -#include +#include "sfWrapper.h" #endif #include @@ -200,11 +200,12 @@ void DivEngine::runExportThread() { case DIV_EXPORT_MODE_ONE: { SNDFILE* sf; SF_INFO si; + SFWrapper sfWrap; si.samplerate=got.rate; si.channels=2; si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16; - sf=sf_open(exportPath.c_str(),SFM_WRITE,&si); + sf=sfWrap.doOpen(exportPath.c_str(),SFM_WRITE,&si); if (sf==NULL) { logE("could not open file for writing! (%s)",sf_strerror(NULL)); exporting=false; @@ -259,7 +260,7 @@ void DivEngine::runExportThread() { delete[] outBuf[1]; delete[] outBuf[2]; - if (sf_close(sf)!=0) { + if (sfWrap.doClose()!=0) { logE("could not close audio file!"); } exporting=false; @@ -280,6 +281,7 @@ void DivEngine::runExportThread() { SNDFILE* sf[32]; SF_INFO si[32]; String fname[32]; + SFWrapper sfWrap[32]; for (int i=0; i16777215) { lastError="this sample is too big! max sample size is 16777215."; - sf_close(f); + sfWrap.doClose(); BUSY_END; return -1; } @@ -2301,7 +2305,7 @@ int DivEngine::addSampleFromFile(const char* path) { if (sample->centerRate<4000) sample->centerRate=4000; if (sample->centerRate>64000) sample->centerRate=64000; - sf_close(f); + sfWrap.doClose(); saveLock.lock(); song.sample.push_back(sample); song.sampleLen=sampleCount+1; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index f8cb79ac0..02d3228f0 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -24,9 +24,6 @@ #include "engine.h" #include "../ta-log.h" #include -#ifdef HAVE_SNDFILE -#include -#endif constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 36886856c..908b9a0d8 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -22,7 +22,7 @@ #include #include #ifdef HAVE_SNDFILE -#include +#include "sfWrapper.h" #endif #include "filter.h" @@ -45,6 +45,7 @@ bool DivSample::save(const char* path) { #else SNDFILE* f; SF_INFO si; + SFWrapper sfWrap; memset(&si,0,sizeof(SF_INFO)); if (length16<1) return false; @@ -60,7 +61,7 @@ bool DivSample::save(const char* path) { break; } - f=sf_open(path,SFM_WRITE,&si); + f=sfWrap.doOpen(path,SFM_WRITE,&si); if (f==NULL) { logE("could not open wave file for saving! %s",sf_error_number(sf_error(f))); @@ -100,7 +101,7 @@ bool DivSample::save(const char* path) { break; } - sf_close(f); + sfWrap.doClose(); return true; #endif diff --git a/src/engine/sfWrapper.cpp b/src/engine/sfWrapper.cpp new file mode 100644 index 000000000..f71bff341 --- /dev/null +++ b/src/engine/sfWrapper.cpp @@ -0,0 +1,112 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 "sfWrapper.h" +#include "../fileutils.h" + +sf_count_t _vioGetSize(void* user) { + return ((SFWrapper*)user)->ioGetSize(); +} + +sf_count_t _vioSeek(sf_count_t offset, int whence, void* user) { + return ((SFWrapper*)user)->ioSeek(offset,whence); +} + +sf_count_t _vioRead(void* ptr, sf_count_t count, void* user) { + return ((SFWrapper*)user)->ioRead(ptr,count); +} + +sf_count_t _vioWrite(const void* ptr, sf_count_t count, void* user) { + return ((SFWrapper*)user)->ioWrite(ptr,count); +} + +sf_count_t _vioTell(void* user) { + return ((SFWrapper*)user)->ioTell(); +} + +sf_count_t SFWrapper::ioGetSize() { + return (sf_count_t)len; +} + +sf_count_t SFWrapper::ioSeek(sf_count_t offset, int whence) { + return fseek(f,offset,whence); +} + +sf_count_t SFWrapper::ioRead(void* ptr, sf_count_t count) { + return fread(ptr,1,count,f); +} + +sf_count_t SFWrapper::ioWrite(const void* ptr, sf_count_t count) { + return fwrite(ptr,1,count,f); +} + +sf_count_t SFWrapper::ioTell() { + return ftell(f); +} + +int SFWrapper::doClose() { + int ret=sf_close(sf); + fclose(f); + return ret; +} + +SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) { + vio.get_filelen=_vioGetSize; + vio.read=_vioRead; + vio.seek=_vioSeek; + vio.tell=_vioTell; + vio.write=_vioWrite; + + const char* modeC="rb"; + if (mode==SFM_WRITE) { + modeC="wb"; + } + if (mode==SFM_RDWR) { + modeC="rb+"; + } + + f=ps_fopen(path,modeC); + if (f==NULL) { + return NULL; + } + + if (fseek(f,0,SEEK_END)==-1) { + fclose(f); + f=NULL; + return NULL; + } + + len=ftell(f); + if (len==(SIZE_MAX>>1)) { + len=0; + fclose(f); + f=NULL; + return NULL; + } + + if (fseek(f,0,SEEK_SET)==-1) { + len=0; + fclose(f); + f=NULL; + return NULL; + } + + sf=sf_open_virtual(&vio,mode,sfinfo,this); + return sf; +} \ No newline at end of file diff --git a/src/engine/sfWrapper.h b/src/engine/sfWrapper.h new file mode 100644 index 000000000..323871597 --- /dev/null +++ b/src/engine/sfWrapper.h @@ -0,0 +1,54 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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. + */ + +// sfWrapper.h: libsndfile FILE* wrapper to work around Windows issue with +// non-ASCII chars in file path +// I wanted to do this in sndfile directly, but it's a +// submodule... + +#ifndef _SFWRAPPER_H +#define _SFWRAPPER_H +#include +#include +#include +#include +#include "../ta-utils.h" + +class SFWrapper { + FILE* f; + size_t len; + SF_VIRTUAL_IO vio; + SNDFILE* sf; + + public: + sf_count_t ioGetSize(); + sf_count_t ioSeek(sf_count_t offset, int whence); + sf_count_t ioRead(void* ptr, sf_count_t count); + sf_count_t ioWrite(const void* ptr, sf_count_t count); + sf_count_t ioTell(); + + int doClose(); + SNDFILE* doOpen(const char* path, int mode, SF_INFO* sfinfo); + SFWrapper(): + f(NULL), + len(0), + sf(NULL) {} +}; + +#endif