Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

channel.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2002 Xavier Trochu
00003 
00004 This software is provided 'as-is', without any express or implied warranty. In no event
00005 will the authors be held liable for any damages arising from the use of this software.
00006 
00007 Permission is granted to anyone to use this software for any purpose, including commercial
00008 applications, and to alter it and redistribute it freely, subject to the following
00009 restrictions:
00010 
00011 1. The origin of this software must not be misrepresented; you must not claim that you
00012    wrote the original software. If you use this software in a product, an acknowledgment
00013    in the product documentation would be appreciated but is not required.
00014 
00015 2. Altered source versions must be plainly marked as such, and must not be misrepresented 
00016    as being the original software.
00017 
00018 3. This notice may not be removed or altered from any source distribution.
00019 */
00020 #include <sstream>
00021 #include <fstream>
00022 #include <stdexcept>
00023 
00024 #include "channel.h"
00025 #include "endian.h"
00026 #include "chunck.h"
00027 #include "chunck_type.h"
00028 
00029 #ifdef DEBUG_CHANNEL
00030 #include <iostream>
00031 #include <iomanip>
00032 #endif
00033 
00034 
00035 using namespace std;
00036 
00037 void saud_channel::handleStrk(chunck & b) throw(exception) {
00038     int size = b.get_size();
00039     if(size != 14 && size != 10) {
00040         ostringstream oss;
00041         oss << "STRK has a invalid size : " << size;
00042         throw runtime_error(oss.str());
00043     }
00044 #ifdef DEBUG_CHANNEL
00045     int unk1 = b.get_byte();
00046     int unk2 = b.get_byte();
00047     int unk3 = from_big_endian(b.get_dword());
00048     int unk4 = from_big_endian(b.get_dword());
00049     cout << "TRACK " << _track << " STRK(" << size << ") content : ";
00050     cout << unk1 << " " << unk2 << " " << unk3 << "  "<< unk4;
00051     if(size == 14) {
00052         int unk5 = from_big_endian(b.get_dword());
00053         cout << " " << unk5;
00054     }
00055     cout << endl;
00056 #endif
00057 }
00058 
00059 void saud_channel::handleSmrk(chunck & b) throw(exception) {
00060 #ifdef DEBUG_CHANNEL
00061     int size = b.get_size();
00062     if(size > 0) // throw runtime_error("SMRK has a invalid size");
00063     {
00064         cout << "TRACK " << _track << " SMRK(" << size << ") content : ";
00065         for(int i = 0; i < size; i++) {
00066             cout << setw(3) << static_cast<int>(b.get_byte()) << " ";
00067         }
00068         cout << endl;
00069     }
00070 #endif
00071     _mark_reached = true;
00072 }
00073 
00074 void saud_channel::handleShdr(chunck & b) throw(exception) {
00075     int size = b.get_size();
00076     if(size != 4) throw runtime_error("SMRK has a invalid size");
00077 #ifdef DEBUG_CHANNEL
00078     int unknown = b.get_dword();
00079     cout << "TRACK " << _track << " SHDR(" << size << ") content : " << unknown << endl;
00080 #endif
00081 }
00082 
00083 bool saud_channel::processBuffer() throw(exception) {
00084 #ifdef DEBUG_CHANNEL
00085     cout << "processBuffer(" << _track << ") : _cur_buffer_write_pos " << _cur_buffer_write_pos << ", _cur_buffer_read_pos " << _cur_buffer_read_pos << endl;
00086 #endif
00087     while((_cur_buffer_write_pos - _cur_buffer_read_pos) > 8) {
00088         int available_size = _cur_buffer_write_pos - _cur_buffer_read_pos;
00089         chunck::type type = from_big_endian(*reinterpret_cast<unsigned int *>(_buffer + _cur_buffer_read_pos));
00090         unsigned int size = from_big_endian(*reinterpret_cast<unsigned int *>(_buffer + _cur_buffer_read_pos + 4));
00091 #ifdef DEBUG_CHANNEL
00092         cout << "TRACK " << _track << ", chunck " << chunck::chunck_string(type) << ", " << available_size << "/" << size+8 << endl;
00093 #endif
00094         cont_chunck * c = 0;
00095         switch(type) {
00096             case TYPE_STRK:
00097                 assert(available_size >= (size + 8));
00098                 c = new cont_chunck(reinterpret_cast<char*>(_buffer) + _cur_buffer_read_pos);
00099                 handleStrk(*c);
00100                 break;
00101             case TYPE_SMRK:
00102                 assert(available_size >= (size + 8));
00103                 c = new cont_chunck(reinterpret_cast<char*>(_buffer) + _cur_buffer_read_pos);
00104                 handleSmrk(*c);
00105                 break;
00106             case TYPE_SHDR:
00107                 assert(available_size >= (size + 8));
00108                 c = new cont_chunck(reinterpret_cast<char*>(_buffer) + _cur_buffer_read_pos);
00109                 handleShdr(*c);
00110                 break;
00111             case TYPE_SDAT: // Sound data !!!
00112                 if(_data_start == -1) {
00113                     _rdata_start = _data_start = _cur_buffer_read_pos + 8;
00114                     _rdata_size = _data_size = size; 
00115                 }
00116                 break;
00117             default:
00118                 ostringstream oss;
00119                 oss << "unknown chunck in SAUD track : " << chunck::chunck_string(type) << endl;
00120                 throw(runtime_error(oss.str()));
00121         }
00122         if(c) delete c;
00123         if(available_size >= (size + 8)) {
00124             _cur_buffer_read_pos += size + 8;
00125         } else break;
00126     }
00127     return true;
00128 }
00129 
00130 saud_channel::saud_channel(int track, int freq) throw() : 
00131             _track(track), 
00132             _nbframes(0), 
00133             _buffer(0), 
00134             _buffer_size(0), 
00135             _frequency(freq), 
00136             _cur_buffer_write_pos(0),
00137             _cur_buffer_read_pos(0),
00138             _data_start(-1), 
00139             _mark_reached(false) {
00140 }
00141 
00142 extern bool win32_save_as_wave(const string & fname, char * data, int size, int freq);
00143 
00144 saud_channel::~saud_channel() throw() {
00145 #ifdef DEBUG_CHANNEL
00146     cout << "~saud_channel(" << _track << ") size == " << _buffer_size << ", _cur_buffer_write_pos == " << _cur_buffer_write_pos << endl;
00147 #endif
00148 #if 0
00149     ostringstream oss;
00150     oss << "track" << _track << "_" << _flags << "_" << _volume << ".wav";
00151     win32_save_as_wave(oss.str(), reinterpret_cast<char *>(_buffer) + _rdata_start, _rdata_size, _frequency);
00152 #endif
00153     if(_buffer) { 
00154         delete []_buffer; 
00155     } 
00156 }
00157 
00158 bool saud_channel::is_terminated() const throw() {
00159 #ifdef DEBUG_CHANNEL
00160     cout << "saud_channel::is_terminated() : " << _cur_buffer_read_pos << ", " << _buffer_size << ", " << _data_start << ", " << _data_size << endl;
00161 #endif
00162     if(_cur_buffer_read_pos < _buffer_size) return false; // Frame completely processed
00163     if(_data_start != -1 && _data_size == 0) return true;
00164     return false;
00165 }
00166 
00167 void saud_channel::recalcVolumeTable() throw(exception) {
00168     const int MAX_BALANCE = 100;
00169     int volume_left, volume_right;
00170     if(_balance < -MAX_BALANCE || _balance > MAX_BALANCE) {
00171         ostringstream oss;
00172         oss << "balance is out of range ! : " << _balance;
00173         throw runtime_error(oss.str());
00174     }
00175     int left_multiplier = MAX_BALANCE - _balance;
00176     int right_multiplier = MAX_BALANCE + _balance;
00177     volume_left = _volume * left_multiplier / (MAX_BALANCE*2);
00178     volume_right = _volume * right_multiplier / (MAX_BALANCE*2);
00179     if(volume_left < 0) volume_left = 0;
00180     if(volume_left > 128) volume_left = 128;
00181     if(volume_right < 0) volume_right = 0;
00182     if(volume_right > 128) volume_right = 128;
00183     for(int i = 0; i < 256; i++) {
00184         _voltable[0][i] = volume_left * static_cast<signed char>(i);
00185         _voltable[1][i] = volume_right * static_cast<signed char>(i);
00186     }
00187 }
00188 
00189 bool saud_channel::set_parameters(int nb, int flags, int volume, int balance) throw(exception) {
00190     if(_nbframes || _buffer) throw(runtime_error("channel initialized twice"));
00191     _nbframes = nb;
00192     _flags = flags; // bit 7 == IS_VOICE, bit 6 == IS_BACKGROUND_MUSIC, other ??
00193     _volume = volume;
00194     _balance = balance;
00195     _index = 0;
00196     //~ cerr << "channel::set_parameters() == " << _track << " : " << _volume << ", " << _balance << endl;
00197     recalcVolumeTable();
00198     //~ for(int i = 0; i < 256; i++)
00199         //~ _voltable[i] = vol1 * (signed char)i;
00200     return true;
00201 }
00202 
00203 bool saud_channel::check_parameters(int index, int nb, int flags, int volume, int balance) throw(exception) {
00204     if(++_index != index) throw runtime_error("invalid index in saud_channel::check_parameters()");
00205     if(_nbframes != nb) throw runtime_error("invalid duration in saud_channel::check_parameters()");
00206     if(_flags != flags) throw runtime_error("invalid flags in saud_channel::check_parameters()");
00207     if(_volume != volume || _balance != balance) {
00208         // throw runtime_error("invalid volume in saud_channel::check_parameters()");
00209         _volume = volume;
00210         _balance = balance;
00211         //~ cerr << "saud_channel::check_parameters() == " << _track << " : " << _volume << ", " << _balance << endl;
00212         recalcVolumeTable();
00213     }
00214     return true;
00215 }
00216 
00217 bool saud_channel::append_data(chunck & b, int size) throw(exception) {
00218     if(!_buffer) { // First call
00219         assert(size > 8);
00220         chunck::type saud_type = from_big_endian(b.get_dword());
00221         unsigned int saud_size = from_big_endian(b.get_dword());
00222         if(saud_type != TYPE_SAUD) throw(runtime_error("Invalid CHUNCK for saud_channel"));
00223         size -= 8;
00224         _buffer_size = saud_size;
00225         assert(_buffer_size);
00226         _buffer = new unsigned char[_buffer_size];
00227         if(!_buffer)  throw(runtime_error("saud_channel failed to allocate memory"));
00228         b.read(_buffer, size);
00229         _cur_buffer_write_pos = size;
00230     } else {
00231         assert(size <= (_buffer_size - _cur_buffer_write_pos));
00232         b.read(_buffer + _cur_buffer_write_pos, size);
00233         _cur_buffer_write_pos += size;
00234     }
00235     return processBuffer();
00236 }
00237 
00238 void saud_channel::read_sound_data(short * snd, int size) throw(std::exception) {
00239     if(_data_start == -1 || _data_size == 0) throw(runtime_error("invalid call to saud_channel::read_sound_data()"));
00240     
00241 #ifdef DEBUG_CHANNEL
00242     int available =  min(_cur_buffer_write_pos - _data_start, _data_size);
00243     cout << "saud_channel::read_sound_data(" << _track << ") size == " << size << ", available == " << available << endl;
00244     assert(available >= size);
00245 #endif
00246     for(int i = 0; i < size; i++) {
00247         snd[2*i] += _voltable[0][_buffer[_data_start+i] ^ 0x80];
00248         snd[2*i+1] += _voltable[1][_buffer[_data_start+i] ^ 0x80];
00249     }
00250     _data_start += size;
00251     _data_size   -= size;
00252 }
00253 
00254 int saud_channel::available_sound_data(void) const throw() {
00255     return min(_cur_buffer_write_pos - _data_start, _data_size); 
00256 }
00257 
00258 imuse_channel::imuse_channel(int track, int freq) throw() : 
00259             _track(track), 
00260             _buffer(0), 
00261             _buffer_size(0), 
00262             _frequency(freq), 
00263             _cur_buffer_write_pos(0),
00264             _cur_buffer_read_pos(0),
00265             _data_start(-1){
00266     _decoded_buffer = new short[_frequency*2];
00267     assert(_decoded_buffer);
00268     _resampled_buffer = new short[_frequency*2];
00269     assert(_resampled_buffer);
00270 }
00271 
00272 imuse_channel::~imuse_channel() throw() {
00273 #ifdef DEBUG_CHANNEL
00274     cout << "~imuse_channel(" << _track << ") size == " << _buffer_size << ", _cur_buffer_write_pos == " << _cur_buffer_write_pos << endl;
00275 #endif
00276 #if 0
00277     ofstream o;
00278     ostringstream oss;
00279     oss << "track" << _track << "_" << _unk1 << "_" << _unk2 << ".dat";
00280     o.open(oss.str().c_str(), ios::out|ios::binary);
00281     o.write(reinterpret_cast<char*>(_buffer), _buffer_size);
00282     o.close();
00283 #endif
00284     if(_buffer) { 
00285         delete []_buffer; 
00286     }
00287     delete []_resampled_buffer;
00288     delete []_decoded_buffer;
00289 }
00290 
00291 bool imuse_channel::is_terminated() const throw()
00292 {
00293     //~ if(_size == 0) return true;
00294     //~ cerr << "imuse_channel::is_terminated(" << _track << ") _cur_buffer_read_pos == " << _cur_buffer_read_pos << ", _buffer_size == " << _buffer_size << ", _data_size == " << _data_size << endl;
00295     if(_cur_buffer_read_pos < (_buffer_size - 8)) return false; // Frame completely processed
00296     if(_data_start != -1 && available_sound_data() == 0) return true;
00297     return false;
00298 }
00299 
00300 bool imuse_channel::set_parameters(int nbframes, int size, int unk1, int unk2) throw(std::exception)
00301 {
00302 #ifdef DEBUG_CHANNEL
00303     cout << "imuse_channel::set_parameters(" << _track << ", " << nbframes<< ", " << size<< ", " << unk1<< ", " << unk2 << ")" << endl;
00304 #endif  
00305     _nbframes = nbframes;
00306     _size = size;
00307     _unk1 = unk1;
00308     _unk2 = unk2;
00309     _index = 0;
00310     return true;
00311 }
00312 
00313 bool imuse_channel::check_parameters(int index, int nbframes, int size, int unk1, int unk2) throw(std::exception)
00314 {
00315 #ifdef DEBUG_CHANNEL
00316     cout << "imuse_channel::check_parameters(" << 
00317         index << ", " << 
00318         nbframes << ", " << 
00319         size << ", " << 
00320         unk1 << ", " << 
00321         unk2 << ") == " << 
00322         _track << ", " << 
00323         _index+1 << ", " << 
00324         _nbframes << ", " << 
00325         _size << ", " << 
00326         _unk1 << ", " << 
00327         _unk2 << endl;
00328     if(++_index != index) throw runtime_error("invalid index in imuse_channel::check_parameters()");
00329     if(_nbframes != nbframes) throw runtime_error("invalid nbframes in imuse_channel::check_parameters()");
00330     if(_size != size) throw runtime_error("invalid size in imuse_channel::check_parameters()");
00331     if(_unk1 != unk1) throw runtime_error("invalid unk1 in imuse_channel::check_parameters()");
00332     if(_unk2 != unk2) throw runtime_error("invalid unk2 in imuse_channel::check_parameters()");
00333     //~ if(_unk3 != unk3) throw runtime_error("invalid unk3 in imuse_channel::check_parameters()");
00334 #endif  
00335     return true;
00336 }
00337 
00338 bool imuse_channel::append_data(chunck & b, int size) throw(std::exception)
00339 {
00340 #ifdef DEBUG_CHANNEL
00341     cout << "imuse_channel::append_data(" << _track << ") : " << size << " < " << _buffer_size<< endl;
00342 #endif
00343     _size -= size;
00344     if(!_buffer) { // First call
00345         assert(size > 8);
00346         chunck::type imus_type = from_big_endian(b.get_dword());
00347         unsigned int imus_size = from_big_endian(b.get_dword());
00348         if(imus_type != TYPE_iMUS) throw runtime_error("Invalid CHUNCK for imuse_channel");
00349         size -= 8;
00350         _buffer_size = imus_size;
00351         assert(_buffer_size);
00352         _buffer = new unsigned char[_buffer_size];
00353         if(!_buffer)  throw runtime_error("imuse_channel failed to allocate memory");
00354         b.read(_buffer, size);
00355         _cur_buffer_write_pos = size;
00356     } else {
00357 #ifdef DEBUG_CHANNEL
00358         if(size > (_buffer_size - _cur_buffer_write_pos))
00359             cerr << "imuse_channel::append_data(" << _track << ") : " << size << " > " << _buffer_size - _cur_buffer_write_pos << endl;
00360 #endif
00361             //~ size = _buffer_size - _cur_buffer_write_pos;
00362         assert(size <= (_buffer_size - _cur_buffer_write_pos));
00363         
00364         b.read(_buffer + _cur_buffer_write_pos, size);
00365         _cur_buffer_write_pos += size;
00366     }
00367     return processBuffer();
00368 }
00369 
00370 bool imuse_channel::handleFormat(chunck & src) {
00371     if(src.get_size() != 20) throw runtime_error("invalid size for FRMT chunck");
00372     _imuse_start = from_big_endian(src.get_dword());
00373 #ifdef DEBUG_CHANNEL
00374     int unknown = from_big_endian(src.get_dword());
00375 #else
00376     src.seek(4);
00377 #endif
00378     _bitsize = from_big_endian(src.get_dword());
00379     _rate = from_big_endian(src.get_dword());
00380     _channels = from_big_endian(src.get_dword());
00381     assert(_channels == 1 || _channels == 2);
00382 #ifdef DEBUG_CHANNEL
00383     if(unknown != 0) cerr << "imuse_channel::handleFormat() == " << unknown << ", " << _imuse_start << ", " << _bitsize << ", " << _rate << ", " << _channels << endl;
00384 #endif
00385     return true;
00386 }
00387 
00388 bool imuse_channel::handleText(chunck & src) {
00389     //~ if(src.get_size() != 20) throw runtime_error("invalid size for FRMT chunck");
00390 #ifdef DEBUG_CHANNEL
00391     int start = from_big_endian(src.get_dword());
00392     assert(start == _imuse_start);
00393     int text_size = src.get_size() - 4;
00394     char * buf = new char[text_size];
00395     src.read(buf, text_size);
00396     string text(buf, text_size);
00397     delete []buf;
00398     assert(text.size() == text_size);
00399 #endif
00400     //~ cerr << "imuse_channel::handleText() == " << text << endl;
00401     return true;
00402 }
00403 
00404 bool imuse_channel::handleRegion(chunck & src) {
00405     if(src.get_size() != 8) throw runtime_error("invalid size for REGN chunck");
00406 #ifdef DEBUG_CHANNEL
00407     int start = from_big_endian(src.get_dword());
00408     assert(start == _imuse_start);
00409 #else
00410     src.seek(4);
00411 #endif
00412     _imuse_length = from_big_endian(src.get_dword());
00413     //~ assert(_imuse_length == _data_size);
00414     return true;
00415 }
00416 
00417 bool imuse_channel::handleStop(chunck & src) {
00418     if(src.get_size() != 4) throw runtime_error("invalid size for STOP chunck");
00419 #ifdef DEBUG_CHANNEL
00420     int _imuse_stop = from_big_endian(src.get_dword());
00421     assert(_imuse_stop == _imuse_start + _imuse_length);
00422 #endif
00423     return true;
00424 }
00425 
00426 bool imuse_channel::handleMap(chunck & map) {
00427     while(!map.eof()) {
00428         chunck * sub = map.sub_block();
00429         //~ if(sub.get_size() & 1) map.seek(1);
00430         switch(sub->get_type()) {
00431             case TYPE_FRMT:
00432                 handleFormat(*sub);
00433                 break;
00434             case TYPE_TEXT:
00435                 handleText(*sub);
00436                 break;
00437             case TYPE_REGN:
00438                 handleRegion(*sub);
00439                 break;
00440             case TYPE_STOP:
00441                 handleStop(*sub);
00442                 break;
00443             default:
00444                 ostringstream oss;
00445                 oss << "Unknown iMUS subchunck found : " << chunck::chunck_string(sub->get_type()) << ", " << sub->get_size();
00446                 throw runtime_error(oss.str());
00447         }
00448         delete sub;
00449     }
00450     return true;
00451 }
00452 
00453 int imuse_channel::resample_mono(int size) throw(std::exception)
00454 {
00455     //~ cout << "imuse_channel::resample() _rate == " << _rate << ", _frequency == " << _frequency << endl;
00456     if(_rate > _frequency) throw runtime_error("Unsupported : imuse rate > output frequency");
00457     int multiplier = _frequency / _rate;
00458     //~ cout << "multiplier == " << multiplier << endl;
00459     if((_rate * multiplier) != _frequency) throw runtime_error("Unsupported : imuse rate is not a multiple of output frequency");
00460     multiplier *= 2;
00461     assert(size * multiplier < _frequency*2); // This is because of the _resampled_buffer size
00462     short * decoded = _decoded_buffer;
00463     short * resampled = _resampled_buffer;
00464     if(multiplier == 2) {
00465         for(int i = 0; i < size; i++) {
00466             *resampled++ = *decoded;
00467             *resampled++ = *decoded++;
00468         }
00469         return size * 2;
00470     } else if(multiplier == 4) {
00471         for(int i = 0; i < size; i++) {
00472             *resampled++ = *decoded;
00473             *resampled++ = *decoded;
00474             *resampled++ = *decoded;
00475             *resampled++ = *decoded++;
00476         }
00477         return size * 4;
00478     } else {
00479 #ifdef DEBUG_CHANNEL
00480         cerr << "imuse_channel::resample() multiplier == " << multiplier << endl;
00481 #endif      
00482         for(int i = 0; i < size; i++) {
00483             for(int x = 0; x < multiplier; x++) {
00484                 *resampled++ = *decoded;
00485             }
00486             decoded++;
00487         }
00488         return resampled - _resampled_buffer;
00489     }
00490 }
00491 
00492 int imuse_channel::resample_stereo(int size) throw(std::exception)
00493 {
00494     assert((size & 1) == 0);
00495     //~ cout << "imuse_channel::resample() _rate == " << _rate << ", _frequency == " << _frequency << endl;
00496     if(_rate > _frequency) throw runtime_error("Unsupported : imuse rate > output frequency");
00497     int multiplier = _frequency / _rate;
00498     //~ cout << "multiplier == " << multiplier << endl;
00499     if((_rate * multiplier) != _frequency) throw runtime_error("Unsupported : imuse rate is not a multiple of output frequency");
00500     if(_channels == 1) multiplier *= 2;
00501     assert(size * multiplier < _frequency*2); // This is because of the _resampled_buffer size
00502     short * decoded = _decoded_buffer;
00503     short * resampled = _resampled_buffer;
00504     if(multiplier == 1) {
00505         memcpy(resampled, decoded, size * sizeof(short));
00506         return size;
00507     } else if(multiplier == 2) {
00508         size /= 2;
00509         for(int i = 0; i < size; i++) {
00510             short v1 = *decoded++;
00511             short v2 = *decoded++;
00512             *resampled++ = v1;
00513             *resampled++ = v2;
00514             *resampled++ = v1;
00515             *resampled++ = v2;
00516         }
00517         return size * 4;
00518     } else {
00519 #ifdef DEBUG_CHANNEL
00520         cerr << "imuse_channel::resample() multiplier == " << multiplier << endl;
00521 #endif      
00522         size /= 2;
00523         for(int i = 0; i < size; i++) {
00524             short v1 = *decoded++;
00525             short v2 = *decoded++;
00526             for(int x = 0; x < multiplier; x++) {
00527                 *resampled++ = v1;
00528                 *resampled++ = v2;
00529             }
00530         }
00531         return resampled - _resampled_buffer;
00532     }
00533 }
00534 
00535 int imuse_channel::decode(int size, int &ret) throw(std::exception)
00536 {
00537     short * decoded = _decoded_buffer;
00538     unsigned char * source = _buffer + _data_start;
00539     //~ cerr << "imuse_channel::decode() bitsize == " << _bitsize << endl;
00540     switch(_bitsize) {
00541         case 8:
00542             while(size--) {
00543                 *decoded++ = (static_cast<short>(*source++) - 128) << 8;
00544             } break;
00545         case 12:
00546             //~ assert((size & 1) == 0);
00547             {
00548                 int msize = size & 1;
00549                 size = (size+1) >> 1;
00550                 while(size--) {
00551                     int v1 =  *source++;
00552                     int v2 =  *source++;
00553                     int v3 =  *source++;
00554                     int value = (((v2 & 0x0f) << 12) | (v1 << 4)) - 0x8000;
00555                     *decoded++ = static_cast<short>(value);
00556                     value = (((v2 & 0xf0) << 8) | (v3 << 4)) - 0x8000;      
00557                     *decoded++ = static_cast<short>(value);
00558                 } 
00559                 if(msize) {
00560                     decoded--;
00561 #ifdef DEBUG_CHANNEL
00562                     cerr << "imuse_channel::decode() had to skip a sample..." << endl;
00563 #endif
00564                 }
00565             } break;
00566         case 16:
00567             while(size--) {
00568                 int value = ((*source++ << 8) +  *source++) - 0x8000;
00569                 *decoded++ = static_cast<short>(value);
00570             } break;
00571         default:
00572             throw runtime_error("Unsupported bit size in imuse chunck");
00573     }
00574     ret = source - (_buffer + _data_start);
00575     return decoded - _decoded_buffer;
00576 }
00577 
00578 bool imuse_channel::processBuffer() throw(exception) {
00579 #ifdef DEBUG_CHANNEL
00580     cout << "imuse_channel::processBuffer(" << _track << ") : _cur_buffer_write_pos " << _cur_buffer_write_pos << ", _cur_buffer_read_pos " << _cur_buffer_read_pos << endl;
00581 #endif
00582     while((_cur_buffer_write_pos - _cur_buffer_read_pos) >= 8) {
00583         int available_size = _cur_buffer_write_pos - _cur_buffer_read_pos;
00584         chunck::type type = from_big_endian(*reinterpret_cast<unsigned int *>(_buffer + _cur_buffer_read_pos));
00585         unsigned int size = from_big_endian(*reinterpret_cast<unsigned int *>(_buffer + _cur_buffer_read_pos + 4));
00586 #ifdef DEBUG_CHANNEL
00587         cout << "TRACK " << _track << ", chunck " << chunck::chunck_string(type) << ", " << available_size << "/" << size+8 << endl;
00588 #endif
00589         switch(type) {
00590             case TYPE_MAP_: 
00591                 {
00592                     assert(available_size >= (size + 8));
00593                     cont_chunck map_chunck(reinterpret_cast<char*>(_buffer) + _cur_buffer_read_pos);
00594                     handleMap(map_chunck);
00595                 } break;
00596             case TYPE_DATA: // Sound data !!!
00597                 if(_data_start == -1) {
00598                     _rdata_start = _data_start = _cur_buffer_read_pos + 8;
00599                     _rdata_size = _data_size = size;
00600                     int reqsize = 1;
00601                     if(_channels == 2) reqsize *= 2;
00602                     if(_bitsize == 16) reqsize *= 2;
00603                     else if(_bitsize == 12) reqsize *= 3;
00604                     if((size % reqsize) != 0)
00605                         throw runtime_error("Invalid sound data size");
00606                 }
00607                 break;
00608             case TYPE_iMUS:
00609                 size = 0;
00610                 break;
00611             default:
00612                 {
00613                     ostringstream oss;
00614                     oss << "unknown chunck in iMUS track : " << chunck::chunck_string(type) << endl;
00615                     throw runtime_error(oss.str());
00616                 } break;
00617         }
00618         //~ if(_track == 65869) cerr << "imuse_channel::processBuffer(" << _track << ") available_size == " << available_size << ", size == " << size << endl;
00619         if(available_size >= (size + 8)) {
00620             _cur_buffer_read_pos += size + 8;
00621         } else {
00622             break;
00623         }
00624     }
00625     return true;
00626 }
00627 
00628 int imuse_channel::available_sound_data(void) const throw() {
00629     assert(_data_start <= _cur_buffer_write_pos);
00630     int size = min(_cur_buffer_write_pos - _data_start, _data_size);
00631     
00632     //~ cerr << "imuse_channel::available_sound_data() : size in byte == " << size << endl;
00633     switch(_bitsize) {
00634         case 12:
00635             size = (size / 3) * 2;
00636             break;
00637         case 16:
00638             size /= 2;
00639     }
00640     if(_channels == 2)
00641         size /= 2;
00642     if(_bitsize == 12) size &= ~1;
00643     //~ cerr << "imuse_channel::available_sound_data() : size in samples == " << size << endl;
00644     return size;
00645 }
00646 
00647 void imuse_channel::read_sound_data(short * snd, int size) throw(std::exception) {
00648     if(_data_start == -1 || _data_size == 0) throw(runtime_error("invalid call to saud_channel::read_sound_data()"));
00649     
00650 #ifdef DEBUG_CHANNEL
00651     int available =  min(_cur_buffer_write_pos - _data_start, _data_size) * 2;
00652     switch(_bitsize) {
00653         case 12:
00654             available = (available / 3) * 2;
00655             break;
00656         case 16:
00657             available /= 2;
00658     }
00659     cout << "saud_channel::read_sound_data(" << _track << ") size == " << size << ", available == " << available << endl;
00660     assert(available >= size);
00661 #endif
00662     //~ if(_channels == 2) size *= 2; // We need to decode twice the amount of data
00663     //~ cerr << "imuse::write() rate == " << _rate << ", bitsize == " << _bitsize << ", channels == " << _channels << endl;
00664     int resampled_size, really_decoded = 0;
00665     int decoded_size = decode((_channels == 2) ? size*2 : size, really_decoded);
00666     _data_start += really_decoded;
00667     _data_size -= really_decoded;
00668     if(_channels == 2)
00669         resampled_size = resample_stereo(decoded_size); 
00670     else
00671         resampled_size = resample_mono(decoded_size);
00672 #ifdef DEBUG_CHANNEL
00673     cerr << "imuse::write() size * 2 == " << size * 2 << ", resampled_size == " << resampled_size << endl;
00674 #endif
00675     //~ if(_bitsize != 12 || _channels != 1)
00676     for(int i = 0; i < size * 2; i++)
00677         snd[i] += _resampled_buffer[i];
00678 }
00679 
00680 
00681 

Generated on Fri Aug 9 22:54:29 2002 for san_player by doxygen1.2.16