Mercurial > audlegacy
diff Plugins/Input/adplug/core/msc.cpp @ 428:15ca2ea93a30 trunk
[svn] Sync with upstream CVS. This implements RIX playback.
author | chainsaw |
---|---|
date | Sat, 14 Jan 2006 07:27:13 -0800 |
parents | |
children | 2b06eb5e472d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/adplug/core/msc.cpp Sat Jan 14 07:27:13 2006 -0800 @@ -0,0 +1,322 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * msc.c - MSC Player by Lubomir Bulej (pallas@kadan.cz) + */ + +#include <stdio.h> + +#include "msc.h" + +const unsigned char CmscPlayer::msc_signature [MSC_SIGN_LEN] = { + 'C', 'e', 'r', 'e', 's', ' ', '\x13', ' ', + 'M', 'S', 'C', 'p', 'l', 'a', 'y', ' ' }; + + +/*** public methods *************************************/ + +CPlayer * +CmscPlayer::factory (Copl * newopl) +{ + return new CmscPlayer (newopl); +} + + +CmscPlayer::CmscPlayer (Copl * newopl) : CPlayer (newopl) { + desc = NULL; + msc_data = NULL; + raw_data = NULL; + nr_blocks = 0; +} + + +CmscPlayer::~CmscPlayer () +{ + if (raw_data != NULL) + delete [] raw_data; + + if (msc_data != NULL) { + // free compressed blocks + for (int blk_num = 0; blk_num < nr_blocks; blk_num++) { + if (msc_data [blk_num].mb_data != NULL) + delete [] msc_data [blk_num].mb_data; + } + + delete [] msc_data; + } + + if (desc != NULL) + delete [] desc; +} + + +bool +CmscPlayer::load (const std::string & filename, const CFileProvider & fp) +{ + binistream * bf; + msc_header hdr; + + // open and validate the file + bf = fp.open (filename); + if (! bf) + return false; + + if (! load_header (bf, & hdr)) { + fp.close (bf); + return false; + } + + // get stuff from the header + version = hdr.mh_ver; + timer_div = hdr.mh_timer; + nr_blocks = hdr.mh_nr_blocks; + block_len = hdr.mh_block_len; + + if (! nr_blocks) { + fp.close (bf); + return false; + } + + // load compressed data blocks + msc_data = new msc_block [nr_blocks]; + raw_data = new u8 [block_len]; + + for (int blk_num = 0; blk_num < nr_blocks; blk_num++) { + msc_block blk; + + blk.mb_length = bf->readInt (2); + blk.mb_data = new u8 [blk.mb_length]; + for (int oct_num = 0; oct_num < blk.mb_length; oct_num++) { + blk.mb_data [oct_num] = bf->readInt (1); + } + + msc_data [blk_num] = blk; + } + + // clean up & initialize + fp.close (bf); + rewind (0); + + return true; +} + + +bool +CmscPlayer::update () +{ + // output data + while (! delay) { + u8 cmnd; + u8 data; + + // decode data + if (! decode_octet (& cmnd)) + return false; + + if (! decode_octet (& data)) + return false; + + // check for special commands + switch (cmnd) { + + // delay + case 0xff: + delay = 1 + (u8) (data - 1); + break; + + // play command & data + default: + opl->write (cmnd, data); + + } // command switch + } // play pass + + + // count delays + if (delay) + delay--; + + // advance player position + play_pos++; + return true; +} + + +void +CmscPlayer::rewind (int subsong) +{ + // reset state + dec_prefix = 0; + block_num = 0; + block_pos = 0; + play_pos = 0; + raw_pos = 0; + delay = 0; + + // init the OPL chip and go to OPL2 mode + opl->init (); + opl->write (1, 32); +} + + +float +CmscPlayer::getrefresh () +{ + // PC timer oscillator frequency / wait register + return 1193180 / (float) (timer_div ? timer_div : 0xffff); +} + +std::string +CmscPlayer::gettype () +{ + char vstr [40]; + + snprintf (vstr, sizeof (vstr), "AdLib MSCplay (version %d)", version); + return std::string (vstr); +} + + +/*** private methods *************************************/ + +bool +CmscPlayer::load_header (binistream * bf, msc_header * hdr) +{ + // check signature + bf->readString ((char *) hdr->mh_sign, sizeof (hdr->mh_sign)); + if (memcmp (msc_signature, hdr->mh_sign, MSC_SIGN_LEN) != 0) + return false; + + // check version + hdr->mh_ver = bf->readInt (2); + if (hdr->mh_ver != 0) + return false; + + bf->readString ((char *) hdr->mh_desc, sizeof (hdr->mh_desc)); + hdr->mh_timer = bf->readInt (2); + hdr->mh_nr_blocks = bf->readInt (2); + hdr->mh_block_len = bf->readInt (2); + return true; +} + + +bool +CmscPlayer::decode_octet (u8 * output) +{ + msc_block blk; // compressed data block + + if (block_num >= nr_blocks) + return false; + + blk = msc_data [block_num]; + while (1) { + u8 octet; // decoded octet + u8 len_corr; // length correction + + // advance to next block if necessary + if (block_pos >= blk.mb_length && dec_len == 0) { + block_num++; + if (block_num >= nr_blocks) + return false; + + blk = msc_data [block_num]; + block_pos = 0; + raw_pos = 0; + } + + // decode the compressed music data + switch (dec_prefix) { + + // decode prefix + case 155: + case 175: + octet = blk.mb_data [block_pos++]; + if (octet == 0) { + // invalid prefix, output original + octet = dec_prefix; + dec_prefix = 0; + break; + } + + // isolate length and distance + dec_len = (octet & 0x0F); + len_corr = 2; + + dec_dist = (octet & 0xF0) >> 4; + if (dec_prefix == 155) + dec_dist++; + + // next decode step for respective prefix type + dec_prefix++; + continue; + + + // check for extended length + case 156: + if (dec_len == 15) + dec_len += blk.mb_data [block_pos++]; + + // add length correction and go for copy mode + dec_len += len_corr; + dec_prefix = 255; + continue; + + + // get extended distance + case 176: + dec_dist += 17 + 16 * blk.mb_data [block_pos++]; + len_corr = 3; + + // check for extended length + dec_prefix = 156; + continue; + + + // prefix copy mode + case 255: + octet = raw_data [raw_pos - dec_dist]; + + dec_len--; + if (dec_len == 0) { + // back to normal mode + dec_prefix = 0; + } + + break; + + + // normal mode + default: + octet = blk.mb_data [block_pos++]; + if (octet == 155 || octet == 175) { + // it's a prefix, restart + dec_prefix = octet; + continue; + } + } // prefix switch + + + // output the octet + if (output != NULL) + *output = octet; + + raw_data [raw_pos++] = octet; + break; + }; // decode pass + + return true; +}