/** * 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. */ #ifndef _SAMPLE_H #define _SAMPLE_H #include "../ta-utils.h" #include "defines.h" #include "safeWriter.h" #include "dataErrors.h" #include "../fixedQueue.h" enum DivSampleLoopMode: unsigned char { DIV_SAMPLE_LOOP_FORWARD=0, DIV_SAMPLE_LOOP_BACKWARD, DIV_SAMPLE_LOOP_PINGPONG, DIV_SAMPLE_LOOP_MAX // boundary for loop mode }; enum DivSampleDepth: unsigned char { DIV_SAMPLE_DEPTH_1BIT=0, DIV_SAMPLE_DEPTH_1BIT_DPCM=1, DIV_SAMPLE_DEPTH_YMZ_ADPCM=3, DIV_SAMPLE_DEPTH_QSOUND_ADPCM=4, DIV_SAMPLE_DEPTH_ADPCM_A=5, DIV_SAMPLE_DEPTH_ADPCM_B=6, DIV_SAMPLE_DEPTH_ADPCM_K=7, DIV_SAMPLE_DEPTH_8BIT=8, DIV_SAMPLE_DEPTH_BRR=9, DIV_SAMPLE_DEPTH_VOX=10, DIV_SAMPLE_DEPTH_MULAW=11, DIV_SAMPLE_DEPTH_C219=12, DIV_SAMPLE_DEPTH_16BIT=16, DIV_SAMPLE_DEPTH_MAX // boundary for sample depth }; enum DivResampleFilters { DIV_RESAMPLE_NONE=0, DIV_RESAMPLE_LINEAR, DIV_RESAMPLE_CUBIC, DIV_RESAMPLE_BLEP, DIV_RESAMPLE_SINC, DIV_RESAMPLE_BEST }; struct DivSampleHistory { unsigned char* data; unsigned int length, samples; DivSampleDepth depth; int rate, centerRate, loopStart, loopEnd; bool loop, brrEmphasis, dither; DivSampleLoopMode loopMode; bool hasSample; DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool di, DivSampleLoopMode lm): data((unsigned char*)d), length(l), samples(s), depth(de), rate(r), centerRate(cr), loopStart(ls), loopEnd(le), loop(lp), brrEmphasis(be), dither(di), loopMode(lm), hasSample(true) {} DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool di, DivSampleLoopMode lm): data(NULL), length(0), samples(0), depth(de), rate(r), centerRate(cr), loopStart(ls), loopEnd(le), loop(lp), brrEmphasis(be), dither(di), loopMode(lm), hasSample(false) {} ~DivSampleHistory(); }; struct DivSample { String name; int rate, centerRate, loopStart, loopEnd; // valid values are: // - 0: ZX Spectrum overlay drum (1-bit) // - 1: 1-bit NES DPCM (1-bit) // - 3: YMZ ADPCM // - 4: QSound ADPCM // - 5: ADPCM-A // - 6: ADPCM-B // - 7: K053260 4-bit simple ADPCM // - 8: 8-bit PCM // - 9: BRR (SNES) // - 10: VOX ADPCM // - 11: 8-bit µ-law PCM // - 12: C219 "µ-law" PCM // - 16: 16-bit PCM DivSampleDepth depth; bool loop, brrEmphasis, dither; // valid values are: // - 0: Forward loop // - 1: Backward loop // - 2: Pingpong loop DivSampleLoopMode loopMode; bool renderOn[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS]; // these are the new data structures. signed char* data8; // 8 short* data16; // 16 unsigned char* data1; // 0 unsigned char* dataDPCM; // 1 unsigned char* dataZ; // 3 unsigned char* dataQSoundA; // 4 unsigned char* dataA; // 5 unsigned char* dataB; // 6 unsigned char* dataK; // 7 unsigned char* dataBRR; // 9 unsigned char* dataVOX; // 10 unsigned char* dataMuLaw; // 11 unsigned char* dataC219; // 12 unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219; unsigned int samples; FixedQueue undoHist; FixedQueue redoHist; /** * put sample data. * @param w a SafeWriter. */ void putSampleData(SafeWriter* w); /** * read sample data. * @param reader the reader. * @param version the format version. * @return a DivDataErrors. */ DivDataErrors readSampleData(SafeReader& reader, short version); /** * check if sample is loopable. * @return whether it is loopable. */ bool isLoopable(); /** * get sample start position * @return the samples start position. */ int getLoopStartPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); /** * get sample loop end position * @return the samples loop end position. */ int getLoopEndPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); /** * get sample end position * @return the samples end position. */ int getEndPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); /** * get sample offset * @return the sample offset. */ int getSampleOffset(int offset, int length, DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); /** * @warning DO NOT USE - internal functions */ void setSampleCount(unsigned int count); /** * @warning DO NOT USE - internal functions */ bool resampleNone(double sRate, double tRate); bool resampleLinear(double sRate, double tRate); bool resampleCubic(double sRate, double tRate); bool resampleBlep(double sRate, double tRate); bool resampleSinc(double sRate, double tRate); /** * save this sample to a file. * @param path a path. * @return whether saving succeeded or not. */ bool save(const char* path); /** * save this sample to a file (raw). * @param path a path. * @return whether saving succeeded or not. */ bool saveRaw(const char* path); /** * @warning DO NOT USE - internal function * initialize sample data. * @param d sample type. * @param count number of samples. * @return whether it was successful. */ bool initInternal(DivSampleDepth d, int count); /** * initialize sample data. make sure you have set `depth` before doing so. * @param count number of samples. * @return whether it was successful. */ bool init(unsigned int count); /** * resize sample data. make sure the sample has been initialized before doing so. * @warning do not attempt to resize a sample outside of a synchronized block! * @param count number of samples. * @return whether it was successful. */ bool resize(unsigned int count); /** * remove part of the sample data. * @warning do not attempt to strip a sample outside of a synchronized block! * @param start the beginning. * @param end the end. * @return whether it was successful. */ bool strip(unsigned int begin, unsigned int end); /** * clip the sample data to specified boundaries. * @warning do not attempt to trim a sample outside of a synchronized block! * @param start the beginning. * @param end the end. * @return whether it was successful. */ bool trim(unsigned int begin, unsigned int end); /** * insert silence at specified position. * @warning do not attempt to do this outside of a synchronized block! * @param pos the beginning. * @param length how many samples to insert. * @return whether it was successful. */ bool insert(unsigned int pos, unsigned int length); /** * change the sample rate. * @warning do not attempt to resample outside of a synchronized block! * @param sRate source rate. * @param tRate target rate. * @param filter the interpolation filter. * @return whether it was successful. */ bool resample(double sRate, double tRate, int filter); /** * convert sample depth. * @warning do not attempt to do this outside of a synchronized block! * @param newDepth the new depth. */ void convert(DivSampleDepth newDepth); /** * initialize the rest of sample formats for this sample. */ void render(unsigned int formatMask=0xffffffff); /** * get the sample data for the current depth. * @return the sample data, or NULL if not created. */ void* getCurBuf(); /** * get the sample data length for the current depth. * @return the sample data length. */ unsigned int getCurBufLen(); /** * prepare an undo step for this sample. * @param data whether to include sample data. * @param doNotPush if this is true, don't push the DivSampleHistory to the undo history. * @return the undo step. */ DivSampleHistory* prepareUndo(bool data, bool doNotPush=false); /** * undo. you may need to call DivEngine::renderSamples afterwards. * @warning do not attempt to undo outside of a synchronized block! * @return 0 on failure; 1 on success and 2 on success (data changed). */ int undo(); /** * redo. you may need to call DivEngine::renderSamples afterwards. * @warning do not attempt to redo outside of a synchronized block! * @return 0 on failure; 1 on success and 2 on success (data changed). */ int redo(); DivSample(): name(""), rate(32000), centerRate(8363), loopStart(-1), loopEnd(-1), depth(DIV_SAMPLE_DEPTH_16BIT), loop(false), brrEmphasis(true), dither(false), loopMode(DIV_SAMPLE_LOOP_FORWARD), data8(NULL), data16(NULL), data1(NULL), dataDPCM(NULL), dataZ(NULL), dataQSoundA(NULL), dataA(NULL), dataB(NULL), dataK(NULL), dataBRR(NULL), dataVOX(NULL), dataMuLaw(NULL), dataC219(NULL), length8(0), length16(0), length1(0), lengthDPCM(0), lengthZ(0), lengthQSoundA(0), lengthA(0), lengthB(0), lengthK(0), lengthBRR(0), lengthVOX(0), lengthMuLaw(0), lengthC219(0), samples(0) { for (int i=0; i