diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/mods/module.cpp | 101 | ||||
-rw-r--r-- | sound/mods/module.h | 11 | ||||
-rw-r--r-- | sound/mods/protracker.cpp | 375 | ||||
-rw-r--r-- | sound/mods/protracker.h | 129 |
4 files changed, 173 insertions, 443 deletions
diff --git a/sound/mods/module.cpp b/sound/mods/module.cpp index 4c20c3a741..291f95fe3f 100644 --- a/sound/mods/module.cpp +++ b/sound/mods/module.cpp @@ -22,40 +22,16 @@ */ #include "common/stdafx.h" -#include "common/system.h" -#include "common/file.h" -#include "common/stream.h" #include "sound/mods/module.h" -namespace Modules { - -bool Module::load(const char *fn) { - Common::File f; - - if (!f.open(fn)) - return false; - - int bufsz = f.size(); - byte *buf = new byte[bufsz]; - - int r = f.read(buf, bufsz); - assert(r); - - f.close(); - - Common::MemoryReadStream st(buf, bufsz); +#include "common/util.h" - bool result = loadStream(st); - - delete[] buf; - - return result; -} +namespace Modules { -bool Module::loadStream(Common::SeekableReadStream &st) { +bool Module::load(Common::ReadStream &st) { st.read(songname, 20); - songname[0] = '\0'; + songname[20] = '\0'; for (int i = 0; i < 31; ++i) { st.read(sample[i].name, 22); @@ -68,18 +44,17 @@ bool Module::loadStream(Common::SeekableReadStream &st) { sample[i].vol = st.readByte(); sample[i].repeat = 2 * st.readUint16BE(); sample[i].replen = 2 * st.readUint16BE(); - - //printf("\"%s\"\tlen: %d\tfinetune: %d\tvol: %d\trepeat: %d\treplen: %d\n", - // sample[i].name, sample[i].len, sample[i].finetune, sample[i].vol, sample[i].repeat, sample[i].replen); - } songlen = 2 * st.readByte(); undef = 2 * st.readByte(); st.read(songpos, 128); - st.read(sig, 4); // Should be "M.K." - assert(0 == memcmp(sig, "M.K.", 4)); + st.read(sig, 4); + if (memcmp(sig, "M.K.", 4)) { + warning("Expected 'M.K.' in protracker module"); + return false; + } int maxpattern = 0; for (int i = 0; i < 128; ++i) @@ -93,62 +68,9 @@ bool Module::loadStream(Common::SeekableReadStream &st) { for (int k = 0; k < 4; ++k) { uint32 note = st.readUint32BE(); pattern[i][j][k].sample = (note & 0xf0000000) >> 24 | (note & 0x0000f000) >> 12; - pattern[i][j][k].period = (note >> 16) & 0xfff; pattern[i][j][k].effect = note & 0xfff; -/* - const char *notename; - - switch (pattern[i][j][k].period) { - case 856: notename = "C-1"; break; - case 808: notename = "C#1"; break; - case 762: notename = "D-1"; break; - case 720: notename = "D#1"; break; - case 678: notename = "E-1"; break; - case 640: notename = "E#1"; break; - case 604: notename = "F-1"; break; - case 570: notename = "F#1"; break; - case 538: notename = "A-1"; break; - case 508: notename = "A#1"; break; - case 480: notename = "B-1"; break; - case 453: notename = "B#1"; break; - case 428: notename = "C-2"; break; - case 404: notename = "C#2"; break; - case 381: notename = "D-2"; break; - case 360: notename = "D#2"; break; - case 339: notename = "E-2"; break; - case 320: notename = "E#2"; break; - case 302: notename = "F-2"; break; - case 285: notename = "F#2"; break; - case 269: notename = "A-2"; break; - case 254: notename = "A#2"; break; - case 240: notename = "B-2"; break; - case 226: notename = "B#2"; break; - case 214: notename = "C-3"; break; - case 202: notename = "C#3"; break; - case 190: notename = "D-3"; break; - case 180: notename = "D#3"; break; - case 170: notename = "E-3"; break; - case 160: notename = "E#3"; break; - case 151: notename = "F-3"; break; - case 143: notename = "F#3"; break; - case 135: notename = "A-3"; break; - case 127: notename = "A#3"; break; - case 120: notename = "B-3"; break; - case 113: notename = "B#3"; break; - case 0: notename = " "; break; - default: notename = "???"; break; - } - - if (k > 0) printf(" | "); - - if (pattern[i][j][k].sample) - printf("%2d %s %3X", pattern[i][j][k].sample, notename, pattern[i][j][k].effect); - else - printf(" %s %3X", notename, pattern[i][j][k].effect); -*/ } - //printf("\n"); } } @@ -156,12 +78,13 @@ bool Module::loadStream(Common::SeekableReadStream &st) { if (!sample[i].len) sample[i].data = 0; else { - sample[i].data = new byte[sample[i].len]; + sample[i].data = new int8[sample[i].len]; st.read((byte *)sample[i].data, sample[i].len); } } - assert(st.eos()); + if (!st.eos()) + warning("Expected EOS on module stream"); return true; } diff --git a/sound/mods/module.h b/sound/mods/module.h index 4073d5d4ec..1e87b7e1ea 100644 --- a/sound/mods/module.h +++ b/sound/mods/module.h @@ -24,7 +24,7 @@ #ifndef SOUND_MODS_MODULE_H #define SOUND_MODS_MODULE_H -#include "common/file.h" +#include "common/stream.h" namespace Modules { @@ -36,12 +36,16 @@ namespace Modules { * like they are in the file. */ +#include "common/pack-start.h" // START STRUCT PACKING + struct note_t { byte sample; uint16 period; uint16 effect; }; +#include "common/pack-end.h" // END STRUCT PACKING + typedef note_t pattern_t[64][4]; struct sample_t { @@ -51,7 +55,7 @@ struct sample_t { byte vol; uint16 repeat; uint16 replen; - byte *data; + int8 *data; }; class Module { @@ -69,8 +73,7 @@ public: Module(); ~Module(); - bool load(const char *fn); - bool loadStream(Common::SeekableReadStream &st); + bool load(Common::ReadStream &stream); }; } // End of namespace Modules diff --git a/sound/mods/protracker.cpp b/sound/mods/protracker.cpp index 74733d5c15..ab3776624b 100644 --- a/sound/mods/protracker.cpp +++ b/sound/mods/protracker.cpp @@ -22,80 +22,158 @@ */ #include "common/stdafx.h" -#include "common/system.h" #include "sound/mods/protracker.h" +#include "sound/mods/module.h" + +#include "sound/audiostream.h" namespace Modules { -#define DUMP 1 +class SoundBuffer { +private: + int _capacity; + int _size; + int16 *_data; + +public: + SoundBuffer() { + _size = 0; + _capacity = 8192; + _data = (int16 *)malloc(_capacity * sizeof(int16)); + assert(_data); + } -void ProtrackerPlayer::init(OSystem *system) { - _system = system; + ~SoundBuffer() { + free(_data); + } - _buf = new SoundBuffer(); + int size() { + return _size; + } -// FIXME BROKEN: You must NOT use OSystem::setSoundCallback(), -// ever! The only piece of code allowed to use it is the mixer. -// And soon the call might be removed completely, too. -// The proper way to do this is to create a AudioStream -// subclass and hook that with the mixer. See also the -// code used by other softsynths (sound/softsynth/emumidi.h). + int16 *getEnd() { + return _data + _size; + } -// _system->setSoundCallback(&audioCallback, this); -error("ProtrackerPlayer::init -- setSoundCallback is no more"); -} + void ensureCapacity(int len) { + if (_size + len > _capacity) { + do { + _capacity *= 2; + } while (_size + len > _capacity); -void ProtrackerPlayer::start() { - if (_module) { - _tick = _row = _pos = 0; - _hasJumpToPattern = false; - _hasPatternBreak = false; - _hasPatternLoop = false; - _patternLoopCount = 0; - _patternLoopRow = 0; - _speed = 6; - _bpm = 125; - - _generatedSamplesOverflow = 0.0; - - for (int t = 0; t < 4; t++) { - _track[t].sample = 0; - _track[t].period = 0; - _track[t].offset = 0.0; - - _track[t].vibrato = 0; + _data = (int16 *)realloc(_data, _capacity * sizeof(int16)); + assert(_data); } + } - //_system->startAudio(); + void finish(int len) { + _size += len; } -} -void ProtrackerPlayer::pause() { -} + void pop(int16 *dest, int len) { + assert(_size >= len); + memcpy(dest, _data, len * sizeof(int16)); + memmove(_data, _data + len, (_size - len) * sizeof(int16)); + _size -= len; + } +}; -void ProtrackerPlayer::stop() { - //_system->stopAudio(); -} +class ProtrackerStream : public ::Audio::AudioStream { +private: + Module *_module; -void ProtrackerPlayer::loadModule(const char *fn) { - if (_module) - delete _module; + int _rate; + SoundBuffer *_buf; + double _generatedSamplesOverflow; - _module = new Module(); - _module->load(fn); -} + int _tick; + int _row; + int _pos; + + int _patternDelay; + + int _speed; + int _bpm; + + // For effect 0xB - Jump To Pattern; + bool _hasJumpToPattern; + int _jumpToPattern; + + // For effect 0xD - PatternBreak; + bool _hasPatternBreak; + int _skiprow; + + // For effect 0xE6 - Pattern Loop + bool _hasPatternLoop; + int _patternLoopCount; + int _patternLoopRow; + + struct { + byte sample; + uint16 period; + double offset; + + byte vol; -void ProtrackerPlayer::loadModuleStream(Common::SeekableReadStream &fs) { - if (_module) - delete _module; + // For effect 0x3 - Porta to note + uint16 portaToNote; + byte portaToNoteSpeed; + // For effect 0x4 - Vibrato + int vibrato; + byte vibratoPos; + byte vibratoSpeed; + byte vibratoDepth; + } _track[4]; + +public: + ProtrackerStream(Common::ReadStream *stream, int rate); + + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return false; } + bool endOfData() const { return false; } + + int getRate() const { return _rate; } + +private: + void generateSound(); + + void updateRow(); + void updateEffects(); +}; + +ProtrackerStream::ProtrackerStream(Common::ReadStream *stream, int rate) { _module = new Module(); - _module->loadStream(fs); + bool result = _module->load(*stream); + assert(result); + + _buf = new SoundBuffer(); + + _rate = rate; + _tick = _row = _pos = 0; + _hasJumpToPattern = false; + _hasPatternBreak = false; + _hasPatternLoop = false; + _patternDelay = 0; + _patternLoopCount = 0; + _patternLoopRow = 0; + _speed = 6; + _bpm = 125; + + _generatedSamplesOverflow = 0.0; + + for (int t = 0; t < 4; t++) { + _track[t].sample = 0; + _track[t].period = 0; + _track[t].offset = 0.0; + + _track[t].vibrato = 0; + } } -void ProtrackerPlayer::generateSound() { - _generatedSamplesOverflow += 5.0 * 44100.0 / (2.0 * _bpm); +void ProtrackerStream::generateSound() { + _generatedSamplesOverflow += 5.0 * _rate / (2.0 * _bpm); int samples = (int)floor(_generatedSamplesOverflow); _generatedSamplesOverflow -= samples; @@ -113,11 +191,11 @@ void ProtrackerPlayer::generateSound() { (7093789.2 / 2.0) / (_track[track].period + _track[track].vibrato); - double rate = frequency / 44100.0; + double rate = frequency / _rate; double offset = _track[track].offset; int sample = _track[track].sample - 1; int slen = _module->sample[sample].len; - byte *data = _module->sample[sample].data; + int8 *data = _module->sample[sample].data; static bool did_warn_about_finetune = false; if (!did_warn_about_finetune && _module->sample[sample].finetune != 0) { @@ -128,22 +206,15 @@ void ProtrackerPlayer::generateSound() { if (_module->sample[sample].replen > 2) { int neededSamples = samples; - //printf("%d %d >= %d\n", (int)offset, (int)(offset + neededSamples*rate), _module->sample[sample].repeat + _module->sample[sample].replen); - while ((int)(offset + neededSamples * rate) >= (_module->sample[sample].repeat + _module->sample[sample].replen)) { - //puts("/* The repeat length is the limiting factor */"); - //printf("case 1: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol); + /* The repeat length is the limiting factor */ int end = (int)((_module->sample[sample]. repeat + _module->sample[sample]. replen - offset) / rate); - if ((int)(offset + rate * end) > slen) - warning("!!!!!!!!!"); - //printf("writing %d-%d max %d\n", (int)offset, (int)(offset + rate*end), slen); - for (int i = 0; i < end; i++) *p++ += _track[track].vol * data[(int)(offset + rate * i)]; @@ -153,11 +224,8 @@ void ProtrackerPlayer::generateSound() { neededSamples -= end; } if (neededSamples > 0) { - //puts("/* The requested number of samples is the limiting factor, not the repeat length */"); - //printf("case 2: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol); + /* The requested number of samples is the limiting factor, not the repeat length */ - //if ((int)(offset + rate*neededSamples) > slen) puts("!!!!!!!!!"); - //printf("writing %d-%d max %d\n", (int)offset, (int)(offset + rate*neededSamples), slen); for (int i = 0; i < neededSamples; i++) *p++ += _track[track].vol * data[(int)(offset + rate * i)]; @@ -169,7 +237,6 @@ void ProtrackerPlayer::generateSound() { if ((int)(offset + samples * rate) >= slen) { /* The end of the sample is the limiting factor */ - //printf("case 3: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol); int end = (int)((slen - offset) / rate); for (int i = 0; i < end; i++) @@ -178,7 +245,6 @@ void ProtrackerPlayer::generateSound() { _track[track].offset = slen; } else { /* The requested number of samples is the limiting factor, not the sample */ - //printf("case 4: period: %d\trate: %g\toffset: %g\tsample: %d\tslen: %d\tvol: %d\n", _track[track].period, rate, _track[track].offset, _track[track].sample, slen, _track[track].vol); for (int i = 0; i < samples; i++) *p++ += _track[track].vol * @@ -193,12 +259,7 @@ void ProtrackerPlayer::generateSound() { _buf->finish(samples); } -void ProtrackerPlayer::updateRow() { - //printf("ProtrackerPlayer::updateRow(): tick: %d\tpos: %d->%d\trow: %d\n", _tick, _pos, _module->songpos[_pos], _row); - -#ifdef DUMP - printf("%3d:%3d:%2d: ", _pos, _module->songpos[_pos], _row); -#endif +void ProtrackerStream::updateRow() { for (int track = 0; track < 4; track++) { _track[track].vibrato = 0; note_t note = @@ -224,136 +285,6 @@ void ProtrackerPlayer::updateRow() { int ex = (note.effect >> 4) & 0xf; int ey = note.effect & 0xf; -#ifdef DUMP - const char *notename; - - switch (note.period) { - case 856: - notename = "C-1"; - break; - case 808: - notename = "C#1"; - break; - case 762: - notename = "D-1"; - break; - case 720: - notename = "D#1"; - break; - case 678: - notename = "E-1"; - break; - case 640: - notename = "E#1"; - break; - case 604: - notename = "F-1"; - break; - case 570: - notename = "F#1"; - break; - case 538: - notename = "A-1"; - break; - case 508: - notename = "A#1"; - break; - case 480: - notename = "B-1"; - break; - case 453: - notename = "B#1"; - break; - case 428: - notename = "C-2"; - break; - case 404: - notename = "C#2"; - break; - case 381: - notename = "D-2"; - break; - case 360: - notename = "D#2"; - break; - case 339: - notename = "E-2"; - break; - case 320: - notename = "E#2"; - break; - case 302: - notename = "F-2"; - break; - case 285: - notename = "F#2"; - break; - case 269: - notename = "A-2"; - break; - case 254: - notename = "A#2"; - break; - case 240: - notename = "B-2"; - break; - case 226: - notename = "B#2"; - break; - case 214: - notename = "C-3"; - break; - case 202: - notename = "C#3"; - break; - case 190: - notename = "D-3"; - break; - case 180: - notename = "D#3"; - break; - case 170: - notename = "E-3"; - break; - case 160: - notename = "E#3"; - break; - case 151: - notename = "F-3"; - break; - case 143: - notename = "F#3"; - break; - case 135: - notename = "A-3"; - break; - case 127: - notename = "A#3"; - break; - case 120: - notename = "B-3"; - break; - case 113: - notename = "B#3"; - break; - case 0: - notename = " "; - break; - default: - notename = "???"; - break; - } - - if (track > 0) - printf(" | "); - - if (note.sample) - printf("%2d %s %3X", note.sample, notename, - note.effect); - else - printf(" %s %3X", notename, note.effect); -#endif - switch (effect) { case 0x0: break; @@ -406,32 +337,24 @@ void ProtrackerPlayer::updateRow() { case 0x9: break; // Retrigger note default: - printf("Unimplemented effect %X\n", - note.effect); + warning("Unimplemented effect %X\n", note.effect); } break; case 0xF: if (exy < 0x20) { _speed = exy; - //printf("Speed: %x\n", _speed); } else { _bpm = exy; - //printf("BPM: %x\n", _bpm); } break; default: - printf("Unimplemented effect %X\n", note.effect); + warning("Unimplemented effect %X\n", note.effect); } } -#ifdef DUMP - putchar('\n'); - fflush(NULL); -#endif } -void ProtrackerPlayer::updateEffects() { - //printf("ProtrackerPlayer::updateEffects(): tick: %d\tpos: %d->%d\trow: %d\n", _tick, _pos, _module->songpos[_pos], _row); +void ProtrackerStream::updateEffects() { static const int16 sinetable[64] = { 0, 24, 49, 74, 97, 120, 141, 161, @@ -456,8 +379,6 @@ void ProtrackerPlayer::updateEffects() { int ex = (note.effect >> 4) & 0xf; int ey = (note.effect) & 0xf; - //printf("track %d: period: %3d\tsample: %2d\teffect: %x\n", track, note.period, note.sample, note.effect); - int vol; switch (effect) { case 0x0: @@ -568,11 +489,8 @@ void ProtrackerPlayer::updateEffects() { } } -void ProtrackerPlayer::mix(byte *buf0, int len) { - int16 *buf = (int16 *)buf0; - len /= 2; - - while (_buf->size() < len) { +int ProtrackerStream::readBuffer(int16 *buffer, const int numSamples) { + while (_buf->size() < numSamples) { if (_tick == 0) { if (_hasJumpToPattern) { _hasJumpToPattern = false; @@ -608,12 +526,17 @@ void ProtrackerPlayer::mix(byte *buf0, int len) { generateSound(); } - _buf->pop(buf, len); -} + _buf->pop(buffer, numSamples); -void ProtrackerPlayer::audioCallback(void *param, byte *buf, int len) { - ((ProtrackerPlayer *)param)->mix(buf, len); + return numSamples; } } // End of namespace Modules +namespace Audio { + +AudioStream *makeProtrackerStream(Common::ReadStream *stream, int rate) { + return new Modules::ProtrackerStream(stream, rate); +} + +} // End of namespace Audio diff --git a/sound/mods/protracker.h b/sound/mods/protracker.h index c6511e8a47..d79694d26d 100644 --- a/sound/mods/protracker.h +++ b/sound/mods/protracker.h @@ -25,133 +25,14 @@ #define SOUND_MODS_PROTRACKER_H #include "common/stdafx.h" -#include "common/system.h" +#include "common/stream.h" -#include "sound/mods/module.h" +namespace Audio { -namespace Modules { +class AudioStream; -class SoundBuffer { -private: - int _capacity; - int _size; - int16 *_data; +AudioStream *makeProtrackerStream(Common::ReadStream *stream, int rate = 44100); -public: - SoundBuffer() { - _size = 0; - _capacity = 8192; - _data = (int16 *)malloc(_capacity * sizeof(int16)); - assert(_data); - } - - ~SoundBuffer() { - free(_data); - } - - int size() { - return _size; - } - - int16 *getEnd() { - return _data + _size; - } - - void ensureCapacity(int len) { - if (_size + len > _capacity) { - do { - _capacity *= 2; - } while (_size + len > _capacity); - - _data = (int16 *)realloc(_data, _capacity * sizeof(int16)); - assert(_data); - memset(_data + _size, 0, len); - } - } - - void finish(int len) { - _size += len; - } - - void pop(int16 *dest, int len) { - assert(_size >= len); - memcpy(dest, _data, len * sizeof(int16)); - memmove(_data, _data + len, (_size - len) * sizeof(int16)); - _size -= len; - } -}; - -class ProtrackerPlayer { -private: - OSystem *_system; - Module *_module; - - SoundBuffer *_buf; - double _generatedSamplesOverflow; - - int _tick; - int _row; - int _pos; - - int _patternDelay; - - int _speed; - int _bpm; - - // For effect 0xB - Jump To Pattern; - bool _hasJumpToPattern; - int _jumpToPattern; - - // For effect 0xD - PatternBreak; - bool _hasPatternBreak; - int _skiprow; - - // For effect 0xE6 - Pattern Loop - bool _hasPatternLoop; - int _patternLoopCount; - int _patternLoopRow; - - struct { - byte sample; - uint16 period; - double offset; - - byte vol; - - // For effect 0x3 - Porta to note - uint16 portaToNote; - byte portaToNoteSpeed; - - // For effect 0x4 - Vibrato - int vibrato; - byte vibratoPos; - byte vibratoSpeed; - byte vibratoDepth; - } _track[4]; - -public: - ProtrackerPlayer() : _system(0), _module(0) { }; - - void init(OSystem *system); - - void start(); - void pause(); - void stop(); - - void loadModule(const char *fn); - void loadModuleStream(Common::SeekableReadStream &fs); - - void mix(byte *buf, int len); - -private: - void generateSound(); - - void updateRow(); - void updateEffects(); - - static void audioCallback(void *param, byte *buf, int len); -}; - -} // End of namespace Modules +} // End of namespace Audio #endif |