├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── datareader.cpp ├── datareader.h ├── dmf.cpp ├── dmf.h ├── doc ├── DMF_SPECS_0x12.txt ├── DMF_SPECS_0x13.txt ├── DMF_SPECS_0x15.txt ├── DMF_SPECS_0x17.txt └── DMF_SPECS_0x18.txt ├── extra ├── vgm2asm.cpp └── vol_env_01.vgm ├── main.cpp ├── mul.tables.c ├── pce.cpp ├── pce.h ├── pce ├── frequency.inc ├── player.asm ├── player.h ├── player_standalone.asm └── task_manager.asm ├── pcewriter.cpp ├── pcewriter.h └── sin.table.c /.gitignore: -------------------------------------------------------------------------------- 1 | *build 2 | *.pce 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.12) 2 | 3 | project( 4 | dmfread 5 | VERSION 0.1.0 6 | LANGUAGES C CXX 7 | ) 8 | 9 | add_executable(mul.tables mul.tables.c) 10 | target_compile_features(mul.tables PUBLIC c_std_11) 11 | 12 | add_executable(sin.table sin.table.c) 13 | target_link_libraries(sin.table m) 14 | target_compile_features(sin.table PUBLIC c_std_11) 15 | 16 | add_custom_target( 17 | tables ALL 18 | COMMAND sin.table sin.inc 19 | COMMAND mul.tables mul.inc 20 | BYPRODUCTS mul.inc sin.inc 21 | ) 22 | 23 | find_package(ZLIB) 24 | # [todo] find libsamplerate 25 | 26 | add_executable(dmfread datareader.cpp dmf.cpp main.cpp pce.cpp pcewriter.cpp) 27 | target_link_libraries(dmfread ${ZLIB_LIBRARIES} samplerate) 28 | target_include_directories(dmfread PRIVATE ${ZLIB_INCLUDE_DIRS}) 29 | target_compile_features(dmfread PUBLIC cxx_std_11) 30 | 31 | install(TARGETS dmfread DESTINATION bin) 32 | install(FILES $/mul.inc $/sin.inc DESTINATION pce) 33 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/pce/ DESTINATION pce) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of dmf-player nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dmf-player 2 | ===== 3 | DefleMask DMF converter/player for PC-Engine. 4 | 5 | ## License 6 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 7 | 8 | ## Author(s) 9 | Vincent "MooZ" Cruz -------------------------------------------------------------------------------- /datareader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Vincent "MooZ" Cruz and other contributors. All rights reserved. 2 | // Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "datareader.h" 9 | 10 | namespace DMF { 11 | 12 | static const char* FormatString = ".DelekDefleMask."; 13 | #define CHUNK_SIZE 16384 14 | 15 | /// Constructor 16 | DataReader::DataReader() 17 | : _buffer() 18 | , _offset(0) 19 | {} 20 | /// Destructor 21 | DataReader::~DataReader() 22 | {} 23 | /// Decompress input file. 24 | /// @param [in] source Source file. 25 | /// @return true if the file was successfully decompressed. 26 | bool DataReader::decompress(FILE* source) { 27 | z_stream stream; 28 | int ret; 29 | size_t nBytes, offset; 30 | 31 | std::array in, out; 32 | 33 | stream.zalloc = Z_NULL; 34 | stream.zfree = Z_NULL; 35 | stream.opaque = Z_NULL; 36 | stream.avail_in = 0; 37 | stream.next_in = Z_NULL; 38 | 39 | ret = inflateInit(&stream); 40 | if(ret != Z_OK) { 41 | return false; 42 | } 43 | 44 | bool ok = true; 45 | do { 46 | stream.avail_in = fread(&in[0], 1, in.size(), source); 47 | if(ferror(source)) { 48 | ret = Z_ERRNO; 49 | break; 50 | } 51 | if(!stream.avail_in) { 52 | ok = false; 53 | break; 54 | } 55 | stream.next_in = &in[0]; 56 | 57 | do { 58 | stream.avail_out = out.size(); 59 | stream.next_out = &out[0]; 60 | 61 | ret = inflate(&stream, Z_NO_FLUSH); 62 | if((ret == Z_STREAM_ERROR) || 63 | (ret == Z_DATA_ERROR) || 64 | (ret == Z_MEM_ERROR)) { 65 | ok = false; 66 | } 67 | else { 68 | nBytes = out.size() - stream.avail_out; 69 | offset = _buffer.size(); 70 | _buffer.resize(offset + nBytes); 71 | memcpy(&_buffer[offset], &out[0], nBytes); 72 | } 73 | }while(!stream.avail_out && ok); 74 | 75 | }while((ret != Z_STREAM_END) && ok); 76 | 77 | inflateEnd(&stream); 78 | return (ok && (Z_STREAM_END == ret)); 79 | } 80 | 81 | /// Load song. 82 | /// @param [in] filename Song filename. 83 | /// @param [out] song Decoded song. 84 | /// @return true if song is successfully loaded. 85 | bool DataReader::load(const std::string& filename, Song &song) { 86 | FILE *source = fopen(filename.c_str(), "rb"); 87 | if(!source) { 88 | return false; 89 | } 90 | 91 | bool ok = decompress(source); 92 | if(ok) { 93 | ok = read(song); 94 | } 95 | 96 | fclose(source); 97 | return ok; 98 | } 99 | /// Get song raw size. 100 | size_t DataReader::size() { 101 | return _buffer.size(); 102 | } 103 | /// Read a single unsigned byte from buffer. 104 | /// @param [out] v Unsigned byte read from buffer. 105 | /// @return false if there is no data left to be read. 106 | bool DataReader::read(uint8_t& v) { 107 | if((_buffer.size() - _offset) < 1) { 108 | return false; 109 | } 110 | v = _buffer[_offset++]; 111 | return true; 112 | } 113 | /// Read a single unsigned short (2 bytes) from buffer. 114 | /// @param [out] v Unsigned short read from buffer. 115 | /// @return false if there is no data left to be read. 116 | bool DataReader::read(uint16_t& v) { 117 | if((_buffer.size() - _offset) < 2) { 118 | return false; 119 | } 120 | v = _buffer[_offset++]; 121 | v |= _buffer[_offset++] << 8; 122 | return true; 123 | } 124 | /// Read a single unsigned word (4 bytes) from buffer. 125 | /// @param [out] v Unsigned word read from buffer. 126 | /// @return false if there is no data left to be read. 127 | bool DataReader::read(uint32_t& v) { 128 | if((_buffer.size() - _offset) < 4) { 129 | return false; 130 | } 131 | v = _buffer[_offset++]; 132 | v |= _buffer[_offset++] << 8; 133 | v |= _buffer[_offset++] << 16; 134 | v |= _buffer[_offset++] << 24; 135 | return true; 136 | } 137 | /// Read n bytes from buffer. 138 | /// @param [in][out] ptr Pointer to output buffer. 139 | /// @param [in] nBytes Number of bytes to read. 140 | /// @return false if there is no data left to be read. 141 | bool DataReader::read(void* ptr, size_t nBytes) { 142 | if((_buffer.size() - _offset) < nBytes) { 143 | return false; 144 | } 145 | memcpy(ptr, &_buffer[_offset], nBytes); 146 | _offset+= nBytes; 147 | return true; 148 | } 149 | /// Read string. 150 | /// @param [out] String Output string. 151 | /// @return false if there is no data left to be read. 152 | bool DataReader::read(String& str) { 153 | bool ok = read(str.length); 154 | if(ok) { 155 | ok = read(str.data, str.length); 156 | } 157 | return ok; 158 | } 159 | /// Read envelope. 160 | /// @param [out] env Volume envelope. 161 | /// @return false if there is no data left to be read. 162 | bool DataReader::read(Envelope& env) { 163 | bool ok = read(env.size); 164 | if(ok && env.size) { 165 | ok = read(env.value, 4*env.size); 166 | if(ok) { 167 | ok = read(env.loop); 168 | } 169 | } 170 | return ok; 171 | } 172 | /// Read a single instrument. 173 | /// @param [out] inst Instrument. 174 | /// @return false if there is no data left to be read. 175 | bool DataReader::read(Instrument& inst) { 176 | bool ok = read(inst.name); 177 | if(ok) { 178 | ok = read(inst.mode); 179 | } 180 | if(!inst.mode) { 181 | if(ok) { ok = read(inst.std.volume); } 182 | if(ok) { ok = read(inst.std.arpeggio); } 183 | if(ok) { ok = read(inst.std.arpeggioMode); } 184 | if(ok) { ok = read(inst.std.noise); } 185 | if(ok) { ok = read(inst.std.wave); } 186 | } 187 | else { 188 | fprintf(stderr, "FM instruments are not yet supported!\n"); 189 | ok = false; 190 | } 191 | return ok; 192 | } 193 | /// Read a wave table. 194 | /// @param [out] wav Wave table. 195 | /// @return false if there is no data left to be read. 196 | bool DataReader::read(WaveTable& tbl) { 197 | bool ok; 198 | uint32_t size; 199 | 200 | ok = read(size); 201 | 202 | if(!size) { 203 | return true; 204 | } 205 | if(!ok) { 206 | return false; 207 | } 208 | 209 | tbl.resize(size); 210 | ok = read(&tbl[0], size*4); 211 | if(!ok) { 212 | tbl.clear(); 213 | } 214 | return ok; 215 | } 216 | /// Read song info. 217 | /// @param [out] Song info. 218 | /// @return false if there is no data left to be read. 219 | bool DataReader::read(Infos& nfo) { 220 | bool ok; 221 | ok = read(nfo.version); 222 | if(ok) { 223 | ok = read(nfo.system); 224 | } 225 | if(ok) { 226 | switch(nfo.system) { 227 | case SYSTEM_YMU759: 228 | nfo.systemChanCount = CHAN_COUNT_YMU759; 229 | break; 230 | case SYSTEM_GENESIS: 231 | nfo.systemChanCount = CHAN_COUNT_GENESIS; 232 | break; 233 | case SYSTEM_GENESIS_EXT_CH3: 234 | nfo.systemChanCount = CHAN_COUNT_GENESIS_EXT_CH3; 235 | break; 236 | case SYSTEM_SMS: 237 | nfo.systemChanCount = CHAN_COUNT_SMS; 238 | break; 239 | case SYSTEM_GAMEBOY: 240 | nfo.systemChanCount = CHAN_COUNT_GAMEBOY; 241 | break; 242 | case SYSTEM_PCENGINE: 243 | nfo.systemChanCount = CHAN_COUNT_PCENGINE; 244 | break; 245 | case SYSTEM_NES: 246 | nfo.systemChanCount = CHAN_COUNT_NES; 247 | break; 248 | case SYSTEM_C64_8580: 249 | case SYSTEM_C64_6581: 250 | nfo.systemChanCount = CHAN_COUNT_C64; 251 | break; 252 | case SYSTEM_YM2151: 253 | nfo.systemChanCount = CHAN_COUNT_YM2151; 254 | break; 255 | default: 256 | return false; 257 | }; 258 | 259 | ok = read(nfo.name); 260 | } 261 | if(ok) { ok = read(nfo.author); } 262 | if(ok) { ok = read(nfo.highlight, 2); } 263 | if(ok) { ok = read(nfo.timeBase); } 264 | if(ok) { ok = read(nfo.tickTime, 2); } 265 | if(ok) { ok = read(nfo.framesMode); } 266 | if(ok) { ok = read(nfo.customFreqFlag); } 267 | if(ok) { ok = read(nfo.customFreqValue, 3); } 268 | if(ok) { 269 | if(nfo.version > 21 ) { 270 | ok = read(nfo.totalRowsPerPattern); 271 | } 272 | else { 273 | uint8_t dummy; 274 | ok = read(dummy); 275 | nfo.totalRowsPerPattern = dummy; 276 | } 277 | } 278 | if(ok) { ok = read(nfo.totalRowsInPatternMatrix); } 279 | if(ok) { 280 | if(nfo.version < 21) { 281 | ok = read(nfo.arpeggioTickSpeed); 282 | } 283 | } 284 | return ok; 285 | } 286 | /// Read pattern data. 287 | /// @param [out] pat Pattern data. 288 | /// @param [in] effectCount Number of effects per pattern entry. 289 | /// @return false if there is no data left to be read. 290 | bool DataReader::read(PatternData& data, uint8_t effectCount) { 291 | bool ok; 292 | ok = read(data.note); 293 | if(ok) { ok = read(data.octave); } 294 | if(ok) { ok = read(data.volume); } 295 | for(size_t i=0; i 21)) { 318 | ok = read(sample.name); 319 | } 320 | if(ok) { ok = read(sample.rate); } 321 | if(ok) { ok = read(sample.pitch); } 322 | if(ok) { ok = read(sample.amp); } 323 | if(ok) { ok = read(sample.bits); } 324 | if(ok && size) { 325 | sample.data.resize(size); 326 | for(size_t i=0; ok && (i _buffer.size())) { 444 | return false; 445 | } 446 | int ret; 447 | ret = memcmp(&_buffer[_offset], src, len); 448 | if(ret) { 449 | return false; 450 | } 451 | _offset += len; 452 | return true; 453 | } 454 | 455 | bool operator==(const PatternData &d0, const PatternData &d1) { 456 | for(int i=0; i(-1); 471 | std::vector data; 472 | std::vector source; 473 | data.resize(song.infos.systemChanCount * song.infos.totalRowsInPatternMatrix * song.infos.totalRowsPerPattern); 474 | for(size_t i=0; i mapping; 477 | for(size_t j=0; j= mapping.size()) { 480 | mapping.resize(pattern_index + 1, unknown); 481 | } 482 | if(mapping[pattern_index] == unknown) { 483 | size_t start = ((i * song.infos.totalRowsInPatternMatrix) + j) * song.infos.totalRowsPerPattern; 484 | size_t end = start + song.infos.totalRowsPerPattern; 485 | size_t dest = ((i * song.infos.totalRowsInPatternMatrix) + last) * song.infos.totalRowsPerPattern; 486 | std::copy(&song.patternData[start], &song.patternData[end], &data[dest]); 487 | 488 | mapping[pattern_index] = last++; 489 | } 490 | 491 | song.patternMatrix[(i*song.infos.totalRowsInPatternMatrix) + j] = mapping[pattern_index]; 492 | } 493 | } 494 | song.patternData = std::move(data); 495 | } 496 | 497 | void DataReader::fixInstruments(Song &song) { 498 | for(size_t i=0; i _buffer; 96 | /// Current read offset in byte buffer. 97 | size_t _offset; 98 | }; 99 | 100 | } // DMF 101 | 102 | #endif // DATA_READER_H 103 | -------------------------------------------------------------------------------- /dmf.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2019, Vincent "MooZ" Cruz and other contributors. All rights reserved. 2 | // Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 3 | #include "dmf.h" 4 | 5 | #include 6 | 7 | namespace DMF { 8 | 9 | /// Check if a pattern element is empty. 10 | /// @param [in] src Pattern data. 11 | /// @param [in] count Effect count. 12 | /// @return true if the pattern element is empty, false otherwise. 13 | bool isEmpty(PatternData const& src, unsigned int count) { 14 | bool empty_fx = true; 15 | for(unsigned int i=0; (i 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAX_STRING_BUFFER_LEN 255 12 | #define DMF_MAX_EFFECT_COUNT 8 13 | 14 | namespace DMF { 15 | 16 | /// Supported systems. 17 | enum System : uint8_t { 18 | /// Yamaha YMU759 19 | SYSTEM_YMU759 = 0x01, 20 | /// Sega Genesis 21 | /// Yamaha YM2612 22 | /// Texas Instruments SN76489 23 | SYSTEM_GENESIS = 0x02, 24 | /// Sega Genesis (Ext CH3) 25 | SYSTEM_GENESIS_EXT_CH3 = 0x12, 26 | /// Sega Master System 27 | /// Texas Instruments SN76489 28 | SYSTEM_SMS = 0x03, 29 | /// Nintendo Gameboy 30 | /// Z80 Variant 31 | SYSTEM_GAMEBOY = 0x04, 32 | /// NEC PC-Engine 33 | /// Hudson Soft HuC6280 34 | SYSTEM_PCENGINE = 0x05, 35 | /// Nintendo Famicom 36 | /// Ricoh 2A03 37 | SYSTEM_NES = 0x06, 38 | /// Commodore 64 39 | /// MOS Technology SID 8580 40 | SYSTEM_C64_8580 = 0x07, 41 | /// Commodore 64 42 | /// MOS Technology SID 6581 43 | SYSTEM_C64_6581 = 0x17, 44 | /// Yamaha YM2151 45 | SYSTEM_YM2151 = 0x08 46 | }; 47 | 48 | /// Number of channels for each supported system. 49 | enum Channels { 50 | CHAN_COUNT_YMU759 = 17, 51 | CHAN_COUNT_GENESIS = 10, 52 | CHAN_COUNT_GENESIS_EXT_CH3 = 13, 53 | CHAN_COUNT_SMS = 4, 54 | CHAN_COUNT_GAMEBOY = 4, 55 | CHAN_COUNT_PCENGINE = 6, 56 | CHAN_COUNT_NES = 5, 57 | CHAN_COUNT_C64 = 3, 58 | CHAN_COUNT_YM2151 = 8 59 | }; 60 | 61 | /// Frames mode. 62 | enum FramesMode { 63 | PAL = 0, 64 | NTSC = 1 65 | }; 66 | 67 | /// Effect list. 68 | enum Effect : uint8_t { 69 | // Standard Commands 70 | ARPEGGIO = 0x00, 71 | PORTAMENTO_UP = 0x01, 72 | PORTAMENTO_DOWN = 0x02, 73 | PORTAMENTO_TO_NOTE = 0x03, 74 | VIBRATO = 0x04, 75 | PORTAMENTO_TO_NOTE_VOLUME_SLIDE = 0x05, 76 | VIBRATO_VOLUME_SIDE = 0x06, 77 | TREMOLO = 0x07, 78 | PANNING = 0x08, 79 | SET_SPEED_VALUE_1 = 0x09, 80 | VOLUME_SLIDE = 0x0A, 81 | POSITION_JUMP = 0x0B, 82 | RETRIG = 0x0C, 83 | PATTERN_BREAK = 0x0D, 84 | SET_SPEED_VALUE_2 = 0x0F, 85 | // Extended Commands 86 | ARPEGGIO_SPEED = 0xE0, 87 | NOTE_SLIDE_UP = 0xE1, 88 | NOTE_SLIDE_DOWN = 0xE2, 89 | VIBRATO_MODE = 0xE3, 90 | VIBRATO_DEPTH = 0xE4, 91 | FINE_TUNE = 0xE5, 92 | SET_SAMPLES_BANK = 0xEB, 93 | NOTE_CUT = 0xEC, 94 | NOTE_DELAY = 0xED, 95 | SYNC_SIGNAL = 0xEE, 96 | GLOBAL_FINE_TUNE = 0xEF, 97 | // PC Engine Commands 98 | SET_WAVE = 0x10, 99 | SET_NOISE = 0x11, 100 | SET_LFO_MODE = 0x12, 101 | SET_LFO_SPEED = 0x13, 102 | SET_SAMPLES = 0x17 103 | }; 104 | 105 | /// DMF fixed size string. 106 | struct String { 107 | /// String length. 108 | uint8_t length; 109 | /// String data. 110 | char data[MAX_STRING_BUFFER_LEN]; 111 | }; 112 | 113 | /// DMG song infos. 114 | struct Infos { 115 | /// DMF version. 116 | uint8_t version; 117 | /// Target system. 118 | uint8_t system; 119 | /// Song name. 120 | String name; 121 | /// Author. 122 | String author; 123 | /// Rows lines highlighting. 124 | uint8_t highlight[2]; 125 | /// The base time value multiplies the tick time value. 126 | uint8_t timeBase; 127 | /// Tick time. 128 | uint8_t tickTime[2]; 129 | /// Frames mode. 130 | uint8_t framesMode; 131 | /// Custom frequency flag. 132 | uint8_t customFreqFlag; 133 | /// Custom frequency value. 134 | uint8_t customFreqValue[3]; 135 | /// Number of rows per pattern. 136 | uint32_t totalRowsPerPattern; 137 | /// Total number of rows in the pattern matrix. 138 | uint8_t totalRowsInPatternMatrix; 139 | /// Speed of the arpeggio command. 140 | uint8_t arpeggioTickSpeed; 141 | /// Number of channels. 142 | uint8_t systemChanCount; 143 | }; 144 | 145 | /// Envelope. 146 | struct Envelope { 147 | /// Length. 148 | uint8_t size; 149 | /// Loop index. 150 | uint8_t loop; 151 | /// Data. 152 | uint8_t value[4*128]; 153 | }; 154 | 155 | /// Instrument. 156 | /// @note Only standard instruments are supported for the moment. 157 | struct Instrument { 158 | /// Standard instrument. 159 | struct Standard { 160 | /// Volume envelope. 161 | Envelope volume; 162 | /// Arpeggio (tone envelope). 163 | Envelope arpeggio; 164 | /// Arpeggio mode. 165 | /// * normal = 0 166 | /// * fixed = 1 167 | uint8_t arpeggioMode; 168 | /// Noise envelope. 169 | Envelope noise; 170 | /// Wavetable macro. 171 | Envelope wave; 172 | }; 173 | /// Name. 174 | String name; 175 | /// Mode. 176 | /// * standard = 0 177 | /// * FM = 1 178 | uint8_t mode; 179 | /// Standard instrument data. 180 | Standard std; 181 | }; 182 | 183 | /// Wavetable. 184 | typedef std::vector WaveTable; 185 | 186 | /// Pattern data. 187 | struct PatternData { 188 | /// Effect. 189 | struct Effect { 190 | /// Code. 191 | uint16_t code; 192 | /// Data. 193 | uint16_t data; 194 | }; 195 | /// Note. 196 | /// * [1,12] = {C#,D ,D#,E ,F ,F#,G ,G#,A ,A#,B ,C }. 197 | /// * 0x100 = note off. 198 | uint16_t note; 199 | /// Octave. 200 | uint16_t octave; 201 | /// Volume. 202 | uint16_t volume; 203 | /// Effects. 204 | Effect effect[DMF_MAX_EFFECT_COUNT]; 205 | /// Instrument index. 206 | uint16_t instrument; 207 | }; 208 | 209 | /// Check if a pattern element is empty. 210 | /// @param [in] src Pattern data. 211 | /// @param [in] count Effect count. 212 | /// @return true if the pattern element is empty, false otherwise. 213 | bool isEmpty(PatternData const& src, unsigned int count); 214 | 215 | /// PCM sample. 216 | struct Sample { 217 | /// Name. 218 | String name; 219 | /// Sample rate. 220 | uint8_t rate; 221 | /// Pitch. 222 | uint8_t pitch; 223 | /// Amp. 224 | uint8_t amp; 225 | /// Sample bits (8 or 16). 226 | uint8_t bits; 227 | /// Sample data. 228 | std::vector data; 229 | }; 230 | 231 | /// DMF Song. 232 | struct Song { 233 | /// Infos. 234 | Infos infos; 235 | /// Pattern matrix. 236 | std::vector patternMatrix; 237 | /// Instruments. 238 | std::vector instrument; 239 | /// Wave tables. 240 | std::vector waveTable; 241 | /// Number of effects per pattern. 242 | std::vector effectCount; 243 | /// Patterns. 244 | std::vector patternData; 245 | /// Samples. 246 | std::vector sample; 247 | }; 248 | 249 | } // DMF 250 | 251 | #endif // DMF_H 252 | -------------------------------------------------------------------------------- /doc/DMF_SPECS_0x12.txt: -------------------------------------------------------------------------------- 1 | Delek @ 2015. Demartino Leonardo. 2 | Specs for DMF (DefleMask Module Format, for DefleMask 10c and above) 3 | 4 | I wrote this text file looking at the source code, any suggestion or request can be done at: 5 | - http://www.delek.com.ar/forum 6 | - http://www.facebook.com/Delek.Page 7 | - http://www.twitter.com/_Delek 8 | - http://www.soundcloud.com/Delek_Music 9 | - deeleek (at) gmail (.) com 10 | 11 | ------------------------------------------------------------------------------------------------ 12 | 13 | "//" means a comment line, not actual information of the format. 14 | // .DMF files are compressed using zlib, after you decompress a DMF, you will start to read the next chunk of bytes. 15 | // I added tabulations for better reading, also you will find IF statements that you should follow. Systems have different things to read and save. 16 | 17 | //START OF DMF FORMAT 18 | 19 | //FORMAT FLAGS 20 | 16 Bytes: Format String, must be ".DelekDefleMask." 21 | 1 Byte: File Version, must be 18 (0x12) for DefleMask 10c (@01/21/2015) 22 | 23 | //SYSTEM SET 24 | 1 Byte: System: 25 | SYSTEM_YMU759 1 (SYSTEM_TOTAL_CHANNELS 17) 26 | SYSTEM_GENESIS 2 (SYSTEM_TOTAL_CHANNELS 10) 27 | SYSTEM_SMS 3 (SYSTEM_TOTAL_CHANNELS 4) 28 | SYSTEM_GAMEBOY 4 (SYSTEM_TOTAL_CHANNELS 4) 29 | SYSTEM_PCENGINE 5 (SYSTEM_TOTAL_CHANNELS 6) 30 | SYSTEM_NES 6 (SYSTEM_TOTAL_CHANNELS 5) 31 | SYSTEM_C64 7 (SYSTEM_TOTAL_CHANNELS 3) 32 | 33 | //VISUAL INFORMATION 34 | 1 Byte: Song Name Chars Count (0-255) 35 | N Bytes: Song Name Chars 36 | 1 Byte: Song Author Chars Count (0-255) 37 | N Bytes: Song Author Chars 38 | 1 Byte: Highlight A in patterns 39 | 1 Byte: Highlight B in patterns 40 | 41 | //MODULE INFORMATION 42 | 1 Byte: Time Base 43 | 1 Byte: Tick Time 1 44 | 1 Byte: Tick Time 2 45 | 1 Byte: Frames Mode (0 = PAL, 1 = NTSC) 46 | 1 Byte: Using Custom HZ (If set to 1, NTSC or PAL is ignored) 47 | 1 Byte: Custom HZ value 1 48 | 1 Byte: Custom HZ value 2 49 | 1 Byte: Custom HZ value 3 50 | 1 Byte: TOTAL_ROWS_PER_PATTERN 51 | 1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX 52 | 1 Byte: Arpeggio Tick Speed 53 | 54 | //PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX) 55 | Repeat this SYSTEM_TOTAL_CHANNELS times 56 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 57 | 1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop) 58 | 59 | //INSTRUMENTS DATA (.DMP format is similar to this part, but there are some discrepancies, please read DMP_Specs.txt for more details) 60 | 1 Byte: TOTAL_INSTRUMENTS 61 | Repeat this TOTAL_INSTRUMENTS times 62 | 1 Byte: Instrument Name Chars Count (0-255) 63 | N Bytes: Instrument Name Chars 64 | 1 Byte: Instrument Mode (0 = STANDARD INS, 1 = FM INS) 65 | 66 | //PER INSTRUMENT MODE DATA 67 | 68 | //IF INSTRUMENT MODE IS FM ( = 1) 69 | 1 Byte: ALG 70 | 1 Byte: *RESERVED (MUST BE 0) 71 | 1 Byte: FB 72 | 1 Byte: *RESERVED (MUST BE 0) 73 | 1 Byte: LFO (FMS on SEGA Genesis) 74 | 1 Byte: *RESERVED (MUST BE 0) 75 | 1 Byte: TOTAL_OPERATORS (0 = 2 OPERATORS, 1 = 4 OPERATORS) 76 | 1 Byte: LFO2 (AMS on SEGA Genesis) 77 | Repeat this TOTAL_OPERATORS times 78 | 1 Byte: AM 79 | 1 Byte: AR 80 | 1 Byte: DAM 81 | 1 Byte: DR 82 | 1 Byte: DVB 83 | 1 Byte: EGT 84 | 1 Byte: KSL 85 | 1 Byte: MULT 86 | 1 Byte: RR 87 | 1 Byte: SL 88 | 1 Byte: SUS 89 | 1 Byte: TL 90 | 1 Byte: VIB 91 | 1 Byte: WS 92 | 1 Byte: KSR (RS on SEGA Genesis) 93 | 1 Byte: DT 94 | 1 Byte: D2R 95 | 1 Byte: SSGMODE (BIT 4 = 0 Disabled, 1 Enabled, BITS 0,1,2 SSG_MODE) 96 | 97 | //IF INSTRUMENT MODE IS STANDARD ( = 0) 98 | //IF NOT SYSTEM_GAMEBOY (Game Boy uses STD instruments but has internal ADSR Volume, do not read VOLUME MACRO for it) 99 | //VOLUME MACRO 100 | 1 Byte: ENVELOPE_SIZE (0 - 127) 101 | Repeat this ENVELOPE_SIZE times 102 | 4 Bytes: ENVELOPE_VALUE 103 | //IF ENVELOPE_SIZE > 0 104 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 105 | 106 | //ARPEGGIO MACRO 107 | 1 Byte: ENVELOPE_SIZE (0 - 127) 108 | Repeat this ENVELOPE_SIZE times 109 | 4 Bytes: ENVELOPE_VALUE (signed int, offset=12) 110 | //IF ENVELOPE_SIZE > 0 111 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 112 | 1 Byte: ARPEGGIO MACRO MODE (0 = Normal, 1 = Fixed) 113 | 114 | //DUTY/NOISE MACRO 115 | 1 Byte: ENVELOPE_SIZE (0 - 127) 116 | Repeat this ENVELOPE_SIZE times 117 | 4 Bytes: ENVELOPE_VALUE 118 | //IF ENVELOPE_SIZE > 0 119 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 120 | 121 | //WAVETABLE MACRO 122 | 1 Byte: ENVELOPE_SIZE (0 - 127) 123 | Repeat this ENVELOPE_SIZE times 124 | 4 Bytes: ENVELOPE_VALUE 125 | //IF ENVELOPE_SIZE > 0 126 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 127 | 128 | //PER SYSTEM DATA 129 | //IF SYSTEM_C64 130 | 1 Byte: Triangle Wave Enabled 131 | 1 Byte: Saw Wave Enabled 132 | 1 Byte: Pulse Wave Enabled 133 | 1 Byte: Noise Wave Enabled 134 | 1 Byte: Attack 135 | 1 Byte: Decay 136 | 1 Byte: Sustain 137 | 1 Byte: Release 138 | 1 Byte: Pulse Width 139 | 1 Byte: Ring Modulation Enabled 140 | 1 Byte: Sync Modulation Enabled 141 | 1 Byte: To Filter 142 | 1 Byte: Volume Macro To Filter Cutoff Enabled 143 | 1 Byte: Use Filter Values From Instrument 144 | //FILTER GLOBALS 145 | 1 Byte: Filter Resonance 146 | 1 Byte: Filter Cutoff 147 | 1 Byte: Filter High Pass 148 | 1 Byte: Filter Low Pass 149 | 1 Byte: Filter CH2 Off 150 | //IF SYSTEM_GAMEBOY 151 | 1 Byte: Envelope Volume 152 | 1 Byte: Envelope Direction 153 | 1 Byte: Envelope Length 154 | 1 Byte: Sound Length 155 | 156 | //END OF INSTRUMENTS DATA 157 | //WAVETABLES DATA 158 | 1 Byte: TOTAL_WAVETABLES 159 | Repeat this TOTAL_WAVETABLES times 160 | 4 Bytes: WAVETABLE_SIZE 161 | Repeat this WAVETABLE_SIZE times 162 | 4 Bytes: Wavetable Data 163 | //PATTERNS DATA 164 | Repeat this SYSTEM_TOTAL_CHANNELS times 165 | 1 Byte: CHANNEL_EFFECTS_COLUMNS_COUNT 166 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 167 | Repeat this TOTAL_ROWS_PER_PATTERN times 168 | 2 Bytes: Note for this index 169 | 2 Bytes: Octave for this index 170 | //Note values: 171 | //01 C# 172 | //02 D- 173 | //03 D# 174 | //04 E- 175 | //05 F- 176 | //06 F# 177 | //07 G- 178 | //08 G# 179 | //09 A- 180 | //10 A# 181 | //11 B- 182 | //12 C- 183 | 184 | //Special cases: 185 | //Note = 0 and octave = 0 means empty. 186 | //Note = 100 means NOTE OFF, no matter what is inside the octave value. 187 | 188 | 2 Bytes: Volume for this index (-1 = Empty) 189 | Repeat this CHANNEL_EFFECTS_COLUMNS_COUNT times 190 | 2 Bytes: Effect Code for this index (-1 = Empty) 191 | 2 Bytes: Effect Value for this index (-1 = Empty) 192 | 2 Bytes: Instrument for this index (-1 = Empty) 193 | 194 | //PCM SAMPLES DATA 195 | 1 Byte: TOTAL_SAMPLES 196 | Repeat this TOTAL_SAMPLES times 197 | 4 Bytes: SAMPLE_SIZE 198 | 1 Byte: Sample Rate 199 | 1 Byte: Sample Pitch 200 | 1 Byte: Sample Amp 201 | Repeat this SAMPLE_SIZE times 202 | 2 Bytes: Actual Sample Data 203 | 204 | //END OF DMF FORMAT -------------------------------------------------------------------------------- /doc/DMF_SPECS_0x13.txt: -------------------------------------------------------------------------------- 1 | Delek @ 2015. Leonardo Demartino. 2 | Specs for DMF (DefleMask Module Format, for DefleMask 11 and above) 3 | 4 | I wrote this text file looking at the source code, any suggestion or request can be done at: 5 | - http://www.delek.com.ar/forum 6 | - http://www.facebook.com/Delek.Page 7 | - http://www.twitter.com/_Delek 8 | - http://www.soundcloud.com/Delek_Music 9 | - deeleek (at) gmail (.) com 10 | 11 | ------------------------------------------------------------------------------------------------ 12 | 13 | "//" means a comment line, not actual information of the format. 14 | // .DMF files are compressed using zlib, after you decompress a DMF, you will start to read the next chunk of bytes. 15 | // I added tabulations for better reading, also you will find IF statements that you should follow. Systems have different things to read and save. 16 | 17 | //START OF DMF FORMAT 18 | 19 | //FORMAT FLAGS 20 | 16 Bytes: Format String, must be ".DelekDefleMask." 21 | 1 Byte: File Version, must be 19 (0x13) for DefleMask 11 22 | 23 | //SYSTEM SET 24 | 1 Byte: System: 25 | SYSTEM_GENESIS 2 (SYSTEM_TOTAL_CHANNELS 10) 26 | SYSTEM_SMS 3 (SYSTEM_TOTAL_CHANNELS 4) 27 | SYSTEM_GAMEBOY 4 (SYSTEM_TOTAL_CHANNELS 4) 28 | SYSTEM_PCENGINE 5 (SYSTEM_TOTAL_CHANNELS 6) 29 | SYSTEM_NES 6 (SYSTEM_TOTAL_CHANNELS 5) 30 | SYSTEM_C64 7 (SYSTEM_TOTAL_CHANNELS 3) 31 | SYSTEM_YM2151 8 (SYSTEM_TOTAL_CHANNELS 13) 32 | 33 | //VISUAL INFORMATION 34 | 1 Byte: Song Name Chars Count (0-255) 35 | N Bytes: Song Name Chars 36 | 1 Byte: Song Author Chars Count (0-255) 37 | N Bytes: Song Author Chars 38 | 1 Byte: Highlight A in patterns 39 | 1 Byte: Highlight B in patterns 40 | 41 | //MODULE INFORMATION 42 | 1 Byte: Time Base 43 | 1 Byte: Tick Time 1 44 | 1 Byte: Tick Time 2 45 | 1 Byte: Frames Mode (0 = PAL, 1 = NTSC) 46 | 1 Byte: Using Custom HZ (If set to 1, NTSC or PAL is ignored) 47 | 1 Byte: Custom HZ value 1 48 | 1 Byte: Custom HZ value 2 49 | 1 Byte: Custom HZ value 3 50 | 1 Byte: TOTAL_ROWS_PER_PATTERN 51 | 1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX 52 | 1 Byte: Arpeggio Tick Speed 53 | 54 | //PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX) 55 | Repeat this SYSTEM_TOTAL_CHANNELS times 56 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 57 | 1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop) 58 | 59 | //INSTRUMENTS DATA (.DMP format is similar to this part, but there are some discrepancies, please read DMP_Specs.txt for more details) 60 | 1 Byte: TOTAL_INSTRUMENTS 61 | Repeat this TOTAL_INSTRUMENTS times 62 | 1 Byte: Instrument Name Chars Count (0-255) 63 | N Bytes: Instrument Name Chars 64 | 1 Byte: Instrument Mode (0 = STANDARD INS, 1 = FM INS) 65 | 66 | //PER INSTRUMENT MODE DATA 67 | 68 | //IF INSTRUMENT MODE IS FM ( = 1) 69 | 1 Byte: ALG 70 | 1 Byte: FB 71 | 1 Byte: LFO (FMS on YM2612, PMS on YM2151) 72 | 1 Byte: LFO2 (AMS on YM2612, AMS on YM2151) 73 | Repeat this TOTAL_OPERATORS times 74 | 1 Byte: AM 75 | 1 Byte: AR 76 | 1 Byte: DR 77 | 1 Byte: MULT 78 | 1 Byte: RR 79 | 1 Byte: SL 80 | 1 Byte: TL 81 | 1 Byte: DT2 82 | 1 Byte: RS 83 | 1 Byte: DT 84 | 1 Byte: D2R 85 | 1 Byte: SSGMODE (BIT 4 = 0 Disabled, 1 Enabled, BITS 0,1,2 SSG_MODE) 86 | 87 | //IF INSTRUMENT MODE IS STANDARD ( = 0) 88 | //IF NOT SYSTEM_GAMEBOY (Game Boy uses STD instruments but has internal ADSR Volume, do not read VOLUME MACRO for it) 89 | //VOLUME MACRO 90 | 1 Byte: ENVELOPE_SIZE (0 - 127) 91 | Repeat this ENVELOPE_SIZE times 92 | 4 Bytes: ENVELOPE_VALUE 93 | //IF ENVELOPE_SIZE > 0 94 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 95 | 96 | //ARPEGGIO MACRO 97 | 1 Byte: ENVELOPE_SIZE (0 - 127) 98 | Repeat this ENVELOPE_SIZE times 99 | 4 Bytes: ENVELOPE_VALUE (signed int, offset=12) 100 | //IF ENVELOPE_SIZE > 0 101 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 102 | 1 Byte: ARPEGGIO MACRO MODE (0 = Normal, 1 = Fixed) 103 | 104 | //DUTY/NOISE MACRO 105 | 1 Byte: ENVELOPE_SIZE (0 - 127) 106 | Repeat this ENVELOPE_SIZE times 107 | 4 Bytes: ENVELOPE_VALUE 108 | //IF ENVELOPE_SIZE > 0 109 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 110 | 111 | //WAVETABLE MACRO 112 | 1 Byte: ENVELOPE_SIZE (0 - 127) 113 | Repeat this ENVELOPE_SIZE times 114 | 4 Bytes: ENVELOPE_VALUE 115 | //IF ENVELOPE_SIZE > 0 116 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 117 | 118 | //PER SYSTEM DATA 119 | //IF SYSTEM_C64 120 | 1 Byte: Triangle Wave Enabled 121 | 1 Byte: Saw Wave Enabled 122 | 1 Byte: Pulse Wave Enabled 123 | 1 Byte: Noise Wave Enabled 124 | 1 Byte: Attack 125 | 1 Byte: Decay 126 | 1 Byte: Sustain 127 | 1 Byte: Release 128 | 1 Byte: Pulse Width 129 | 1 Byte: Ring Modulation Enabled 130 | 1 Byte: Sync Modulation Enabled 131 | 1 Byte: To Filter 132 | 1 Byte: Volume Macro To Filter Cutoff Enabled 133 | 1 Byte: Use Filter Values From Instrument 134 | //FILTER GLOBALS 135 | 1 Byte: Filter Resonance 136 | 1 Byte: Filter Cutoff 137 | 1 Byte: Filter High Pass 138 | 1 Byte: Filter Low Pass 139 | 1 Byte: Filter CH2 Off 140 | //IF SYSTEM_GAMEBOY 141 | 1 Byte: Envelope Volume 142 | 1 Byte: Envelope Direction 143 | 1 Byte: Envelope Length 144 | 1 Byte: Sound Length 145 | 146 | //END OF INSTRUMENTS DATA 147 | //WAVETABLES DATA 148 | 1 Byte: TOTAL_WAVETABLES 149 | Repeat this TOTAL_WAVETABLES times 150 | 4 Bytes: WAVETABLE_SIZE 151 | Repeat this WAVETABLE_SIZE times 152 | 4 Bytes: Wavetable Data 153 | //PATTERNS DATA 154 | Repeat this SYSTEM_TOTAL_CHANNELS times 155 | 1 Byte: CHANNEL_EFFECTS_COLUMNS_COUNT 156 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 157 | Repeat this TOTAL_ROWS_PER_PATTERN times 158 | 2 Bytes: Note for this index 159 | 2 Bytes: Octave for this index 160 | //Note values: 161 | //01 C# 162 | //02 D- 163 | //03 D# 164 | //04 E- 165 | //05 F- 166 | //06 F# 167 | //07 G- 168 | //08 G# 169 | //09 A- 170 | //10 A# 171 | //11 B- 172 | //12 C- 173 | 174 | //Special cases: 175 | //Note = 0 and octave = 0 means empty. 176 | //Note = 100 means NOTE OFF, no matter what is inside the octave value. 177 | 178 | 2 Bytes: Volume for this index (-1 = Empty) 179 | Repeat this CHANNEL_EFFECTS_COLUMNS_COUNT times 180 | 2 Bytes: Effect Code for this index (-1 = Empty) 181 | 2 Bytes: Effect Value for this index (-1 = Empty) 182 | 2 Bytes: Instrument for this index (-1 = Empty) 183 | 184 | //PCM SAMPLES DATA 185 | 1 Byte: TOTAL_SAMPLES 186 | Repeat this TOTAL_SAMPLES times 187 | 4 Bytes: SAMPLE_SIZE 188 | 1 Byte: Sample Rate 189 | 1 Byte: Sample Pitch 190 | 1 Byte: Sample Amp 191 | Repeat this SAMPLE_SIZE times 192 | 2 Bytes: Actual Sample Data 193 | 194 | //END OF DMF FORMAT -------------------------------------------------------------------------------- /doc/DMF_SPECS_0x15.txt: -------------------------------------------------------------------------------- 1 | Delek @ 2016. Leonardo Demartino. 2 | Specs for DMF (DefleMask Module Format, for DefleMask 11.1 and above) 3 | 4 | I wrote this text file looking at the source code, any suggestion or request can be done at: 5 | - http://www.delek.com.ar/forum 6 | - http://www.facebook.com/Delek.Page 7 | - http://www.twitter.com/_Delek 8 | - http://www.soundcloud.com/Delek_Music 9 | - deeleek (at) gmail (.) com 10 | 11 | ------------------------------------------------------------------------------------------------ 12 | 13 | "//" means a comment line, not actual information of the format. 14 | // .DMF files are compressed using zlib, after you decompress a DMF, you will start to read the next chunk of bytes. 15 | // I added tabulations for better reading, also you will find IF statements that you should follow. Systems have different things to read and save. 16 | 17 | //START OF DMF FORMAT 18 | 19 | //FORMAT FLAGS 20 | 16 Bytes: Format String, must be ".DelekDefleMask." 21 | 1 Byte: File Version, must be 21 (0x15) for DefleMask 11.1 22 | 23 | //SYSTEM SET 24 | 1 Byte: System: 25 | Less significant Nibble: 26 | SYSTEM_GENESIS 0x02 (SYSTEM_TOTAL_CHANNELS 10) 27 | SYSTEM_GENESIS (EXT. CH3) 0x12(SYSTEM_TOTAL_CHANNELS 13) 28 | SYSTEM_SMS 0x03 (SYSTEM_TOTAL_CHANNELS 4) 29 | SYSTEM_GAMEBOY 0x04 (SYSTEM_TOTAL_CHANNELS 4) 30 | SYSTEM_PCENGINE 0x05 (SYSTEM_TOTAL_CHANNELS 6) 31 | SYSTEM_NES 0x06 (SYSTEM_TOTAL_CHANNELS 5) 32 | SYSTEM_C64 (SID 8580) 0x07 (SYSTEM_TOTAL_CHANNELS 3) 33 | SYSTEM_C64 (SID 6581) 0x17 (SYSTEM_TOTAL_CHANNELS 3) 34 | SYSTEM_YM2151 0x08 (SYSTEM_TOTAL_CHANNELS 13) 35 | 36 | //VISUAL INFORMATION 37 | 1 Byte: Song Name Chars Count (0-255) 38 | N Bytes: Song Name Chars 39 | 1 Byte: Song Author Chars Count (0-255) 40 | N Bytes: Song Author Chars 41 | 1 Byte: Highlight A in patterns 42 | 1 Byte: Highlight B in patterns 43 | 44 | //MODULE INFORMATION 45 | 1 Byte: Time Base 46 | 1 Byte: Tick Time 1 47 | 1 Byte: Tick Time 2 48 | 1 Byte: Frames Mode (0 = PAL, 1 = NTSC) 49 | 1 Byte: Using Custom HZ (If set to 1, NTSC or PAL is ignored) 50 | 1 Byte: Custom HZ value 1 51 | 1 Byte: Custom HZ value 2 52 | 1 Byte: Custom HZ value 3 53 | 1 Byte: TOTAL_ROWS_PER_PATTERN 54 | 1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX 55 | 56 | (Arpeggio Tick speed was here in previous version (1 Byte), it was REMOVED since ver 11.1) 57 | 58 | //PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX) 59 | Repeat this SYSTEM_TOTAL_CHANNELS times 60 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 61 | 1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop) 62 | 63 | //INSTRUMENTS DATA (.DMP format is similar to this part, but there are some discrepancies, please read DMP_Specs.txt for more details) 64 | 1 Byte: TOTAL_INSTRUMENTS 65 | Repeat this TOTAL_INSTRUMENTS times 66 | 1 Byte: Instrument Name Chars Count (0-255) 67 | N Bytes: Instrument Name Chars 68 | 1 Byte: Instrument Mode (0 = STANDARD INS, 1 = FM INS) 69 | 70 | //PER INSTRUMENT MODE DATA 71 | 72 | //IF INSTRUMENT MODE IS FM ( = 1) 73 | 1 Byte: ALG 74 | 1 Byte: FB 75 | 1 Byte: LFO (FMS on YM2612, PMS on YM2151) 76 | 1 Byte: LFO2 (AMS on YM2612, AMS on YM2151) 77 | Repeat this TOTAL_OPERATORS times 78 | 1 Byte: AM 79 | 1 Byte: AR 80 | 1 Byte: DR 81 | 1 Byte: MULT 82 | 1 Byte: RR 83 | 1 Byte: SL 84 | 1 Byte: TL 85 | 1 Byte: DT2 86 | 1 Byte: RS 87 | 1 Byte: DT 88 | 1 Byte: D2R 89 | 1 Byte: SSGMODE (BIT 4 = 0 Disabled, 1 Enabled, BITS 0,1,2 SSG_MODE) 90 | 91 | //IF INSTRUMENT MODE IS STANDARD ( = 0) 92 | //IF NOT SYSTEM_GAMEBOY (Game Boy uses STD instruments but has internal ADSR Volume, do not read VOLUME MACRO for it) 93 | //VOLUME MACRO 94 | 1 Byte: ENVELOPE_SIZE (0 - 127) 95 | Repeat this ENVELOPE_SIZE times 96 | 4 Bytes: ENVELOPE_VALUE 97 | //IF ENVELOPE_SIZE > 0 98 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 99 | 100 | //ARPEGGIO MACRO 101 | 1 Byte: ENVELOPE_SIZE (0 - 127) 102 | Repeat this ENVELOPE_SIZE times 103 | 4 Bytes: ENVELOPE_VALUE (signed int, offset=12) 104 | //IF ENVELOPE_SIZE > 0 105 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 106 | 1 Byte: ARPEGGIO MACRO MODE (0 = Normal, 1 = Fixed) 107 | 108 | //DUTY/NOISE MACRO 109 | 1 Byte: ENVELOPE_SIZE (0 - 127) 110 | Repeat this ENVELOPE_SIZE times 111 | 4 Bytes: ENVELOPE_VALUE 112 | //IF ENVELOPE_SIZE > 0 113 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 114 | 115 | //WAVETABLE MACRO 116 | 1 Byte: ENVELOPE_SIZE (0 - 127) 117 | Repeat this ENVELOPE_SIZE times 118 | 4 Bytes: ENVELOPE_VALUE 119 | //IF ENVELOPE_SIZE > 0 120 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 121 | 122 | //PER SYSTEM DATA 123 | //IF SYSTEM_C64 124 | 1 Byte: Triangle Wave Enabled 125 | 1 Byte: Saw Wave Enabled 126 | 1 Byte: Pulse Wave Enabled 127 | 1 Byte: Noise Wave Enabled 128 | 1 Byte: Attack 129 | 1 Byte: Decay 130 | 1 Byte: Sustain 131 | 1 Byte: Release 132 | 1 Byte: Pulse Width 133 | 1 Byte: Ring Modulation Enabled 134 | 1 Byte: Sync Modulation Enabled 135 | 1 Byte: To Filter 136 | 1 Byte: Volume Macro To Filter Cutoff Enabled 137 | 1 Byte: Use Filter Values From Instrument 138 | //FILTER GLOBALS 139 | 1 Byte: Filter Resonance 140 | 1 Byte: Filter Cutoff 141 | 1 Byte: Filter High Pass 142 | 1 Byte: Filter Low Pass 143 | 1 Byte: Filter CH2 Off 144 | //IF SYSTEM_GAMEBOY 145 | 1 Byte: Envelope Volume 146 | 1 Byte: Envelope Direction 147 | 1 Byte: Envelope Length 148 | 1 Byte: Sound Length 149 | 150 | //END OF INSTRUMENTS DATA 151 | //WAVETABLES DATA 152 | 1 Byte: TOTAL_WAVETABLES 153 | Repeat this TOTAL_WAVETABLES times 154 | 4 Bytes: WAVETABLE_SIZE 155 | Repeat this WAVETABLE_SIZE times 156 | 4 Bytes: Wavetable Data 157 | //PATTERNS DATA 158 | Repeat this SYSTEM_TOTAL_CHANNELS times 159 | 1 Byte: CHANNEL_EFFECTS_COLUMNS_COUNT 160 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 161 | Repeat this TOTAL_ROWS_PER_PATTERN times 162 | 2 Bytes: Note for this index 163 | 2 Bytes: Octave for this index 164 | //Note values: 165 | //01 C# 166 | //02 D- 167 | //03 D# 168 | //04 E- 169 | //05 F- 170 | //06 F# 171 | //07 G- 172 | //08 G# 173 | //09 A- 174 | //10 A# 175 | //11 B- 176 | //12 C- 177 | 178 | //Special cases: 179 | //Note = 0 and octave = 0 means empty. 180 | //Note = 100 means NOTE OFF, no matter what is inside the octave value. 181 | 182 | 2 Bytes: Volume for this index (-1 = Empty) 183 | Repeat this CHANNEL_EFFECTS_COLUMNS_COUNT times 184 | 2 Bytes: Effect Code for this index (-1 = Empty) 185 | 2 Bytes: Effect Value for this index (-1 = Empty) 186 | 2 Bytes: Instrument for this index (-1 = Empty) 187 | 188 | //PCM SAMPLES DATA 189 | 1 Byte: TOTAL_SAMPLES 190 | Repeat this TOTAL_SAMPLES times 191 | 4 Bytes: SAMPLE_SIZE 192 | 1 Byte: Sample Rate 193 | 1 Byte: Sample Pitch 194 | 1 Byte: Sample Amp 195 | Repeat this SAMPLE_SIZE times 196 | 2 Bytes: Actual Sample Data 197 | 198 | //END OF DMF FORMAT -------------------------------------------------------------------------------- /doc/DMF_SPECS_0x17.txt: -------------------------------------------------------------------------------- 1 | Delek @ 2014. Demartino Leonardo. 2 | 3 | Specs for DMF (DefleMask Module Format, for DefleMask 9 and above) 4 | 5 | I wrote this text file looking at the source code, there could be some mistakes. 6 | If you think so, please contact me at: 7 | - http://www.delek.com.ar/forum 8 | - http://www.facebook.com/Delek.Page 9 | - http://www.twitter.com/_Delek 10 | - http://www.soundcloud.com/Delek_Music 11 | - deeleek@gmail.com 12 | 13 | ------------------------------------------------------------------------------------------------ 14 | 15 | "//" means a comment line, not actual information of the format. 16 | // .DMF files are compressed using zlib, after you decompress a DMF, you will start to read this. 17 | 18 | 19 | //START OF DMF FORMAT 20 | 21 | //FORMAT FLAGS 22 | 16 Bytes: Format String, must be ".DelekDefleMask." 23 | 1 Byte: File Version, must be 17 for DefleMask 9c (@06/09/13) 24 | 25 | //SYSTEM SET 26 | 1 Byte: System: 27 | SYSTEM_YMU759 1 (SYSTEM_TOTAL_CHANNELS 17) 28 | SYSTEM_GENESIS 2 (SYSTEM_TOTAL_CHANNELS 10) 29 | SYSTEM_SMS 3 (SYSTEM_TOTAL_CHANNELS 4) 30 | SYSTEM_GAMEBOY 4 (SYSTEM_TOTAL_CHANNELS 4) 31 | SYSTEM_PCENGINE 5 (SYSTEM_TOTAL_CHANNELS 6) 32 | SYSTEM_NES 6 (SYSTEM_TOTAL_CHANNELS 5) 33 | SYSTEM_C64 7 (SYSTEM_TOTAL_CHANNELS 3) 34 | 35 | //VISUAL INFORMATION 36 | 1 Byte: Song Name Chars Count (0-255) 37 | N Bytes: Song Name Chars 38 | 1 Byte: Song Author Chars Count (0-255) 39 | N Bytes: Song Author Chars 40 | 1 Byte: Highlight A in patterns 41 | 1 Byte: Highlight B in patterns 42 | 43 | //MODULE INFORMATION 44 | 1 Byte: Time Base 45 | 1 Byte: Tick Time 1 46 | 1 Byte: Tick Time 2 47 | 1 Byte: Frames Mode (0 = PAL, 1 = NTSC) 48 | 1 Byte: Using Custom HZ (If set to 1, NTSC or PAL is ignored) 49 | 1 Byte: Custom HZ value 1 50 | 1 Byte: Custom HZ value 2 51 | 1 Byte: Custom HZ value 3 52 | 1 Byte: TOTAL_ROWS_PER_PATTERN 53 | 1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX 54 | 1 Byte: Arpeggio Tick Speed 55 | 56 | //PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX) 57 | Repeat this SYSTEM_TOTAL_CHANNELS times 58 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 59 | 1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop) 60 | 61 | //INSTRUMENTS DATA (.DMP format is similar to this part, but there are some discrepancies, please read DMP_Specs.txt for more details) 62 | 1 Byte: TOTAL_INSTRUMENTS 63 | Repeat this TOTAL_INSTRUMENTS times 64 | 1 Byte: Instrument Name Chars Count (0-255) 65 | N Bytes: Instrument Name Chars 66 | 1 Byte: Instrument Mode (0 = STANDARD INS, 1 = FM INS) 67 | 68 | //PER INSTRUMENT MODE DATA 69 | 70 | //IF INSTRUMENT MODE IS FM ( = 1) 71 | 1 Byte: ALG 72 | 1 Byte: *RESERVED (MUST BE 0) 73 | 1 Byte: FB 74 | 1 Byte: *RESERVED (MUST BE 0) 75 | 1 Byte: LFO (FMS on SEGA Genesis) 76 | 1 Byte: *RESERVED (MUST BE 0) 77 | 1 Byte: TOTAL_OPERATORS (0 = 2 OPERATORS, 1 = 4 OPERATORS) 78 | 1 Byte: LFO2 (AMS on SEGA Genesis) 79 | Repeat this TOTAL_OPERATORS times 80 | 1 Byte: AM 81 | 1 Byte: AR 82 | 1 Byte: DAM 83 | 1 Byte: DR 84 | 1 Byte: DVB 85 | 1 Byte: EGT 86 | 1 Byte: KSL 87 | 1 Byte: MULT 88 | 1 Byte: RR 89 | 1 Byte: SL 90 | 1 Byte: SUS 91 | 1 Byte: TL 92 | 1 Byte: VIB 93 | 1 Byte: WS 94 | 1 Byte: KSR (RS on SEGA Genesis) 95 | 1 Byte: DT 96 | 1 Byte: D2R 97 | 1 Byte: SSGMODE (BIT 4 = 0 Disabled, 1 Enabled, BITS 0,1,2 SSG_MODE) 98 | 99 | //IF INSTRUMENT MODE IS STANDARD ( = 0) 100 | //VOLUME MACRO 101 | 1 Byte: ENVELOPE_SIZE (0 - 127) 102 | Repeat this ENVELOPE_SIZE times 103 | 4 Bytes: ENVELOPE_VALUE 104 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 105 | 106 | //ARPEGGIO MACRO 107 | 1 Byte: ENVELOPE_SIZE (0 - 127) 108 | Repeat this ENVELOPE_SIZE times 109 | 4 Bytes: ENVELOPE_VALUE (signed int, offset=12) 110 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 111 | 1 Byte: ARPEGGIO MACRO MODE (0 = Normal, 1 = Fixed) 112 | 113 | //DUTY/NOISE MACRO 114 | 1 Byte: ENVELOPE_SIZE (0 - 127) 115 | Repeat this ENVELOPE_SIZE times 116 | 4 Bytes: ENVELOPE_VALUE 117 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 118 | 119 | //WAVETABLE MACRO 120 | 1 Byte: ENVELOPE_SIZE (0 - 127) 121 | Repeat this ENVELOPE_SIZE times 122 | 4 Bytes: ENVELOPE_VALUE 123 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 124 | 125 | //PER SYSTEM DATA 126 | //IF SYSTEM IS C64 127 | 1 Byte: Triangle Wave Enabled 128 | 1 Byte: Saw Wave Enabled 129 | 1 Byte: Pulse Wave Enabled 130 | 1 Byte: Noise Wave Enabled 131 | 1 Byte: Attack 132 | 1 Byte: Decay 133 | 1 Byte: Sustain 134 | 1 Byte: Release 135 | 1 Byte: Pulse Width 136 | 1 Byte: Ring Modulation Enabled 137 | 1 Byte: Sync Modulation Enabled 138 | 1 Byte: To Filter 139 | 1 Byte: Volume Macro To Filter Cutoff Enabled 140 | 1 Byte: Use Filter Values From Instrument 141 | //FILTER GLOBALS 142 | 1 Byte: Filter Resonance 143 | 1 Byte: Filter Cutoff 144 | 1 Byte: Filter High Pass 145 | 1 Byte: Filter Low Pass 146 | 1 Byte: Filter CH2 Off 147 | 148 | //END OF INSTRUMENTS DATA 149 | //WAVETABLES DATA 150 | 1 Byte: TOTAL_WAVETABLES 151 | Repeat this TOTAL_WAVETABLES times 152 | 4 Bytes: WAVETABLE_SIZE 153 | Repeat this WAVETABLE_SIZE times 154 | 4 Bytes: Wavetable Data 155 | //PATTERNS DATA 156 | Repeat this SYSTEM_TOTAL_CHANNELS times 157 | 1 Byte: CHANNEL_EFFECTS_COLUMNS_COUNT 158 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 159 | Repeat this TOTAL_ROWS_PER_PATTERN times 160 | 2 Bytes: Note for this index 161 | 2 Bytes: Octave for this index 162 | //Note values: 163 | //01 C# 164 | //02 D- 165 | //03 D# 166 | //04 E- 167 | //05 F- 168 | //06 F# 169 | //07 G- 170 | //08 G# 171 | //09 A- 172 | //10 A# 173 | //11 B- 174 | //12 C- 175 | 176 | //Special cases: 177 | //Note = 0 and octave = 0 means empty. 178 | //Note = 100 means NOTE OFF, no matter what is inside the octave value. 179 | 180 | 2 Bytes: Volume for this index (-1 = Empty) 181 | Repeat this CHANNEL_EFFECTS_COLUMNS_COUNT times 182 | 2 Bytes: Effect Code for this index (-1 = Empty) 183 | 2 Bytes: Effect Value for this index (-1 = Empty) 184 | 2 Bytes: Instrument for this index (-1 = Empty) 185 | 186 | //PCM SAMPLES DATA 187 | 1 Byte: TOTAL_SAMPLES 188 | Repeat this TOTAL_SAMPLES times 189 | 4 Bytes: SAMPLE_SIZE 190 | 1 Byte: Sample Rate 191 | 1 Byte: Sample Pitch 192 | 1 Byte: Sample Amp 193 | Repeat this SAMPLE_SIZE times 194 | 2 Bytes: Actual Sample Data 195 | 196 | //END OF DMF FORMAT -------------------------------------------------------------------------------- /doc/DMF_SPECS_0x18.txt: -------------------------------------------------------------------------------- 1 | Delek @ 2016. Leonardo Demartino. 2 | Specs for DMF (DefleMask Module Format, for DefleMask v0.12.0 and above) 3 | 4 | I wrote this text file looking at the source code, any suggestion or request can be done at: 5 | - http://www.delek.net 6 | - http://www.deflemask.com/forum 7 | - http://www.facebook.com/Delek.Page 8 | - http://www.twitter.com/_Delek 9 | - http://www.soundcloud.com/Delek_Music 10 | - deeleek (at) gmail (.) com 11 | 12 | ------------------------------------------------------------------------------------------------ 13 | 14 | "//" means a comment line, not actual information of the format. 15 | // .DMF files are compressed using zlib, after you decompress a DMF, you will start to read the next chunk of bytes. 16 | // I added tabulations for better reading, also you will find IF statements that you should follow. Systems have different things to read and save. 17 | 18 | //START OF DMF FORMAT 19 | 20 | //FORMAT FLAGS 21 | 16 Bytes: Format String, must be ".DelekDefleMask." 22 | 1 Byte: File Version, must be 24 (0x18) for DefleMask v0.12.0 23 | 24 | //SYSTEM SET 25 | 1 Byte: System: 26 | SYSTEM_GENESIS 0x02 (SYSTEM_TOTAL_CHANNELS 10) 27 | SYSTEM_GENESIS (EXT. CH3) 0x12(SYSTEM_TOTAL_CHANNELS 13) 28 | SYSTEM_SMS 0x03 (SYSTEM_TOTAL_CHANNELS 4) 29 | SYSTEM_GAMEBOY 0x04 (SYSTEM_TOTAL_CHANNELS 4) 30 | SYSTEM_PCENGINE 0x05 (SYSTEM_TOTAL_CHANNELS 6) 31 | SYSTEM_NES 0x06 (SYSTEM_TOTAL_CHANNELS 5) 32 | SYSTEM_C64 (SID 8580) 0x07 (SYSTEM_TOTAL_CHANNELS 3) 33 | SYSTEM_C64 (SID 6581) 0x17 (SYSTEM_TOTAL_CHANNELS 3) 34 | SYSTEM_YM2151 0x08 (SYSTEM_TOTAL_CHANNELS 13) 35 | 36 | //VISUAL INFORMATION 37 | 1 Byte: Song Name Chars Count (0-255) 38 | N Bytes: Song Name Chars 39 | 1 Byte: Song Author Chars Count (0-255) 40 | N Bytes: Song Author Chars 41 | 1 Byte: Highlight A in patterns 42 | 1 Byte: Highlight B in patterns 43 | 44 | //MODULE INFORMATION 45 | 1 Byte: Time Base 46 | 1 Byte: Tick Time 1 47 | 1 Byte: Tick Time 2 48 | 1 Byte: Frames Mode (0 = PAL, 1 = NTSC) 49 | 1 Byte: Using Custom HZ (If set to 1, NTSC or PAL is ignored) 50 | 1 Byte: Custom HZ value 1 51 | 1 Byte: Custom HZ value 2 52 | 1 Byte: Custom HZ value 3 53 | 4 Bytes: TOTAL_ROWS_PER_PATTERN 54 | 1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX 55 | 56 | (Arpeggio Tick speed was here in previous version (1 Byte), it was REMOVED since ver 11.1) 57 | 58 | //PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX) 59 | Repeat this SYSTEM_TOTAL_CHANNELS times 60 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 61 | 1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop) 62 | 63 | //INSTRUMENTS DATA (.DMP format is similar to this part, but there are some discrepancies, please read DMP_Specs.txt for more details) 64 | 1 Byte: TOTAL_INSTRUMENTS 65 | Repeat this TOTAL_INSTRUMENTS times 66 | 1 Byte: Instrument Name Chars Count (0-255) 67 | N Bytes: Instrument Name Chars 68 | 1 Byte: Instrument Mode (0 = STANDARD INS, 1 = FM INS) 69 | 70 | //PER INSTRUMENT MODE DATA 71 | 72 | //IF INSTRUMENT MODE IS FM ( = 1) 73 | 1 Byte: ALG 74 | 1 Byte: FB 75 | 1 Byte: LFO (FMS on YM2612, PMS on YM2151) 76 | 1 Byte: LFO2 (AMS on YM2612, AMS on YM2151) 77 | Repeat this TOTAL_OPERATORS times 78 | 1 Byte: AM 79 | 1 Byte: AR 80 | 1 Byte: DR 81 | 1 Byte: MULT 82 | 1 Byte: RR 83 | 1 Byte: SL 84 | 1 Byte: TL 85 | 1 Byte: DT2 86 | 1 Byte: RS 87 | 1 Byte: DT 88 | 1 Byte: D2R 89 | 1 Byte: SSGMODE (BIT 4 = 0 Disabled, 1 Enabled, BITS 0,1,2 SSG_MODE) 90 | 91 | //IF INSTRUMENT MODE IS STANDARD ( = 0) 92 | //IF NOT SYSTEM_GAMEBOY (Game Boy uses STD instruments but has internal ADSR Volume, do not read VOLUME MACRO for it) 93 | //VOLUME MACRO 94 | 1 Byte: ENVELOPE_SIZE (0 - 127) 95 | Repeat this ENVELOPE_SIZE times 96 | 4 Bytes: ENVELOPE_VALUE 97 | //IF ENVELOPE_SIZE > 0 98 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 99 | 100 | //ARPEGGIO MACRO 101 | 1 Byte: ENVELOPE_SIZE (0 - 127) 102 | Repeat this ENVELOPE_SIZE times 103 | 4 Bytes: ENVELOPE_VALUE (signed int, offset=12) 104 | //IF ENVELOPE_SIZE > 0 105 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 106 | 1 Byte: ARPEGGIO MACRO MODE (0 = Normal, 1 = Fixed) 107 | 108 | //DUTY/NOISE MACRO 109 | 1 Byte: ENVELOPE_SIZE (0 - 127) 110 | Repeat this ENVELOPE_SIZE times 111 | 4 Bytes: ENVELOPE_VALUE 112 | //IF ENVELOPE_SIZE > 0 113 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 114 | 115 | //WAVETABLE MACRO 116 | 1 Byte: ENVELOPE_SIZE (0 - 127) 117 | Repeat this ENVELOPE_SIZE times 118 | 4 Bytes: ENVELOPE_VALUE 119 | //IF ENVELOPE_SIZE > 0 120 | 1 Byte: LOOP_POSITION (-1 = NO LOOP) 121 | 122 | //PER SYSTEM DATA 123 | //IF SYSTEM_C64 124 | 1 Byte: Triangle Wave Enabled 125 | 1 Byte: Saw Wave Enabled 126 | 1 Byte: Pulse Wave Enabled 127 | 1 Byte: Noise Wave Enabled 128 | 1 Byte: Attack 129 | 1 Byte: Decay 130 | 1 Byte: Sustain 131 | 1 Byte: Release 132 | 1 Byte: Pulse Width 133 | 1 Byte: Ring Modulation Enabled 134 | 1 Byte: Sync Modulation Enabled 135 | 1 Byte: To Filter 136 | 1 Byte: Volume Macro To Filter Cutoff Enabled 137 | 1 Byte: Use Filter Values From Instrument 138 | //FILTER GLOBALS 139 | 1 Byte: Filter Resonance 140 | 1 Byte: Filter Cutoff 141 | 1 Byte: Filter High Pass 142 | 1 Byte: Filter Low Pass 143 | 1 Byte: Filter CH2 Off 144 | //IF SYSTEM_GAMEBOY 145 | 1 Byte: Envelope Volume 146 | 1 Byte: Envelope Direction 147 | 1 Byte: Envelope Length 148 | 1 Byte: Sound Length 149 | 150 | //END OF INSTRUMENTS DATA 151 | //WAVETABLES DATA 152 | 1 Byte: TOTAL_WAVETABLES 153 | Repeat this TOTAL_WAVETABLES times 154 | 4 Bytes: WAVETABLE_SIZE 155 | Repeat this WAVETABLE_SIZE times 156 | 4 Bytes: Wavetable Data 157 | //PATTERNS DATA 158 | Repeat this SYSTEM_TOTAL_CHANNELS times 159 | 1 Byte: CHANNEL_EFFECTS_COLUMNS_COUNT 160 | Repeat this TOTAL_ROWS_IN_PATTERN_MATRIX times 161 | Repeat this TOTAL_ROWS_PER_PATTERN times 162 | 2 Bytes: Note for this index 163 | 2 Bytes: Octave for this index 164 | //Note values: 165 | //01 C# 166 | //02 D- 167 | //03 D# 168 | //04 E- 169 | //05 F- 170 | //06 F# 171 | //07 G- 172 | //08 G# 173 | //09 A- 174 | //10 A# 175 | //11 B- 176 | //12 C- 177 | 178 | //Special cases: 179 | //Note = 0 and octave = 0 means empty. 180 | //Note = 100 means NOTE OFF, no matter what is inside the octave value. 181 | 182 | 2 Bytes: Volume for this index (-1 = Empty) 183 | Repeat this CHANNEL_EFFECTS_COLUMNS_COUNT times 184 | 2 Bytes: Effect Code for this index (-1 = Empty) 185 | 2 Bytes: Effect Value for this index (-1 = Empty) 186 | 2 Bytes: Instrument for this index (-1 = Empty) 187 | 188 | //PCM SAMPLES DATA 189 | 1 Byte: TOTAL_SAMPLES 190 | Repeat this TOTAL_SAMPLES times 191 | 4 Bytes: SAMPLE_SIZE 192 | 1 Byte: Sample Name Chars Count (0-255) 193 | N Bytes: Sample Name Chars 194 | 1 Byte: Sample Rate 195 | 1 Byte: Sample Pitch 196 | 1 Byte: Sample Amp 197 | 1 Byte: Sample Bits (8 or 16) 198 | Repeat this SAMPLE_SIZE times 199 | 2 Bytes: Actual Sample Data 200 | 201 | //END OF DMF FORMAT -------------------------------------------------------------------------------- /extra/vgm2asm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // PC Engine PSG register friendly names. 8 | static const std::vector g_psgReg = { 9 | "psg_ch", 10 | "psg_mainvol", 11 | "psg_freq.lo", 12 | "psg_freq.hi", 13 | "psg_ctrl", 14 | "psg_pan", 15 | "psg_wavebuf", 16 | "psg_noise", 17 | "psg_lfoctrl", 18 | "psg_lfofreq" 19 | }; 20 | 21 | // Translate a LSB encoded 32 bytes word into a native 32 bytes unsigned 22 | // int. 23 | inline uint32_t u32(uint8_t b[4]) 24 | { 25 | return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)); 26 | } 27 | 28 | int main(int argc, char **argv) 29 | { 30 | std::ifstream input; 31 | input.open(argv[1], std::ifstream::binary); 32 | 33 | if(!input) 34 | { 35 | std::cerr << "Failed to open " << argv[1] << " " << std::endl; 36 | return 1; 37 | } 38 | 39 | uint8_t buffer[4]; 40 | uint32_t gd3Offset; 41 | uint32_t loopOffset; 42 | uint32_t dataOffset; 43 | 44 | // We first fetch the g3d, loop and data offset. 45 | input.seekg(0x14); 46 | input.read(reinterpret_cast(buffer), 4); 47 | gd3Offset = u32(buffer); 48 | 49 | input.seekg(0x1C); 50 | input.read(reinterpret_cast(buffer), 4); 51 | loopOffset = u32(buffer); 52 | 53 | input.seekg(0x34); 54 | input.read(reinterpret_cast(buffer), 4); 55 | dataOffset = u32(buffer); 56 | 57 | uint32_t dataSize = gd3Offset - dataOffset; 58 | loopOffset = (loopOffset + 0x1C) - dataOffset - 0x34; 59 | 60 | // Output register names 61 | for(size_t i=0; i(buffer), 1); 75 | if(0xb9 == buffer[0]) 76 | { 77 | // data + register index 78 | input.read(reinterpret_cast(buffer), 2); 79 | std::cout << " lda #$" << std::setw(2) << std::setfill('0') << std::hex << static_cast(buffer[1]) << std::endl; 80 | std::cout << " sta " << g_psgReg[buffer[0]] << std::endl; 81 | } 82 | else if(0x62 == buffer[0]) 83 | { 84 | // end of frame 85 | std::cout << " jsr wait_vsync" << std::endl; 86 | } 87 | } 88 | 89 | input.close(); 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /extra/vol_env_01.vgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockoS/dmf-player/711be7ab53fcd829765a47e8f941f18a543b4c3b/extra/vol_env_01.vgm -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2020, Vincent "MooZ" Cruz and other contributors. 2 | // All rights reserved. 3 | // Copyrights licensed under the New BSD License. See the accompanying 4 | // LICENSE file for terms. 5 | #include 6 | 7 | #include "dmf.h" 8 | #include "datareader.h" 9 | #include "pce.h" 10 | #include "pcewriter.h" 11 | 12 | int main(int argc, char** argv) { 13 | if(argc < 3) { 14 | fprintf(stderr, "Usage: %s out.asm file0.dmf file1.dmf ... \n", argv[0]); 15 | return EXIT_FAILURE; 16 | } 17 | 18 | PCE::Packer packer; 19 | 20 | for(int i=2; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | FILE *out; 8 | 9 | void print(const char *name, uint8_t* tbl, int count) { 10 | fprintf(out, "%s:\n", name); 11 | for(int i=0; i> 8; 37 | } 38 | print("sqr0.lo", sqr_lo, 512); 39 | print("sqr0.hi", sqr_hi, 512); 40 | for(i=0; i<512; i++) { 41 | uint16_t m = (255-i) * (255-i) / 4; 42 | sqr_lo[i] = m & 0xff; 43 | sqr_hi[i] = m >> 8; 44 | } 45 | print("sqr1.lo", sqr_lo, 512); 46 | print("sqr1.hi", sqr_hi, 512); 47 | fclose(out); 48 | return EXIT_SUCCESS; 49 | } 50 | -------------------------------------------------------------------------------- /pce.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2019, Vincent "MooZ" Cruz and other contributors. All rights reserved. 2 | // Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "pce.h" 11 | #include "pcewriter.h" 12 | 13 | namespace PCE { 14 | 15 | static Effect DMF2PCE(DMF::Effect fx) { 16 | switch(fx) { 17 | case DMF::ARPEGGIO: 18 | return Arpeggio; 19 | case DMF::PORTAMENTO_UP: 20 | return PortamentoUp; 21 | case DMF::PORTAMENTO_DOWN: 22 | return PortamentoDown; 23 | case DMF::PORTAMENTO_TO_NOTE: 24 | return PortamentoToNote; 25 | case DMF::VIBRATO: 26 | return Vibrato; 27 | case DMF::PORTAMENTO_TO_NOTE_VOLUME_SLIDE: 28 | return PortToNoteVolSlide; 29 | case DMF::VIBRATO_VOLUME_SIDE: 30 | return VibratoVolSlide; 31 | case DMF::TREMOLO: 32 | return Tremolo; 33 | case DMF::PANNING: 34 | return Panning; 35 | case DMF::SET_SPEED_VALUE_1: 36 | return SetSpeedValue1; 37 | case DMF::VOLUME_SLIDE: 38 | return VolumeSlide; 39 | case DMF::POSITION_JUMP: 40 | return PositionJump; 41 | case DMF::RETRIG: 42 | return Retrig; 43 | case DMF::PATTERN_BREAK: 44 | return PatternBreak; 45 | case DMF::SET_SPEED_VALUE_2: 46 | return SetSpeedValue2; 47 | case DMF::ARPEGGIO_SPEED: 48 | return ArpeggioSpeed; 49 | case DMF::NOTE_SLIDE_UP: 50 | return NoteSlideUp; 51 | case DMF::NOTE_SLIDE_DOWN: 52 | return NoteslideDown; 53 | case DMF::VIBRATO_MODE: 54 | return VibratoMode; 55 | case DMF::VIBRATO_DEPTH: 56 | return VibratoDepth; 57 | case DMF::FINE_TUNE: 58 | return FineTune; 59 | case DMF::SET_SAMPLES_BANK: 60 | return SetSampleBank; 61 | case DMF::NOTE_CUT: 62 | return NoteCut; 63 | case DMF::NOTE_DELAY: 64 | return NoteDelay; 65 | case DMF::SYNC_SIGNAL: 66 | return SyncSignal; 67 | case DMF::GLOBAL_FINE_TUNE: 68 | return GlobalFineTune; 69 | case DMF::SET_WAVE: 70 | return SetWave; 71 | case DMF::SET_NOISE: 72 | return EnableNoiseChannel; 73 | case DMF::SET_LFO_MODE: 74 | return SetLFOMode; 75 | case DMF::SET_LFO_SPEED: 76 | return SetLFOSpeed; 77 | case DMF::SET_SAMPLES: 78 | return SetSamples; 79 | default: 80 | return Rest; 81 | } 82 | } 83 | 84 | bool operator==(Envelope const& e0, Envelope const& e1) { 85 | if(e0.size != e1.size) { 86 | return false; 87 | } 88 | else if(e0.loop != e1.loop) { 89 | for(size_t i=0; i &buffer, size_t &rest) { 131 | for(; rest >= 64; rest -= (rest >= 256) ? 256 : rest) { 132 | buffer.push_back(PCE::RestEx); 133 | buffer.push_back(rest % 256); 134 | } 135 | if(rest) { 136 | buffer.push_back(PCE::Rest | rest); 137 | } 138 | rest = 0; 139 | } 140 | 141 | static void pack(Packer::Song &out, DMF::Song &song) { 142 | std::vector &matrix = out.matrix; 143 | 144 | std::vector offsets; 145 | offsets.resize(song.infos.totalRowsInPatternMatrix); 146 | matrix.resize(song.infos.systemChanCount); 147 | for(size_t j=0; j(pattern_data.effect[m].code))); 206 | 207 | uint8_t data; 208 | data = (pattern_data.effect[m].data != 0xffff) ? pattern_data.effect[m].data : 0x00; 209 | 210 | // Preprocess / fix 211 | // - Global fine tune 212 | if(pattern_data.effect[m].code == DMF::GLOBAL_FINE_TUNE) { 213 | if(data > 0x80) { 214 | data -= 0x80; 215 | } 216 | else { 217 | data = (data ^ 0xff) + 1; 218 | } 219 | } 220 | // - Volume slide 221 | else if(pattern_data.effect[m].code == DMF::VOLUME_SLIDE) { 222 | if(data > 0x0f) { 223 | // Positive delta. 224 | data >>= 4; 225 | } 226 | else { 227 | // Negative delta. 228 | data = ((data & 0x0f) ^ 0xff) + 1; 229 | } 230 | } 231 | // - Note cut 232 | else if(pattern_data.effect[m].code == DMF::NOTE_CUT) { 233 | // Nothing atm... 234 | } 235 | // - Set wav 236 | else if(pattern_data.effect[m].code == DMF::SET_WAVE) { 237 | data = out.wave[data % out.wave.size()]; 238 | } 239 | matrix[i].buffer[j].push_back(data); 240 | } 241 | } // effects 242 | matrix[i].buffer[j][last] |= 0x80; 243 | } 244 | flush_rest(matrix[i].buffer[j], rest); 245 | matrix[i].buffer[j].push_back(PCE::EndOfTrack); 246 | } 247 | } 248 | } 249 | 250 | static void pack(WaveTable &out, DMF::WaveTable &in) { 251 | out.resize(in.size()); 252 | for(size_t i=0; i(in[i] & 0x1f); // [todo] clamp or normalize? 254 | } 255 | } 256 | 257 | static void pack(Packer::Song &out, std::vector const& in) { 258 | InstrumentList &inst = out.instruments; 259 | inst.count = in.size(); 260 | 261 | inst.flag.resize(inst.count); 262 | 263 | inst.env[InstrumentList::Volume].size.resize(inst.count); 264 | inst.env[InstrumentList::Volume].loop.resize(inst.count); 265 | inst.env[InstrumentList::Volume].data.resize(inst.count); 266 | 267 | inst.env[InstrumentList::Arpeggio].size.resize(inst.count); 268 | inst.env[InstrumentList::Arpeggio].loop.resize(inst.count); 269 | inst.env[InstrumentList::Arpeggio].data.resize(inst.count); 270 | 271 | inst.env[InstrumentList::Wave].size.resize(inst.count); 272 | inst.env[InstrumentList::Wave].loop.resize(inst.count); 273 | inst.env[InstrumentList::Wave].data.resize(inst.count); 274 | 275 | for(size_t i=0; i(1 << in.bits); 313 | 314 | int error; 315 | SRC_DATA data; 316 | SRC_STATE *state = src_new(SRC_SINC_BEST_QUALITY, 1, &error) ; 317 | src_reset(state); 318 | 319 | out.rate = 7159090 / 1024; 320 | data.src_ratio = 7159090.f / 1024.f / (float)freq[j]; 321 | 322 | float *dummy = new float[in.data.size()]; 323 | data.data_in = dummy; 324 | data.data_out = new float[PCM_BLOCK_SIZE]; 325 | 326 | /* 327 | float s_min = in.data[0]; 328 | float s_max = in.data[0]; 329 | for(j=1; j s_max) { 334 | s_max = in.data[j]; 335 | } 336 | } 337 | */ 338 | for(j=0; j 1.f) ? 1.f : v); 342 | } 343 | 344 | long n = 0; 345 | data.input_frames_used = 0; 346 | do { 347 | data.data_in += data.input_frames_used; 348 | data.input_frames = dummy + in.data.size() - data.data_in; 349 | 350 | if(data.input_frames > PCM_BLOCK_SIZE) { 351 | data.input_frames = PCM_BLOCK_SIZE; 352 | data.end_of_input = 0; 353 | } 354 | else { 355 | data.end_of_input = 1; 356 | } 357 | data.output_frames = PCM_BLOCK_SIZE; 358 | data.input_frames_used = 0; 359 | data.output_frames_gen = 0; 360 | 361 | error = src_process(state, &data); 362 | 363 | n += data.input_frames_used; 364 | 365 | 366 | for(j=0; j 1.f) ? 1.f : u); 369 | uint8_t v = (0.5f * u + 0.5f) * 31.f; 370 | out.data.push_back(v); 371 | } 372 | } while(!data.end_of_input); 373 | 374 | out.data.push_back(0xff); 375 | 376 | src_delete(state); 377 | 378 | delete [] dummy; 379 | delete [] data.data_out; 380 | } 381 | 382 | void add(Packer &p, DMF::Song &in) { 383 | p.song.push_back({}); 384 | 385 | Packer::Song &song = p.song.back(); 386 | song.infos = in.infos; 387 | 388 | for(size_t i=0; i 9 | #include "dmf.h" 10 | 11 | #define PCE_CHAN_COUNT 6 12 | 13 | namespace PCE { 14 | 15 | enum Effect { 16 | Arpeggio, 17 | ArpeggioSpeed, 18 | PortamentoUp, 19 | PortamentoDown, 20 | PortamentoToNote, 21 | Vibrato, 22 | VibratoMode, 23 | VibratoDepth, 24 | PortToNoteVolSlide, 25 | VibratoVolSlide, 26 | Tremolo, 27 | Panning, 28 | SetSpeedValue1, 29 | VolumeSlide, 30 | PositionJump, 31 | Retrig, 32 | PatternBreak, 33 | SetSpeedValue2, 34 | SetWave, 35 | EnableNoiseChannel, 36 | SetLFOMode, 37 | SetLFOSpeed, 38 | NoteSlideUp, 39 | NoteslideDown, 40 | NoteCut, 41 | NoteDelay, 42 | SyncSignal, 43 | FineTune, 44 | GlobalFineTune, 45 | SetSampleBank, 46 | SetVolume, 47 | SetInstrument, 48 | Note, // Set note+octave 49 | NoteOff, 50 | SetSamples, 51 | RestEx = 0x3f, // For values >= 64 52 | Rest = 0x40, // For values between 0 and 63 53 | EndOfTrack = 0xff 54 | }; 55 | 56 | struct PatternMatrix { 57 | std::vector pattern; 58 | std::vector packed; 59 | std::vector> buffer; 60 | }; 61 | 62 | struct Envelope { 63 | typedef std::array Data_t; 64 | 65 | std::vector size; 66 | std::vector loop; 67 | std::vector data; 68 | }; 69 | 70 | bool operator==(Envelope const& e0, Envelope const& e1); 71 | bool operator!=(Envelope const& e0, Envelope const& e1); 72 | 73 | struct InstrumentList { 74 | enum EnvelopeType { 75 | Volume = 0, 76 | Arpeggio, 77 | Wave, 78 | EnvelopeCount 79 | }; 80 | std::vector flag; 81 | Envelope env[EnvelopeCount]; 82 | size_t count; 83 | }; 84 | 85 | bool operator==(InstrumentList const& i0, InstrumentList const& i1); 86 | bool operator!=(InstrumentList const& i0, InstrumentList const& i1); 87 | 88 | typedef std::vector WaveTable; 89 | 90 | struct Sample { 91 | uint32_t rate; 92 | std::vector data; 93 | }; 94 | 95 | bool operator==(Sample const& s0, Sample const& s1) ; 96 | bool operator!=(Sample const& s0, Sample const& s1); 97 | 98 | template 99 | void add(std::vector &data, std::vector &index, T const& elmnt) { 100 | size_t i; 101 | for(i=0; (i matrix; 113 | std::vector wave; 114 | InstrumentList instruments; 115 | std::vector sample; 116 | }; 117 | 118 | std::vector song; 119 | std::vector wave; 120 | std::vector sample; 121 | }; 122 | 123 | void add(Packer &p, DMF::Song &song); 124 | 125 | } // PCE 126 | 127 | #endif // PCE_H 128 | -------------------------------------------------------------------------------- /pce/frequency.inc: -------------------------------------------------------------------------------- 1 | freq_table.hi: 2 | freq_table.hi0: 3 | .db $1a 4 | .db $19 5 | .db $17 6 | .db $16 7 | .db $15 8 | .db $14 9 | .db $12 10 | .db $11 11 | .db $10 12 | .db $0f 13 | .db $0f 14 | .db $0e 15 | freq_table.hi1: 16 | .db $0d 17 | .db $0c 18 | .db $0b 19 | .db $0b 20 | .db $0a 21 | .db $0a 22 | .db $09 23 | .db $08 24 | .db $08 25 | .db $07 26 | .db $07 27 | .db $07 28 | freq_table.hi2: 29 | .db $06 30 | .db $06 31 | .db $05 32 | .db $05 33 | .db $05 34 | .db $05 35 | .db $04 36 | .db $04 37 | .db $04 38 | .db $03 39 | .db $03 40 | .db $03 41 | freq_table.hi3: 42 | .db $03 43 | .db $03 44 | .db $02 45 | .db $02 46 | .db $02 47 | .db $02 48 | .db $02 49 | .db $02 50 | .db $02 51 | .db $01 52 | .db $01 53 | .db $01 54 | freq_table.hi4: 55 | .db $01 56 | .db $01 57 | .db $01 58 | .db $01 59 | .db $01 60 | .db $01 61 | .db $01 62 | .db $01 63 | .db $01 64 | .db $00 65 | .db $00 66 | .db $00 67 | freq_table.hi5: 68 | .db $00 69 | .db $00 70 | .db $00 71 | .db $00 72 | .db $00 73 | .db $00 74 | .db $00 75 | .db $00 76 | .db $00 77 | .db $00 78 | .db $00 79 | .db $00 80 | freq_table.hi6: 81 | .db $00 82 | .db $00 83 | .db $00 84 | .db $00 85 | .db $00 86 | .db $00 87 | .db $00 88 | .db $00 89 | .db $00 90 | .db $00 91 | .db $00 92 | .db $00 93 | freq_table.hi7: 94 | .db $00 95 | .db $00 96 | .db $00 97 | .db $00 98 | .db $00 99 | .db $00 100 | .db $00 101 | .db $00 102 | .db $00 103 | .db $00 104 | .db $00 105 | .db $00 106 | freq_table.hi8: 107 | .db $00 108 | .db $00 109 | .db $00 110 | .db $00 111 | .db $00 112 | .db $00 113 | .db $00 114 | .db $00 115 | .db $00 116 | .db $00 117 | .db $00 118 | .db $00 119 | freq_table.lo: 120 | freq_table.lo0: 121 | .db $ba 122 | .db $3a 123 | .db $cf 124 | .db $79 125 | .db $36 126 | .db $06 127 | .db $e6 128 | .db $d6 129 | .db $d6 130 | .db $e4 131 | .db $00 132 | .db $28 133 | freq_table.lo1: 134 | .db $5d 135 | .db $9d 136 | .db $e7 137 | .db $3c 138 | .db $9b 139 | .db $03 140 | .db $73 141 | .db $eb 142 | .db $6b 143 | .db $f2 144 | .db $80 145 | .db $14 146 | freq_table.lo2: 147 | .db $ae 148 | .db $4e 149 | .db $f3 150 | .db $9e 151 | .db $4d 152 | .db $01 153 | .db $b9 154 | .db $75 155 | .db $35 156 | .db $f9 157 | .db $c0 158 | .db $8a 159 | freq_table.lo3: 160 | .db $57 161 | .db $27 162 | .db $f9 163 | .db $cf 164 | .db $a6 165 | .db $80 166 | .db $5c 167 | .db $3a 168 | .db $1a 169 | .db $fc 170 | .db $e0 171 | .db $c5 172 | freq_table.lo4: 173 | .db $ab 174 | .db $93 175 | .db $7c 176 | .db $67 177 | .db $53 178 | .db $40 179 | .db $2e 180 | .db $1d 181 | .db $0d 182 | .db $fe 183 | .db $f0 184 | .db $e2 185 | freq_table.lo5: 186 | .db $d5 187 | .db $c9 188 | .db $be 189 | .db $b3 190 | .db $a9 191 | .db $a0 192 | .db $97 193 | .db $8e 194 | .db $86 195 | .db $7f 196 | .db $78 197 | .db $71 198 | freq_table.lo6: 199 | .db $6a 200 | .db $64 201 | .db $5f 202 | .db $59 203 | .db $54 204 | .db $50 205 | .db $4b 206 | .db $47 207 | .db $43 208 | .db $3f 209 | .db $3c 210 | .db $38 211 | freq_table.lo7: 212 | .db $35 213 | .db $32 214 | .db $2f 215 | .db $2c 216 | .db $2a 217 | .db $28 218 | .db $25 219 | .db $23 220 | .db $21 221 | .db $1f 222 | .db $1e 223 | .db $1c 224 | freq_table.lo8: 225 | .db $1a 226 | .db $19 227 | .db $17 228 | .db $16 229 | .db $15 230 | .db $14 231 | .db $12 232 | .db $11 233 | .db $10 234 | .db $0f 235 | .db $0f 236 | .db $0e 237 | 238 | noise_table: 239 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 240 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 241 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 242 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 243 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 244 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 245 | .db $84,$ad,$af,$b2,$b5,$b7,$b9,$bb,$bd,$bf,$81,$83 246 | .db $84,$86,$87,$89,$8a,$8b,$8c,$8d,$8e,$8f,$90,$91 247 | .db $92,$86,$87,$89,$8a,$8b,$8c,$8d,$8e,$8f,$90,$91 248 | .db $92,$86,$87,$89,$8a,$8b,$8c,$8d,$8e,$8f,$90,$91 249 | -------------------------------------------------------------------------------- /pce/player.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (c) 2015-2020, Vincent "MooZ" Cruz and other contributors. All rights reserved. 2 | ; Copyrights licensed under the New BSD License. 3 | ; See the accompanying LICENSE file for terms. 4 | ;;------------------------------------------------------------------------------------------ 5 | 6 | ; [todo] 7 | ; porta to note borken? 8 | ; pattern_break 9 | ; position_jump 10 | ; fine_tune 11 | 12 | ;;------------------------------------------------------------------------------------------ 13 | 14 | ;; 15 | ;; Title: DMF player. 16 | ;; 17 | 18 | DMF_HEADER_SIZE = 12 19 | 20 | DMF_CHAN_COUNT = 6 21 | 22 | ;;------------------------------------------------------------------------------------------ 23 | ; Song effects. 24 | Arpeggio = $00 25 | ArpeggioSpeed = $01 26 | PortamentoUp = $02 27 | PortamentoDown = $03 28 | PortamentoToNote = $04 29 | Vibrato = $05 30 | VibratoMode = $06 31 | VibratoDepth = $07 32 | PortToNoteVolSlide = $08 33 | VibratoVolSlide = $09 34 | Tremolo = $0a 35 | Panning = $0b 36 | SetSpeedValue1 = $0c 37 | VolumeSlide = $0d 38 | PositionJump = $0e 39 | Retrig = $0f 40 | PatternBreak = $10 41 | ExtendedCommands = $11 42 | SetSpeedValue2 = $12 43 | SetWav = $13 44 | EnableNoiseChannel = $14 45 | SetLFOMode = $15 46 | SetLFOSpeed = $16 47 | EnableSampleOutput = $17 48 | SetVolume = $18 49 | SetInstrument = $19 50 | Note = $1a ; Set note+octave 51 | NoteOff = $1b 52 | RestEx = $3f ; For values >= 64 53 | Rest = $40 ; For values between 0 and 63 54 | 55 | ;;------------------------------------------------------------------------------------------ 56 | PCM_UPDATE = %1000_0000 57 | WAV_UPDATE = %0100_0000 58 | PAN_UPDATE = %0010_0000 59 | FRQ_UPDATE = %0001_0000 60 | NOI_UPDATE = %0000_0001 61 | 62 | ;;------------------------------------------------------------------------------------------ 63 | INST_VOL = %0000_0001 64 | INST_ARP = %0000_0010 65 | INST_WAV = %0000_0100 66 | 67 | ;;------------------------------------------------------------------------------------------ 68 | FX_PRT_UP = %0000_0001 69 | FX_PRT_DOWN = %0000_0010 70 | FX_PRT_NOTE_UP = %0000_0100 71 | FX_PRT_NOTE_DOWN = %0000_1000 72 | FX_VIBRATO = %0001_0000 73 | FX_NOTE = %1000_0000 74 | 75 | ;;------------------------------------------------------------------------------------------ 76 | .zp 77 | mul8.lo .ds 4 78 | mul8.hi .ds 4 79 | 80 | dmf.zp.begin: 81 | 82 | dmf.player.si .ds 2 83 | dmf.player.al .ds 1 84 | dmf.player.ah .ds 1 85 | dmf.player.bl .ds 1 86 | dmf.player.bh .ds 1 87 | dmf.player.cl .ds 1 88 | dmf.player.ch .ds 1 89 | dmf.player.dl .ds 1 90 | dmf.player.dh .ds 1 91 | dmf.player.r0 .ds 2 92 | dmf.player.r1 .ds 2 93 | 94 | dmf.player.status .ds 1 95 | dmf.player.samples.offset .ds 2 ; PCM samples index 96 | 97 | dmf.player.ptr .ds 2 98 | 99 | dmf.player.psg.main .ds 1 100 | dmf.player.psg.ctrl .ds DMF_CHAN_COUNT 101 | dmf.player.psg.freq.lo .ds DMF_CHAN_COUNT 102 | dmf.player.psg.freq.hi .ds DMF_CHAN_COUNT 103 | dmf.player.psg.pan .ds DMF_CHAN_COUNT 104 | 105 | dmf.player.flag .ds 1 106 | dmf.player.note .ds DMF_CHAN_COUNT ; [todo] move to bss? 107 | dmf.player.volume .ds DMF_CHAN_COUNT ; [todo] move to bss? 108 | dmf.player.rest .ds DMF_CHAN_COUNT ; [todo] move to bss? 109 | 110 | ; 0: backup of header mpr 111 | dmf.player.mpr_backup .ds 2 ; 1: backup of data mpr 112 | dmf.player.chn .ds 1 ; current psg channel 113 | dmf.player.chn.flag .ds DMF_CHAN_COUNT 114 | 115 | dmf.player.matrix.row .ds 1 ; current matrix row 116 | dmf.player.tick .ds 2 ; current time tick (fine/coarse kinda sort off) 117 | 118 | dmf.player.pattern.pos .ds 1 ; current pattern position 119 | 120 | dmf.player.detune .ds 1 ; global detune 121 | 122 | dmf.player.pcm.bank .ds PSG_CHAN_COUNT 123 | dmf.player.pcm.ptr .ds PSG_CHAN_COUNT*2 124 | dmf.player.pcm.state .ds 1 125 | 126 | dmf.player.note_on .ds 1 ; [todo] rename new_note 127 | 128 | dmf.zp.end: 129 | 130 | ;;------------------------------------------------------------------------------------------ 131 | .bss 132 | dmf.bss.begin: 133 | 134 | dmf.song.bank .ds 1 135 | dmf.song.id .ds 1 136 | dmf.song.infos: 137 | dmf.song.time.base .ds 1 138 | dmf.song.time.tick .ds 2 139 | dmf.song.pattern.rows .ds 1 140 | dmf.song.matrix.rows .ds 1 141 | dmf.song.instrument_count .ds 1 142 | dmf.song.instruments .ds 2 143 | dmf.song.wav .ds 2 144 | dmf.song.matrix .ds 2 145 | 146 | dmf.player.pattern.bank .ds DMF_CHAN_COUNT 147 | dmf.player.pattern.lo .ds DMF_CHAN_COUNT 148 | dmf.player.pattern.hi .ds DMF_CHAN_COUNT 149 | 150 | dmf.player.wav.id .ds DMF_CHAN_COUNT 151 | 152 | dmf.player.delay .ds DMF_CHAN_COUNT 153 | dmf.player.cut .ds DMF_CHAN_COUNT 154 | 155 | dmf.player.note.previous .ds DMF_CHAN_COUNT 156 | 157 | dmf.player.volume.orig .ds DMF_CHAN_COUNT 158 | dmf.player.volume.offset .ds DMF_CHAN_COUNT 159 | dmf.player.volume.delta .ds DMF_CHAN_COUNT 160 | 161 | dmf.player.freq.delta.lo .ds DMF_CHAN_COUNT 162 | dmf.player.freq.delta.hi .ds DMF_CHAN_COUNT 163 | 164 | dmf.instrument.flag .ds PSG_CHAN_COUNT 165 | dmf.instrument.flag.orig .ds PSG_CHAN_COUNT 166 | 167 | dmf.instrument.vol.size .ds PSG_CHAN_COUNT 168 | dmf.instrument.vol.loop .ds PSG_CHAN_COUNT 169 | dmf.instrument.vol.lo .ds PSG_CHAN_COUNT 170 | dmf.instrument.vol.hi .ds PSG_CHAN_COUNT 171 | dmf.instrument.vol.index .ds PSG_CHAN_COUNT 172 | 173 | dmf.instrument.arp.size .ds PSG_CHAN_COUNT 174 | dmf.instrument.arp.loop .ds PSG_CHAN_COUNT 175 | dmf.instrument.arp.lo .ds PSG_CHAN_COUNT 176 | dmf.instrument.arp.hi .ds PSG_CHAN_COUNT 177 | dmf.instrument.arp.index .ds PSG_CHAN_COUNT 178 | 179 | dmf.instrument.wav.size .ds PSG_CHAN_COUNT 180 | dmf.instrument.wav.loop .ds PSG_CHAN_COUNT 181 | dmf.instrument.wav.lo .ds PSG_CHAN_COUNT 182 | dmf.instrument.wav.hi .ds PSG_CHAN_COUNT 183 | dmf.instrument.wav.index .ds PSG_CHAN_COUNT 184 | 185 | dmf.fx.flag .ds PSG_CHAN_COUNT 186 | 187 | dmf.fx.arp.data .ds PSG_CHAN_COUNT 188 | dmf.fx.arp.current .ds PSG_CHAN_COUNT 189 | dmf.fx.arp.tick .ds PSG_CHAN_COUNT 190 | dmf.fx.arp.speed .ds PSG_CHAN_COUNT 191 | 192 | dmf.fx.vib.data .ds PSG_CHAN_COUNT 193 | dmf.fx.vib.tick .ds PSG_CHAN_COUNT 194 | 195 | dmf.fx.prt.speed .ds PSG_CHAN_COUNT ; portamento speed 196 | 197 | dmf.pcm.bank .ds PSG_CHAN_COUNT 198 | dmf.pcm.src.bank .ds PSG_CHAN_COUNT 199 | dmf.pcm.src.ptr .ds PSG_CHAN_COUNT*2 200 | 201 | dmf.bss.end: 202 | 203 | ;;------------------------------------------------------------------------------------------ 204 | .code 205 | 206 | ; Align to 256 207 | .org (* + $ff) & $ff00 208 | .include "mul.inc" 209 | .include "sin.inc" 210 | 211 | ;; 212 | ;; Function: dmf_init 213 | ;; Initializes player internals. 214 | ;; 215 | dmf_init: 216 | lda #high(sqr0.lo) 217 | sta 3 336 | bbr0 ); -------------------------------------------------------------------------------- /pce/player_standalone.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (c) 2015-2019, Vincent "MooZ" Cruz and other contributors. All rights reserved. 2 | ; Copyrights licensed under the New BSD License. 3 | ; See the accompanying LICENSE file for terms. 4 | ;;-------_---------------------------------------------------------------------------------- 5 | 6 | ; VDC (Video Display Controller) 7 | videoport .equ $0000 8 | 9 | video_reg .equ videoport 10 | video_reg_l .equ video_reg 11 | video_reg_h .equ video_reg+1 12 | 13 | video_data .equ videoport+2 14 | video_data_l .equ video_data 15 | video_data_h .equ video_data+1 16 | 17 | ;;--------------------------------------------------------------------- 18 | ; VCE 19 | colorport = $0400 20 | color_ctrl = colorport 21 | 22 | color_reg = colorport+2 23 | color_reg_lo = color_reg 24 | color_reg_hi = color_reg+1 25 | 26 | color_data = colorport+4 27 | color_data_lo = color_data 28 | color_data_hi = color_data+1 29 | 30 | ;;--------------------------------------------------------------------- 31 | ; TIMER 32 | timerport .equ $0C00 33 | timer_cnt .equ timerport 34 | timer_ctrl .equ timerport+1 35 | 36 | ;;--------------------------------------------------------------------- 37 | ; IRQ ports 38 | irqport .equ $1400 39 | irq_disable .equ irqport+2 40 | irq_status .equ irqport+3 41 | 42 | ;;--------------------------------------------------------------------- 43 | ; PSG informations 44 | psgport .equ $0800 45 | psg_chn .equ psgport 46 | psg_mainvol .equ psgport+1 47 | psg_freq_lo .equ psgport+2 48 | psg_freq_hi .equ psgport+3 49 | psg_ctrl .equ psgport+4 50 | psg_pan .equ psgport+5 51 | psg_wavebuf .equ psgport+6 52 | psg_noise .equ psgport+7 53 | psg_lfoctrl .equ psgport+9 54 | psg_lfofreq .equ psgport+8 55 | 56 | PSG_CHAN_COUNT .equ $06 ; channel count 57 | 58 | ;;--------------------------------------------------------------------- 59 | ; PSG control register bit masks 60 | PSG_CTRL_CHAN_ON .equ %1000_0000 ; channel on (1), off(0) 61 | PSG_CTRL_CHAN_OFF .equ %0000_0000 ; channel on (1), off(0) 62 | PSG_CTRL_WRITE_RESET .equ %0100_0000 ; reset waveform write index to 0 63 | PSG_CTRL_DDA_ON .equ %1100_0000 ; dda output on(1), off(0) 64 | PSG_CTRL_VOL_MASK .equ %0001_1111 ; channel volume 65 | PSG_CTRL_FULL_VOLUME .equ %0011_1111 ; channel maximum volume (bit 5 is unused) 66 | 67 | PSG_VOLUME_MAX = $1f ; Maximum volume value 68 | 69 | .zp 70 | _bl .ds 1 71 | _si .ds 2 72 | _vdc_reg .ds 1 73 | _vdc_status .ds 1 74 | _vdc_ctrl .ds 1 75 | _vsync_cnt .ds 1 76 | 77 | joyport = $1000 78 | 79 | .bss 80 | joyold .ds 1 81 | joypad .ds 1 82 | joytrg .ds 1 83 | 84 | ;;--------------------------------------------------------------------- 85 | 86 | ;---------------------------------------------------------------------- 87 | ; Vector table 88 | ;---------------------------------------------------------------------- 89 | .data 90 | .bank 0 91 | .org $FFF6 92 | 93 | .dw irq_2 ; irq 2 94 | .dw irq_1 ; irq 1 95 | .dw irq_timer ; timer 96 | .dw irq_nmi ; nmi 97 | .dw irq_reset ; reset 98 | 99 | ;---------------------------------------------------------------------- 100 | ; IRQ Vectors 101 | ;---------------------------------------------------------------------- 102 | .code 103 | .bank 0 104 | .org $E000 105 | 106 | .include "task_manager.asm" 107 | .include "player.asm" 108 | .include "frequency.inc" 109 | 110 | ;---------------------------------------------------------------------- 111 | ; 112 | ;---------------------------------------------------------------------- 113 | .macro joypad_delay 114 | pha 115 | pla 116 | nop 117 | nop 118 | .endmacro 119 | 120 | joypad_read: 121 | lda #$01 ; reset multitap to joypad #1 122 | sta joyport 123 | lda #$03 124 | sta joyport 125 | joypad_delay 126 | 127 | lda #$01 ; read directions (SEL=1) 128 | sta joyport 129 | joypad_delay 130 | 131 | lda joypad 132 | sta joyold 133 | lda joyport 134 | asl A 135 | asl A 136 | asl A 137 | asl A 138 | sta joypad 139 | 140 | stz joyport ; read buttons (SEL=0) 141 | joypad_delay 142 | 143 | lda joyport 144 | and #$0f 145 | ora joypad 146 | eor #$ff 147 | sta joypad 148 | 149 | eor joyold 150 | and joypad 151 | sta joytrg 152 | 153 | rts 154 | 155 | ;---------------------------------------------------------------------- 156 | ; IRQ 2 157 | ;---------------------------------------------------------------------- 158 | irq_2: 159 | rti 160 | 161 | ;---------------------------------------------------------------------- 162 | ; IRQ 1 163 | ; HSync/VSync/VRAM DMA/etc... 164 | ;---------------------------------------------------------------------- 165 | irq_1: 166 | lda video_reg ; get VDC status register 167 | sta <_vdc_status 168 | 169 | bbr5 <_vdc_status, @end 170 | @vsync: 171 | task.irq_install 172 | 173 | st0 #$06 174 | st1 #$40 175 | st2 #$00 176 | 177 | inc <_vsync_cnt 178 | @end: 179 | stz video_reg 180 | rti 181 | 182 | ;---------------------------------------------------------------------- 183 | ; CPU Timer. 184 | ;---------------------------------------------------------------------- 185 | irq_timer: 186 | pha 187 | phx 188 | phy 189 | 190 | jsr dmf_pcm_update 191 | stz irq_status 192 | 193 | ply 194 | plx 195 | pla 196 | rti 197 | 198 | ;---------------------------------------------------------------------- 199 | ; NMI. 200 | ;---------------------------------------------------------------------- 201 | irq_nmi: 202 | rti 203 | 204 | ;---------------------------------------------------------------------- 205 | ; Default VDC registers value. 206 | ;---------------------------------------------------------------------- 207 | vdcInitTable: 208 | ; reg low hi 209 | .db $07, $00, $00 ; background x-scroll register 210 | .db $08, $00, $00 ; background y-scroll register 211 | .db $09, $00, $00 ; background map size 212 | .db $0A, $02, $02 ; horizontal period register 213 | .db $0B, $1F, $04 ; horizontal display register 214 | .db $0C, $02, $17 ; vertical sync register 215 | .db $0D, $DF, $00 ; vertical display register 216 | .db $0E, $0C, $00 ; vertical display position end register 217 | 218 | ;---------------------------------------------------------------------- 219 | ; Reset. 220 | ; This routine is called when the console is powered on. 221 | ;---------------------------------------------------------------------- 222 | irq_reset: 223 | sei ; disable interrupts 224 | csh ; select the 7.16 MHz clock 225 | cld ; clear the decimal flag 226 | ldx #$FF ; initialize the stack pointer 227 | txs 228 | lda #$FF ; map the I/O bank in the first page 229 | tam #0 230 | lda #$F8 ; and the RAM bank in the second page 231 | tam #1 232 | stz $2000 ; clear all the RAM 233 | tii $2000,$2001,$1FFF 234 | 235 | lda #%11111001 236 | sta irq_disable 237 | stz irq_status 238 | 239 | stz timer_ctrl ; disable timer 240 | 241 | st0 #$05 ; set vdc control register 242 | st1 #$00 ; disable vdc interupts 243 | st2 #$00 ; sprite and bg are disabled 244 | 245 | lda #low(vdcInitTable) ; setup vdc 246 | sta <_si 247 | lda #high(vdcInitTable) 248 | sta <_si+1 249 | 250 | cly 251 | .l0: 252 | lda [_si],Y 253 | sta videoport 254 | iny 255 | lda [_si],Y 256 | sta video_data_l 257 | iny 258 | lda [_si],Y 259 | sta video_data_h 260 | iny 261 | cpy #24 262 | bne .l0 263 | 264 | ; clear bat 265 | st0 #$00 266 | st1 #$00 267 | st2 #$00 268 | 269 | st0 #$02 270 | ldy #$20 271 | @bat.y; 272 | ldx #$20 273 | @bat.x: 274 | st1 #$00 275 | st2 #$02 276 | dex 277 | bne @bat.x 278 | dey 279 | bne @bat.y 280 | 281 | ; set vdc control register 282 | st0 #5 283 | ; enable bg, enable sprite, vertical blanking 284 | lda #%1100_1100 285 | sta <_vdc_ctrl 286 | sta video_data_l 287 | st2 #$00 288 | 289 | lda #%00000_1_00 290 | sta color_ctrl 291 | 292 | clx 293 | .l1: 294 | stx psg_chn 295 | lda #$ff 296 | sta psg_mainvol 297 | sta psg_pan 298 | 299 | inx 300 | cpx #PSG_CHAN_COUNT 301 | bne .l1 302 | 303 | jsr dmf_init 304 | 305 | lda #low(user_update) 306 | sta <_si 307 | lda #high(user_update) 308 | sta <_si+1 309 | jsr task.add 310 | 311 | lda #low(dmf_update) 312 | sta <_si 313 | lda #high(dmf_update) 314 | sta <_si+1 315 | jsr task.add 316 | 317 | stz timer_cnt 318 | 319 | lda #$01 320 | sta timer_ctrl 321 | 322 | ldy #$00 323 | jsr dmf_load_song 324 | 325 | cli 326 | 327 | .loop: 328 | stz <_vsync_cnt 329 | @wait_vsync: 330 | lda <_vsync_cnt 331 | beq @wait_vsync 332 | 333 | bra .loop 334 | 335 | user_update: 336 | jsr joypad_read 337 | lda joytrg 338 | bit #$01 339 | beq @end 340 | 341 | sei 342 | 343 | ldy dmf.song.id 344 | iny 345 | cpy #song.count 346 | bne @no_reset 347 | cly 348 | @no_reset: 349 | 350 | stz 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pcewriter.h" 9 | 10 | namespace PCE { 11 | 12 | struct Context { 13 | std::string filename; 14 | FILE *stream; 15 | size_t output_bytes; 16 | uint32_t bank; 17 | }; 18 | 19 | static bool open(const std::string &in, Context &ctx) { 20 | ctx.filename = in; 21 | ctx.stream = fopen(in.c_str(), "wb"); 22 | if(!ctx.stream) { 23 | fprintf(stderr, "Failed to open %s: %s\n", in.c_str(), strerror(errno)); 24 | return false; 25 | } 26 | ctx.bank = 0; 27 | ctx.output_bytes = 0; 28 | return true; 29 | } 30 | 31 | static void close(Context &ctx) { 32 | if(ctx.stream) { 33 | fclose(ctx.stream); 34 | ctx.stream = nullptr; 35 | } 36 | } 37 | 38 | #define ELEMENTS_PER_LINE 16 39 | 40 | static void write_ptr_tbl(Context &ctx, const char* prefix, const char* suffix, size_t start, size_t count, size_t elements_per_line, bool bank) { 41 | static char const* postfix[] = { "lo", "hi" }; 42 | static char const* op[] = { "dwl", "dwh" }; 43 | 44 | // Compute element char count and adjust elements per line. 45 | size_t element_char_len = strlen(prefix) + strlen(suffix) + 8; 46 | if((element_char_len*elements_per_line) >= MAX_CHAR_PER_LINE) { 47 | elements_per_line = MAX_CHAR_PER_LINE / element_char_len; 48 | } 49 | 50 | if(bank) { 51 | fprintf(ctx.stream, "%s.%s.bank:\n", prefix, suffix); 52 | for(size_t i=0; i(start+i), suffix); 54 | i++; 55 | for(size_t j=1; (j(start+i), suffix); 57 | } 58 | fprintf(ctx.stream, "\n"); 59 | } 60 | } 61 | 62 | for(int p=0; p<2; p++) { 63 | fprintf(ctx.stream, "%s.%s.%s:\n", prefix, suffix, postfix[p]); 64 | for(size_t i=0; i(start+i), suffix); 66 | i++; 67 | for(size_t j=1; (j(start+i), suffix); 69 | } 70 | fprintf(ctx.stream, "\n"); 71 | } 72 | } 73 | } 74 | 75 | static void write_ptr_tbl(Context &ctx, const char* name, size_t start, size_t count, size_t elements_per_line, bool bank) { 76 | static char const* postfix[] = { "lo", "hi" }; 77 | static char const* op[] = { "dwl", "dwh" }; 78 | 79 | // Compute element char count and adjust elements per line. 80 | size_t element_char_len = strlen(name) + 8; 81 | if((element_char_len*elements_per_line) >= MAX_CHAR_PER_LINE) { 82 | elements_per_line = MAX_CHAR_PER_LINE / element_char_len; 83 | } 84 | 85 | if(bank) { 86 | fprintf(ctx.stream, "%s.bank:\n", name); 87 | for(size_t i=0; i(start+i)); 89 | i++; 90 | for(size_t j=1; (j(start+i)); 92 | } 93 | fprintf(ctx.stream, "\n"); 94 | } 95 | } 96 | 97 | for(int p=0; p<2; p++) { 98 | fprintf(ctx.stream, "%s.%s:\n", name, postfix[p]); 99 | for(size_t i=0; i(start+i)); 101 | i++; 102 | for(size_t j=1; (j(start+i)); 104 | } 105 | fprintf(ctx.stream, "\n"); 106 | } 107 | } 108 | } 109 | 110 | template 111 | static void write_tbl(Context &ctx, std::vector const& elmnt, size_t elements_per_line) { 112 | for(size_t i=0; i(elmnt[i++])); 114 | for(size_t j=1; (j(elmnt[i])); 116 | } 117 | fprintf(ctx.stream, "\n"); 118 | } 119 | } 120 | 121 | static void write_bytes(Context &ctx, const uint8_t* buffer, size_t size, size_t elements_per_line) { 122 | for(size_t i=0; i& index, size_t elements_per_line, bool bank) { 134 | static char const* postfix[] = { "lo", "hi" }; 135 | static char const* op[] = { "dwl", "dwh" }; 136 | 137 | // Compute element char count and adjust elements per line. 138 | size_t element_char_len = strlen(prefix) + strlen(element) + 8; 139 | if((element_char_len*elements_per_line) >= MAX_CHAR_PER_LINE) { 140 | elements_per_line = MAX_CHAR_PER_LINE / element_char_len; 141 | } 142 | 143 | size_t count = index.size(); 144 | 145 | if(bank) { 146 | fprintf(ctx.stream, "%s.%s.bank:\n", prefix, table); 147 | for(size_t i=0; i pattern_index; 172 | 173 | index = 0; 174 | for(size_t i=0; i const& matrix = in.matrix; 176 | 177 | pattern_index.resize(matrix[i].pattern.size()); 178 | for(size_t j=0; j(i)); 183 | write_ptr_tbl(ctx, prefix, name, "pat", pattern_index, ELEMENTS_PER_LINE, true); 184 | 185 | index += matrix[i].packed.size(); 186 | } 187 | } 188 | 189 | static void write_pattern(Context &ctx, Packer::Song const& in, const char *prefix) { 190 | size_t index = 0; 191 | 192 | for(size_t i=0; i= 8192) { 197 | fprintf(ctx.stream, "\n .data\n .bank DMF_DATA_ROM_BANK+%d\n .org (DMF_DATA_MPR << 13)\n", ctx.bank); 198 | ctx.output_bytes = 0; 199 | ctx.bank++; 200 | } 201 | fprintf(ctx.stream, "%s.pat%02x:\n", prefix, static_cast(index++)); 202 | write_bytes(ctx, pattern.buffer[j].data(), pattern.buffer[j].size(), 16); 203 | ctx.output_bytes += pattern.buffer[j].size(); 204 | } 205 | } 206 | } 207 | 208 | static void write_instruments(Context &ctx, const char *prefix, InstrumentList const& instruments) { 209 | const char* names[InstrumentList::EnvelopeCount] = { 210 | "vol", 211 | "arp", 212 | "wav" 213 | }; 214 | char buffer[256]; 215 | 216 | fprintf(ctx.stream, "%s.it:\n", prefix); 217 | for(size_t i=0; i(j)); 233 | if(instruments.env[i].size[j]) { 234 | write_bytes(ctx, &instruments.env[i].data[j][0], instruments.env[i].size[j], 16); 235 | } 236 | } 237 | } 238 | } 239 | 240 | 241 | static void write_samples(Context &ctx, const char* prefix, std::vector const& samples) { 242 | for(size_t j=0; j(j)); 246 | while(start < end) { 247 | size_t count = ((start+16) > end) ? (end-start) : 16; 248 | size_t next = ctx.output_bytes + count; 249 | if(next >= 8192) { 250 | count = 8192 - ctx.output_bytes; 251 | } 252 | write_bytes(ctx, samples[j].data.data()+start, count, 16); 253 | if(next >= 8192) { 254 | fprintf(ctx.stream, " .data\n .bank DMF_DATA_ROM_BANK+%d\n .org (DMF_DATA_MPR << 13)\n", ctx.bank); 255 | ctx.output_bytes = 0; 256 | ctx.bank++; 257 | } 258 | else { 259 | ctx.output_bytes += count; 260 | } 261 | start += count; 262 | } 263 | } 264 | } 265 | 266 | static bool write_header(Context &ctx, Packer const &in) { 267 | #define print(name, member) \ 268 | do { \ 269 | fprintf(ctx.stream, #name":\n"); \ 270 | for(size_t i=0; i(i)); 296 | write_bytes(ctx, in.wave[i].data(), in.wave[i].size(), ELEMENTS_PER_LINE); 297 | } 298 | 299 | std::vector wave_first; 300 | for(size_t i=0; i(i)); 312 | write_tbl(ctx, in.song[i].sample, ELEMENTS_PER_LINE); 313 | } 314 | 315 | for(size_t i=0; i(i)); 318 | write_instruments(ctx, prefix, in.song[i].instruments); 319 | } 320 | 321 | ctx.bank++; 322 | 323 | fprintf(ctx.stream, " .data\n .bank DMF_DATA_ROM_BANK+%d\n .org (DMF_MATRIX_MPR << 13)\n", ctx.bank); 324 | write_ptr_tbl(ctx, "song" ,"mat", 0, in.song.size(), ELEMENTS_PER_LINE, false); 325 | for(size_t i=0; i(i)); 328 | fprintf(ctx.stream, "%s.mat:\n", name); 329 | write_matrices(ctx, in.song[i], name); 330 | } 331 | 332 | ctx.bank++; 333 | 334 | #undef print 335 | 336 | return true; 337 | } 338 | 339 | bool write(std::string const& filename, Packer const& in) { 340 | Context ctx; 341 | 342 | ctx.output_bytes = 8192; 343 | ctx.bank = 0; 344 | 345 | if(!open(filename, ctx)) { 346 | return false; 347 | } 348 | if(!write_header(ctx, in)) { 349 | return false; 350 | } 351 | 352 | ctx.output_bytes = 8192; 353 | for(size_t i=0; i(i)); 356 | write_pattern(ctx, in.song[i], name); 357 | } 358 | 359 | write_samples(ctx, "song.sp.data", in.sample); 360 | 361 | close(ctx); 362 | return true; 363 | } 364 | 365 | } // PCE 366 | -------------------------------------------------------------------------------- /pcewriter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2019, Vincent "MooZ" Cruz and other contributors. 2 | // All rights reserved. 3 | // Copyrights licensed under the New BSD License. See the accompanying 4 | // LICENSE file for terms. 5 | #ifndef PCE_WRITER_H 6 | #define PCE_WRITER_H 7 | 8 | #include "pce.h" 9 | 10 | #define MAX_CHAR_PER_LINE 92 11 | 12 | namespace PCE { 13 | 14 | bool write(std::string const& filename, Packer const& in); 15 | 16 | } // PCE 17 | 18 | #endif // PCE_WRITER_H 19 | -------------------------------------------------------------------------------- /sin.table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | FILE *out; 9 | 10 | void print(const char *name, uint8_t* tbl, int count) { 11 | fprintf(out, "%s:\n", name); 12 | for(int i=0; i