diff options
author | Simei Yin | 2017-09-11 03:06:45 +0200 |
---|---|---|
committer | Eugene Sandulenko | 2017-09-11 08:10:09 +0200 |
commit | 6558578f5423b3e1f072a4a06e12a51f48e91102 (patch) | |
tree | c502290256460a5978fe2dc7b6039a44531e5f68 /audio/mods/module_mod_xm_s3m.cpp | |
parent | 59f8e62adf89af283d4dfa636d8981e59d34fc6f (diff) | |
download | scummvm-rg350-6558578f5423b3e1f072a4a06e12a51f48e91102.tar.gz scummvm-rg350-6558578f5423b3e1f072a4a06e12a51f48e91102.tar.bz2 scummvm-rg350-6558578f5423b3e1f072a4a06e12a51f48e91102.zip |
AUDIO: Import micromod code, xm/s3m/mod decoder
Diffstat (limited to 'audio/mods/module_mod_xm_s3m.cpp')
-rw-r--r-- | audio/mods/module_mod_xm_s3m.cpp | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/audio/mods/module_mod_xm_s3m.cpp b/audio/mods/module_mod_xm_s3m.cpp new file mode 100644 index 0000000000..6283dbe726 --- /dev/null +++ b/audio/mods/module_mod_xm_s3m.cpp @@ -0,0 +1,841 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + */ + +/* + * This code is based on IBXM mod player + * + * Copyright (c) 2015, Martin Cameron + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the + * following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * * Neither the name of the organization nor the names of + * its contributors may be used to endorse or promote + * products derived from this software without specific + * prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "common/debug.h" +#include "common/endian.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/util.h" +#include "module_mod_xm_s3m.h" + +namespace Modules { + +const int ModuleModXmS3m::FP_SHIFT = 0xF; +const int ModuleModXmS3m::FP_ONE = 0x8000; +const int ModuleModXmS3m::FP_MASK = 0x7FFF; + +const int ModuleModXmS3m::exp2table[] = { + 32768, 32946, 33125, 33305, 33486, 33667, 33850, 34034, + 34219, 34405, 34591, 34779, 34968, 35158, 35349, 35541, + 35734, 35928, 36123, 36319, 36516, 36715, 36914, 37114, + 37316, 37518, 37722, 37927, 38133, 38340, 38548, 38757, + 38968, 39180, 39392, 39606, 39821, 40037, 40255, 40473, + 40693, 40914, 41136, 41360, 41584, 41810, 42037, 42265, + 42495, 42726, 42958, 43191, 43425, 43661, 43898, 44137, + 44376, 44617, 44859, 45103, 45348, 45594, 45842, 46091, + 46341, 46593, 46846, 47100, 47356, 47613, 47871, 48131, + 48393, 48655, 48920, 49185, 49452, 49721, 49991, 50262, + 50535, 50810, 51085, 51363, 51642, 51922, 52204, 52488, + 52773, 53059, 53347, 53637, 53928, 54221, 54515, 54811, + 55109, 55408, 55709, 56012, 56316, 56622, 56929, 57238, + 57549, 57861, 58176, 58491, 58809, 59128, 59449, 59772, + 60097, 60423, 60751, 61081, 61413, 61746, 62081, 62419, + 62757, 63098, 63441, 63785, 64132, 64480, 64830, 65182, + 65536 +}; + +int ModuleModXmS3m::exp2(int x) { + int c, m, y; + int x0 = (x & FP_MASK) >> (FP_SHIFT - 7); + c = exp2table[x0]; + m = exp2table[x0 + 1] - c; + y = (m * (x & (FP_MASK >> 7)) >> 8) + c; + return (y << FP_SHIFT) >> (FP_SHIFT - (x >> FP_SHIFT)); +} + +int ModuleModXmS3m::log2(int x) { + int y = 16 << FP_SHIFT; + for (int step = y; step > 0; step >>= 1) { + if (exp2(y - step) >= x) { + y -= step; + } + } + return y; +} + +bool ModuleModXmS3m::load(Common::SeekableReadStream &st) { + int32 setPos = st.pos(); + + // xm file + char sigXm[18] = { 0 }; + st.read(sigXm, 17); + if (!memcmp(sigXm, "Extended Module: ", 17)) { + return loadXm(st); + } + st.seek(setPos); + + // s3m file + char sigS3m[4]; + st.skip(44); + st.read(sigS3m, 4); + if (!memcmp(sigS3m, "SCRM", 4)) { + st.seek(setPos); + return loadS3m(st); + } + st.seek(setPos); + + // mod file + return loadMod(st); +} + +ModuleModXmS3m::ModuleModXmS3m() { + sequenceLen = 1; + sequence = nullptr; + restartPos = 0; + + // patterns + numChannels = 4; + numPatterns = 1; + patterns = nullptr; + + // instruments + numInstruments = 1; + instruments = nullptr; + + // others + defaultGvol = 64; + defaultSpeed = 6; + defaultTempo = 125; + c2Rate = 8287; + gain = 64; + linearPeriods = false; + fastVolSlides = false; + defaultPanning = nullptr; //{ 51, 204, 204, 51 }; +} + +ModuleModXmS3m::~ModuleModXmS3m() { + // free song position + if (sequence) { + delete[] sequence; + sequence = nullptr; + } + + // free instruments + if (instruments) { + for (int i = 0; i <= numInstruments; ++i) { + // free samples + for (int j = 0; j < instruments[i].numSamples; ++j) { + if (instruments[i].samples[j].data) { + delete[] instruments[i].samples[j].data; + instruments[i].samples[j].data = nullptr; + } + } + delete[] instruments[i].samples; + instruments[i].samples = nullptr; + } + delete[] instruments; + instruments = nullptr; + } + + // free patterns + if (patterns) { + for (int i = 0; i < numPatterns; ++i) { + delete []patterns[i].notes; + } + delete[] patterns; + patterns = nullptr; + } + + // free default values + if (defaultPanning) { + delete[] defaultPanning; + defaultPanning = nullptr; + } +} + +bool ModuleModXmS3m::loadMod(Common::SeekableReadStream &st) { + // load song name + st.read(name, 20); + name[20] = '\0'; + + // load instruments + numInstruments = 31; + instruments = new Instrument[numInstruments + 1]; + memset(instruments, 0, sizeof(Instrument) * (numInstruments + 1)); + instruments[0].numSamples = 1; + instruments[0].samples = new Sample[1]; + memset(&instruments[0].samples[0], 0, sizeof(Sample)); + + for (int i = 1; i <= numInstruments; ++i) { + instruments[i].numSamples = 1; + instruments[i].samples = new Sample[1]; + memset(&instruments[i].samples[0], 0, sizeof(Sample)); + + // load sample + Sample &sample = instruments[i].samples[0]; + st.read((byte *)sample.name, 22); + sample.name[22] = '\0'; + sample.length = 2 * st.readUint16BE(); + + sample.finetune = st.readByte(); + assert(sample.finetune < 0x10); + + sample.volume = st.readByte(); + sample.loopStart = 2 * st.readUint16BE(); + sample.loopLength = 2 * st.readUint16BE(); + + if (sample.loopStart + sample.loopLength > sample.length) { + sample.loopLength = sample.length - sample.loopStart; + } + if (sample.loopLength < 4) { + sample.loopStart = sample.length; + sample.loopLength = 0; + } + } + + sequenceLen = st.readByte(); + if (sequenceLen > 128) + sequenceLen = 128; + + restartPos = 0; + st.readByte(); // undefined byte, should be 127 + + sequence = new byte[128]; + st.read(sequence, 128); + + // check signature + byte xx[2]; + st.read(xx, 2); // first 2 bytes of the signature + switch (st.readUint16BE()) { + case MKTAG16('K', '.'): /* M.K. */ + // Fall Through intended + case MKTAG16('K', '!'): /* M!K! */ + // Fall Through intended + case MKTAG16('T', '4'): /* FLT4 */ + // Fall Through intended + numChannels = 4; + c2Rate = 8287; + gain = 64; + break; + + case MKTAG16('H', 'N'): /* xCHN */ + numChannels = xx[0] - '0'; + c2Rate = 8363; + gain = 32; + break; + + case MKTAG16('C', 'H'): /* xxCH */ + numChannels = (xx[0] - '0') * 10 + xx[1] - '0'; + c2Rate = 8363; + gain = 32; + break; + + default: + warning("No known signature found in micromod module"); + return false; + + } + + // default values + defaultGvol = 64; + defaultSpeed = 6; + defaultTempo = 125; + defaultPanning = new byte[numChannels]; + for (int i = 0; i < numChannels; ++i) { + defaultPanning[i] = 51; + if ((i & 3) == 1 || (i & 3) == 2) { + defaultPanning[i] = 204; + } + } + + // load patterns + numPatterns = 0; + for (int i = 0; i < 128; ++i) + if (numPatterns < sequence[i]) + numPatterns = sequence[i]; + ++numPatterns; + + // load patterns + patterns = new Pattern[numPatterns]; + for (int i = 0; i < numPatterns; ++i) { + patterns[i].numChannels = numChannels; + patterns[i].numRows = 64; + + // load notes + /* + Old (amiga) noteinfo: + + _____byte 1_____ byte2_ _____byte 3_____ byte4_ + / \ / \ / \ / \ + 0000 0000-00000000 0000 0000-00000000 + + Upper four 12 bits for Lower four Effect command. + bits of sam- note period. bits of sam- + ple number. ple number. + */ + + int numNotes = patterns[i].numChannels * patterns[i].numRows; + patterns[i].notes = new Note[numNotes]; + memset(patterns[i].notes, 0, numNotes * sizeof(Note)); + for (int idx = 0; idx < numNotes; ++idx) { + byte first = st.readByte(); + byte second = st.readByte(); + byte third = st.readByte(); + byte fourth = st.readByte(); + + // period, key + uint period = (first & 0xF) << 8; + period = (period | second) * 4; + if (period >= 112 && period <= 6848) { + int key = -12 * log2((period << FP_SHIFT) / 29021); + key = (key + (key & (FP_ONE >> 1))) >> FP_SHIFT; + patterns[i].notes[idx].key = key; + } + + // instrument + uint ins = (third & 0xF0) >> 4; + ins = ins | (first & 0x10); + patterns[i].notes[idx].instrument = ins; + + // effect, param + byte effect = third & 0x0F; + byte param = fourth & 0xff; + if(param == 0 && (effect < 3 || effect == 0xA)) { + effect = 0; + } + if(param == 0 && (effect == 5 || effect == 6)) { + effect -= 2; + } + if(effect == 8 && numChannels == 4) { + effect = param = 0; + } + patterns[i].notes[idx].effect = effect; + patterns[i].notes[idx].param = param; + } + } + + // load data for the sample of instruments + for (int i = 1; i <= numInstruments; ++i) { + Sample &sample = instruments[i].samples[0]; + if (!sample.length) { + sample.data = 0; + } else { + sample.data = new int16[sample.length + 1]; + readSampleSint8(st, sample.length, sample.data); + sample.data[sample.loopStart + sample.loopLength] = sample.data[sample.loopStart]; + } + } + + return true; +} + +bool ModuleModXmS3m::loadXm(Common::SeekableReadStream &st) { + st.read(name, 20); + name[20] = '\0'; + st.readByte(); // reserved byte + + byte trackername[20]; + st.read(trackername, 20); + bool deltaEnv = !memcmp(trackername, "DigiBooster Pro", 15); + + uint16 version = st.readUint16LE(); + if (version != 0x0104) { + warning("XM format version must be 0x0104!"); + return false; + } + + uint offset = st.pos() + st.readUint32LE(); + + sequenceLen = st.readUint16LE(); + restartPos = st.readUint16LE(); + + numChannels = st.readUint16LE(); + numPatterns = st.readUint16LE(); + numInstruments = st.readUint16LE(); + linearPeriods = st.readUint16LE() & 0x1; + defaultGvol = 64; + defaultSpeed = st.readUint16LE(); + defaultTempo = st.readUint16LE(); + c2Rate = 8363; + gain = 64; + + defaultPanning = new byte[numChannels]; + for (int i = 0; i < numChannels; ++i) { + defaultPanning[i] = 128; + } + + sequence = new byte[sequenceLen]; + for (int i = 0; i < sequenceLen; ++i) { + int entry = st.readByte(); + sequence[i] = entry < numPatterns ? entry : 0; + } + + // load patterns + patterns = new Pattern[numPatterns]; + for (int i = 0; i < numPatterns; ++i) { + st.seek(offset, SEEK_SET); + offset += st.readUint32LE(); + if (st.readByte()) { + warning("Unknown pattern packing type!"); + return false; + } + patterns[i].numRows = st.readUint16LE(); + if (patterns[i].numRows < 1) + patterns[i].numRows = 1; + uint16 patDataLength = st.readUint16LE(); + offset += patDataLength; + + // load notes + patterns[i].numChannels = numChannels; + int numNotes = patterns[i].numRows * numChannels; + patterns[i].notes = new Note[numNotes]; + memset(patterns[i].notes, 0, numNotes * sizeof(Note)); + + if (patDataLength > 0) { + for (int j = 0; j < numNotes; ++j) { + Note ¬e = patterns[i].notes[j]; + byte cmp = st.readByte(); + if (cmp & 0x80) { + if (cmp & 1) + note.key = st.readByte(); + if (cmp & 2) + note.instrument = st.readByte(); + if (cmp & 4) + note.volume = st.readByte(); + if (cmp & 8) + note.effect = st.readByte(); + if (cmp & 16) + note.param = st.readByte(); + } else { + note.key = cmp; + note.instrument = st.readByte(); + note.volume = st.readByte(); + note.effect = st.readByte(); + note.param = st.readByte(); + } + if( note.effect >= 0x40 ) { + note.effect = note.param = 0; + } + } + } + } + + // load instruments + instruments = new Instrument[numInstruments + 1]; + memset(instruments, 0, (numInstruments + 1) * sizeof(Instrument)); + instruments[0].samples = new Sample[1]; + memset(instruments[0].samples, 0, sizeof(Sample)); + for (int i = 1; i <= numInstruments; ++i) { + st.seek(offset, SEEK_SET); + offset += st.readUint32LE(); + + Instrument &ins = instruments[i]; + st.read(ins.name, 22); + ins.name[22] = '\0'; + + st.readByte(); // Instrument type (always 0) + + // load sample number + int nSamples = st.readUint16LE(); + ins.numSamples = nSamples > 0 ? nSamples : 1; + ins.samples = new Sample[ins.numSamples]; + memset(ins.samples, 0, ins.numSamples * sizeof(Sample)); + st.readUint32LE(); // skip 4 byte + + // load instrument informations + if (nSamples > 0) { + for (int k = 0; k < 96; ++k) { + ins.keyToSample[k + 1] = st.readByte(); + } + int pointTick = 0; + for (int p = 0; p < 12; ++p) { + pointTick = (deltaEnv ? pointTick : 0) + st.readUint16LE(); + ins.volEnv.pointsTick[p] = pointTick; + ins.volEnv.pointsAmpl[p] = st.readUint16LE(); + } + pointTick = 0; + for (int p = 0; p < 12; ++p) { + pointTick = (deltaEnv ? pointTick : 0) + st.readUint16LE(); + ins.panEnv.pointsTick[p] = pointTick; + ins.panEnv.pointsAmpl[p] = st.readUint16LE(); + } + ins.volEnv.numPoints = st.readByte(); + if (ins.volEnv.numPoints > 12) + ins.volEnv.numPoints = 0; + ins.panEnv.numPoints = st.readByte(); + if (ins.panEnv.numPoints > 12) + ins.panEnv.numPoints = 0; + ins.volEnv.sustainTick = ins.volEnv.pointsTick[st.readByte() & 0xF]; + ins.volEnv.loopStartTick = ins.volEnv.pointsTick[st.readByte() & 0xF]; + ins.volEnv.loopEndTick = ins.volEnv.pointsTick[st.readByte() & 0xF]; + ins.panEnv.sustainTick = ins.panEnv.pointsTick[st.readByte() & 0xF]; + ins.panEnv.loopStartTick = ins.panEnv.pointsTick[st.readByte() & 0xF]; + ins.panEnv.loopEndTick = ins.panEnv.pointsTick[st.readByte() & 0xF]; + byte volParam = st.readByte(); + ins.volEnv.enabled = ins.volEnv.numPoints > 0 && (volParam & 0x1); + ins.volEnv.sustain = (volParam & 0x2) > 0; + ins.volEnv.looped = (volParam & 0x4) > 0; + byte panParam = st.readByte(); + ins.panEnv.enabled = ins.panEnv.numPoints > 0 && (panParam & 0x1); + ins.panEnv.sustain = (panParam & 0x2) > 0; + ins.panEnv.looped = (panParam & 0x4) > 0; + ins.vibType = st.readByte(); + ins.vibSweep = st.readByte(); + ins.vibDepth = st.readByte(); + ins.vibRate = st.readByte(); + ins.volFadeout = st.readUint16LE(); + } + + // load samples + uint samHeadOffset = offset; + offset += nSamples * 40; // offset for sample data + for (int j = 0; j < nSamples; ++j) { + // load sample head + st.seek(samHeadOffset, SEEK_SET); + samHeadOffset += 40; // increment + Sample &sample = ins.samples[j]; + uint samDataBytes = st.readUint32LE(); + uint samLoopStart = st.readUint32LE(); + uint samLoopLength = st.readUint32LE(); + sample.volume = st.readByte(); + sample.finetune = st.readSByte(); + byte loopType = st.readByte(); + bool looped = (loopType & 0x3) > 0; + bool pingPong = (loopType & 0x2) > 0; + bool sixteenBit = (loopType & 0x10) > 0; + sample.panning = st.readByte() + 1; + sample.relNote = st.readSByte(); + st.readByte(); // reserved byte + st.read(sample.name, 22); + sample.name[22] = '\0'; + + uint samDataSamples = samDataBytes; + if (sixteenBit) { + samDataSamples = samDataSamples >> 1; + samLoopStart = samLoopStart >> 1; + samLoopLength = samLoopLength >> 1; + } + if (!looped || (samLoopStart + samLoopLength) > samDataSamples) { + samLoopStart = samDataSamples; + samLoopLength = 0; + } + sample.loopStart = samLoopStart; + sample.loopLength = samLoopLength; + + // load sample data + st.seek(offset, SEEK_SET); + offset += samDataBytes; // increment + sample.data = new int16[samDataSamples + 1]; + if (sixteenBit) { + readSampleSint16LE(st, samDataSamples, sample.data); + } else { + readSampleSint8(st, samDataSamples, sample.data); + } + int amp = 0; + for (uint idx = 0; idx < samDataSamples; idx++) { + amp = amp + sample.data[idx]; + amp = (amp & 0x7FFF) - (amp & 0x8000); + sample.data[idx] = amp; + } + sample.data[samLoopStart + samLoopLength] = sample.data[samLoopStart]; + if (pingPong) { + SamplePingPong(sample); + } + } + } + return true; +} + +bool ModuleModXmS3m::loadS3m(Common::SeekableReadStream &st) { + st.read(name, 28); + name[28] = '\0'; + st.skip(4); // skip 4 bytes + + sequenceLen = st.readUint16LE(); + numInstruments = st.readUint16LE(); + numPatterns = st.readUint16LE(); + uint16 flags = st.readUint16LE(); + uint16 version = st.readUint16LE(); + fastVolSlides = ((flags & 0x40) == 0x40) || version == 0x1300; + bool signedSamples = st.readUint16LE() == 1; + + // check signature + if (st.readUint32BE() != MKTAG('S', 'C', 'R', 'M')) { + warning("Not an S3M file!"); + return false; + } + + defaultGvol = st.readByte(); + defaultSpeed = st.readByte(); + defaultTempo = st.readByte(); + c2Rate = 8363; + byte mastermult = st.readByte(); + gain = mastermult & 0x7F; + bool stereoMode = (mastermult & 0x80) == 0x80; + st.readByte(); // skip ultra-click + bool defaultPan = st.readByte() == 0xFC; + st.skip(10); // skip 10 bytes + + // load channel map + numChannels = 0; + int channelMap[32]; + for (int i = 0; i < 32; ++i) { + channelMap[i] = -1; + if (st.readByte() < 16) { + channelMap[i] = numChannels++; + } + } + + // load sequence + sequence = new byte[sequenceLen]; + st.read(sequence, sequenceLen); + + int moduleDataIndex = st.pos(); + + // load instruments + instruments = new Instrument[numInstruments + 1]; + memset(instruments, 0, sizeof(Instrument) * (numInstruments + 1)); + instruments[0].numSamples = 1; + instruments[0].samples = new Sample[1]; + memset(instruments[0].samples, 0, sizeof(Sample)); + for (int i = 1; i <= numInstruments; ++i) { + Instrument &instrum = instruments[i]; + instrum.numSamples = 1; + instrum.samples = new Sample[1]; + memset(instrum.samples, 0, sizeof(Sample)); + Sample &sample = instrum.samples[0]; + + // get instrument offset + st.seek(moduleDataIndex, SEEK_SET); + int instOffset = st.readUint16LE() << 4; + moduleDataIndex += 2; + st.seek(instOffset, SEEK_SET); + + // load instrument, sample + if (st.readByte() == 1) { // type + st.skip(12); // skip file name + int sampleOffset = (st.readByte() << 20) + (st.readUint16LE() << 4); + uint sampleLength = st.readUint32LE(); + uint loopStart = st.readUint32LE(); + uint loopLength = st.readUint32LE() - loopStart; + sample.volume = st.readByte(); + st.skip(1); // skip dsk + if (st.readByte() != 0) { + warning("Packed samples not supported for S3M files"); + return false; + } + byte samParam = st.readByte(); + + if (loopStart + loopLength > sampleLength) { + loopLength = sampleLength - loopStart; + } + if (loopLength < 1 || !(samParam & 0x1)) { + loopStart = sampleLength; + loopLength = 0; + } + + sample.loopStart = loopStart; + sample.loopLength = loopLength; + + bool sixteenBit = samParam & 0x4; + int tune = (log2(st.readUint32LE()) - log2(c2Rate)) * 12; + sample.relNote = tune >> FP_SHIFT; + sample.finetune = (tune & FP_MASK) >> (FP_SHIFT - 7); + st.skip(12); // skip unused bytes + st.read(instrum.name, 28); + + // load sample data + sample.data = new int16[sampleLength + 1]; + st.seek(sampleOffset, SEEK_SET); + if (sixteenBit) { + readSampleSint16LE(st, sampleLength, sample.data); + } else { + readSampleSint8(st, sampleLength, sample.data); + } + if (!signedSamples) { + for (uint idx = 0; idx < sampleLength; ++idx) { + sample.data[idx] = (sample.data[idx] & 0xFFFF) - 32768; + } + } + sample.data[loopStart + loopLength] = sample.data[loopStart]; + } + } + + // load patterns + patterns = new Pattern[numPatterns]; + memset(patterns, 0, numPatterns * sizeof(Pattern)); + for (int i = 0; i < numPatterns; ++i) { + patterns[i].numChannels = numChannels; + patterns[i].numRows = 64; + + // get pattern data offset + st.seek(moduleDataIndex, SEEK_SET); + int patOffset = (st.readUint16LE() << 4) + 2; + st.seek(patOffset, SEEK_SET); + + // load notes + patterns[i].notes = new Note[numChannels * 64]; + memset(patterns[i].notes, 0, numChannels * 64 * sizeof(Note)); + int row = 0; + while (row < 64) { + byte token = st.readByte(); + if (token) { + byte key = 0; + byte ins = 0; + if ((token & 0x20) == 0x20) { + /* Key + Instrument.*/ + key = st.readByte(); + ins = st.readByte(); + if (key < 0xFE) { + key = (key >> 4) * 12 + (key & 0xF) + 1; + } else if (key == 0xFF) { + key = 0; + } + } + byte volume = 0; + if ((token & 0x40) == 0x40) { + /* Volume Column.*/ + volume = (st.readByte() & 0x7F) + 0x10; + if (volume > 0x50) { + volume = 0; + } + } + byte effect = 0; + byte param = 0; + if ((token & 0x80) == 0x80) { + /* Effect + Param.*/ + effect = st.readByte(); + param = st.readByte(); + if (effect < 1 || effect >= 0x40) { + effect = param = 0; + } else if (effect > 0) { + effect += 0x80; + } + } + int chan = channelMap[token & 0x1F]; + if (chan >= 0) { + int noteIndex = row * numChannels + chan; + patterns[i].notes[noteIndex].key = key; + patterns[i].notes[noteIndex].instrument = ins; + patterns[i].notes[noteIndex].volume = volume; + patterns[i].notes[noteIndex].effect = effect; + patterns[i].notes[noteIndex].param = param; + } + } else { + row++; + } + } + + // increment index + moduleDataIndex += 2; + } + + // load default panning + defaultPanning = new byte[numChannels]; + memset(defaultPanning, 0, numChannels); + for (int chan = 0; chan < 32; ++chan) { + if (channelMap[chan] >= 0) { + byte panning = 7; + if (stereoMode) { + panning = 12; + st.seek(64 + chan, SEEK_SET); + if (st.readByte() < 8) { + panning = 3; + } + } + if (defaultPan) { + st.seek(moduleDataIndex + chan, SEEK_SET); + flags = st.readByte(); + if ((flags & 0x20) == 0x20) { + panning = flags & 0xF; + } + } + defaultPanning[channelMap[chan]] = panning * 17; + } + } + return true; +} + +void ModuleModXmS3m::readSampleSint8(Common::SeekableReadStream &stream, int length, int16 *dest) { + for (int i = 0; i < length; ++i) { + dest[i] = (stream.readSByte() << 8); + dest[i] = (dest[i] & 0x7FFF) - (dest[i] & 0x8000); + } +} + +void ModuleModXmS3m::readSampleSint16LE(Common::SeekableReadStream &stream, int length, int16 *dest) { + for (int i = 0; i < length; ++i) { + dest[i] = stream.readSint16LE(); + dest[i] = (dest[i] & 0x7FFF) - (dest[i] & 0x8000); + } +} + +void ModuleModXmS3m::SamplePingPong(Sample &sample) { + int loopStart = sample.loopStart; + int loopLength = sample.loopLength; + int loopEnd = loopStart + loopLength; + int16 *sampleData = sample.data; + int16 *newData = new int16[loopEnd + loopLength + 1]; + if (newData) { + memcpy(newData, sampleData, loopEnd * sizeof(int16)); + for (int idx = 0; idx < loopLength; idx++) { + newData[loopEnd + idx] = sampleData[loopEnd - idx - 1]; + } + delete []sample.data; + sample.data = newData; + sample.loopLength *= 2; + sample.data[loopStart + sample.loopLength] = sample.data[loopStart]; + } +} + +} // End of namespace Modules |