diff options
Diffstat (limited to 'modules/chibi/cp_loader_it_samples.cpp')
-rw-r--r-- | modules/chibi/cp_loader_it_samples.cpp | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/modules/chibi/cp_loader_it_samples.cpp b/modules/chibi/cp_loader_it_samples.cpp new file mode 100644 index 0000000000..ced7252a6c --- /dev/null +++ b/modules/chibi/cp_loader_it_samples.cpp @@ -0,0 +1,620 @@ +/*************************************************************************/ +/* cp_loader_it_samples.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "cp_loader_it.h" +#include "cp_sample.h" + +struct AuxSampleData { + + + uint32_t fileofs; + uint32_t c5spd; + uint32_t length; + uint32_t loop_begin; + uint32_t loop_end; + bool loop_enabled; + bool pingpong_enabled; + bool is16bit; + bool stereo; + bool exists; + bool compressed; + +}; + + +enum IT_Sample_Flags { + + IT_SAMPLE_EXISTS=1, + IT_SAMPLE_16BITS=2, + IT_SAMPLE_STEREO=4, + IT_SAMPLE_COMPRESSED=8, + IT_SAMPLE_LOOPED=16, + IT_SAMPLE_SUSTAIN_LOOPED=32, + IT_SAMPLE_LOOP_IS_PINGPONG=64, + IT_SAMPLE_SUSTAIN_LOOP_IS_PINGPONG=128 +}; + + +CPLoader::Error CPLoader_IT::load_sample(CPSample *p_sample) { + + + AuxSampleData aux_sample_data; + + char aux_header[4]; + + file->get_byte_array((uint8_t*)aux_header,4); + + if ( aux_header[0]!='I' || + aux_header[1]!='M' || + aux_header[2]!='P' || + aux_header[3]!='S') { + + //CP_PRINTERR("IT CPLoader CPSample: Failed Identifier"); + return FILE_UNRECOGNIZED; + } + + + // Ignore deprecated 8.3 filename + for (int i=0;i<12;i++) file->get_byte(); + + file->get_byte(); //ignore zerobyte + + p_sample->set_global_volume( file->get_byte() ); + + /* SAMPLE FLAGS */ + uint8_t flags=file->get_byte(); + aux_sample_data.loop_enabled=flags&IT_SAMPLE_LOOPED; + aux_sample_data.pingpong_enabled=flags&IT_SAMPLE_LOOP_IS_PINGPONG; + aux_sample_data.is16bit=flags&IT_SAMPLE_16BITS; + aux_sample_data.exists=flags&IT_SAMPLE_EXISTS; + aux_sample_data.stereo=flags&IT_SAMPLE_STEREO; + aux_sample_data.compressed=flags&IT_SAMPLE_COMPRESSED; + + p_sample->set_default_volume(file->get_byte()); + /* SAMPLE NAME */ + char aux_name[26]; + file->get_byte_array((uint8_t*)aux_name,26); + p_sample->set_name(aux_name); + + // ?? + uint8_t convert_flag=file->get_byte(); + // PAN + uint8_t pan=file->get_byte(); + p_sample->set_pan( pan&0x7F ); + p_sample->set_pan_enabled( pan & 0x80 ); + + aux_sample_data.length=file->get_dword(); + + + aux_sample_data.loop_begin= file->get_dword(); + aux_sample_data.loop_end= file->get_dword(); + aux_sample_data.c5spd=file->get_dword(); + /*p_sample->data.set_sustain_loop_begin=*/file->get_dword(); + /*p_sample->data.sustain_loop_end=*/file->get_dword(); + aux_sample_data.fileofs=file->get_dword(); + p_sample->set_vibrato_speed( file->get_byte() ); + p_sample->set_vibrato_depth( file->get_byte() ); + p_sample->set_vibrato_rate( file->get_byte() ); + switch( file->get_byte() ) { + /* Vibrato Wave: 0=sine, 1=rampdown, 2=square, 3=random */ + case 0: p_sample->set_vibrato_type( CPSample::VIBRATO_SINE ); break; + case 1: p_sample->set_vibrato_type( CPSample::VIBRATO_SAW ); break; + case 2: p_sample->set_vibrato_type( CPSample::VIBRATO_SQUARE ); break; + case 3: p_sample->set_vibrato_type( CPSample::VIBRATO_RANDOM ); break; + default: p_sample->set_vibrato_type( CPSample::VIBRATO_SINE ); break; + } + + //printf("Name %s - Flags: fileofs :%i - c5spd %i - len %i 16b %i - data?: %i\n",p_sample->get_name(),aux_sample_data.fileofs,aux_sample_data.c5spd, aux_sample_data.length, aux_sample_data.is16bit,aux_sample_data.exists); + CPSample_ID samp_id; + + if (aux_sample_data.exists) { + samp_id=load_sample_data(aux_sample_data); + CPSampleManager::get_singleton()->set_c5_freq(samp_id,aux_sample_data.c5spd); + CPSampleManager::get_singleton()->set_loop_begin( samp_id,aux_sample_data.loop_begin ); + CPSampleManager::get_singleton()->set_loop_end( samp_id,aux_sample_data.loop_end ); + CPSample_Loop_Type loop_type=aux_sample_data.loop_enabled?( aux_sample_data.pingpong_enabled? CP_LOOP_BIDI: CP_LOOP_FORWARD):CP_LOOP_NONE; + CPSampleManager::get_singleton()->set_loop_end( samp_id,aux_sample_data.loop_end ); + CPSampleManager::get_singleton()->set_loop_type( samp_id, loop_type); + + } + + //printf("Loaded id is null?: %i\n",samp_id.is_null()); + p_sample->set_sample_data(samp_id); + if (!samp_id.is_null()) { + + // printf("Loaded ID: stereo: %i len %i 16bit %i\n",CPSampleManager::get_singleton()->is_stereo(samp_id), CPSampleManager::get_singleton()->get_size( samp_id), CPSampleManager::get_singleton()->is_16bits( samp_id) ); + } + + CP_ERR_COND_V( file->eof_reached(),FILE_CORRUPTED ); + CP_ERR_COND_V( file->get_error(),FILE_CORRUPTED ); + + return FILE_OK; + +} + +CPSample_ID CPLoader_IT::load_sample_data(AuxSampleData& p_sample_data) { + + + int aux_sample_properties = (p_sample_data.is16bit?IT_SAMPLE_16BITS:0)|(p_sample_data.compressed?IT_SAMPLE_COMPRESSED:0)|(p_sample_data.stereo?IT_SAMPLE_STEREO:0); + + file->seek(p_sample_data.fileofs); + + CPSampleManager *sm=CPSampleManager::get_singleton(); + + CPSample_ID id; + + switch (aux_sample_properties) { + + case (0): // 8 bits, mono + case (IT_SAMPLE_16BITS): // 16 bits mono + case (IT_SAMPLE_STEREO): // 8 bits stereo + case (IT_SAMPLE_16BITS|IT_SAMPLE_STEREO): { // 16 bits mono + + id=sm->create(p_sample_data.is16bit,p_sample_data.stereo,p_sample_data.length); + if (id.is_null()) + break; + + sm->lock_data(id); + + int16_t *ptr16 = (int16_t*)sm->get_data(id); + int8_t *ptr8=(int8_t*)ptr16; + + int chans=p_sample_data.stereo?2:1; + + if (p_sample_data.is16bit) { + + for (int c=0;c<chans;c++) { + + for (int i=0;i<p_sample_data.length;i++) { + + ptr16[i*chans+c]=file->get_word(); + } + } + } else { + + for (int c=0;c<chans;c++) { + + for (int i=0;i<p_sample_data.length;i++) { + + ptr8[i*chans+c]=file->get_byte(); + } + } + + } + + sm->unlock_data(id); + + } break; + case (IT_SAMPLE_COMPRESSED): { // 8 bits compressed + + + id=sm->create(false,false,p_sample_data.length); + if (id.is_null()) + break; + sm->lock_data(id); + + if ( load_sample_8bits_IT_compressed((void*)sm->get_data( id),p_sample_data.length) ) { + + sm->unlock_data(id); + sm->destroy(id); + + break; + } + + sm->unlock_data(id); + + + } break; + case (IT_SAMPLE_16BITS|IT_SAMPLE_COMPRESSED): { // 16 bits compressed + + + id=sm->create(true,false,p_sample_data.length); + if (id.is_null()) + break; + sm->lock_data(id); + + if ( load_sample_16bits_IT_compressed((void*)sm->get_data(id),p_sample_data.length) ) { + + sm->unlock_data(id); + sm->destroy(id); + break; + } + + sm->unlock_data(id); + + } break; + default: { + + // I dont know how to handle stereo compressed, does that exist? + } break; + + } + + + return id; +} + + +CPLoader::Error CPLoader_IT::load_samples() { + + for (int i=0;i<header.smpnum;i++) { + + //seek to sample + file->seek(0xC0+header.ordnum+header.insnum*4+i*4); + + uint32_t final_location=file->get_dword(); + file->seek( final_location ); + + + Error err=load_sample(song->get_sample(i)); + CP_ERR_COND_V(err,err); + + } + + if (file->eof_reached() || file->get_error()) + return FILE_CORRUPTED; + + return FILE_OK; +} +/* * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE + + -The following sample decompression code is based on xmp's code.(http://xmp.helllabs.org) which is based in openCP code. + +* NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE */ + +uint32_t CPLoader_IT::read_n_bits_from_IT_compressed_block (uint8_t p_bits_to_read) { + + uint32_t aux_return_value; + uint32_t val; + + uint8_t *buffer=(uint8_t*)source_position; + if ( p_bits_to_read <= source_remaining_bits ) { + + val=buffer[3]; + val<<=8; + val|=buffer[2]; + val<<=8; + val|=buffer[1]; + val<<=8; + val|=buffer[0]; + + aux_return_value = val & ((1 << p_bits_to_read) - 1); + val >>= p_bits_to_read; + source_remaining_bits -= p_bits_to_read; + + buffer[3]=val>>24; + buffer[2]=(val>>16)&0xFF; + buffer[1]=(val>>8)&0xFF; + buffer[0]=(val)&0xFF; + + } else { + aux_return_value=buffer[3]; + aux_return_value<<=8; + aux_return_value|=buffer[2]; + aux_return_value<<=8; + aux_return_value|=buffer[1]; + aux_return_value<<=8; + aux_return_value|=buffer[0]; + + uint32_t nbits = p_bits_to_read - source_remaining_bits; + source_position++; + + buffer+=4; + val=buffer[3]; + val<<=8; + val|=buffer[2]; + val<<=8; + val|=buffer[1]; + val<<=8; + val|=buffer[0]; + aux_return_value |= ((val & ((1 << nbits) - 1)) << source_remaining_bits); + val >>= nbits; + source_remaining_bits = 32 - nbits; + buffer[3]=val>>24; + buffer[2]=(val>>16)&0xFF; + buffer[1]=(val>>8)&0xFF; + buffer[0]=(val)&0xFF; + + } + + return aux_return_value; +} + +bool CPLoader_IT::read_IT_compressed_block (bool p_16bits) { + + uint16_t size; + + size=file->get_word(); + + if (file->eof_reached() || file->get_error()) return true; + + pat_data = (uint8_t*)CP_ALLOC( 4* ((size >> 2) + 2) ); + if (!pat_data) + return true; + + + source_buffer=(uint32_t*)pat_data; + file->get_byte_array((uint8_t*)source_buffer,size); + + if (file->eof_reached() || file->get_error()) { + + free_IT_compressed_block(); + return true; + } + + source_position = source_buffer; + source_remaining_bits = 32; + + return false; +} + +void CPLoader_IT::free_IT_compressed_block () { + + + if (pat_data) { + CP_FREE(pat_data); + pat_data=NULL; + } + +} + +bool CPLoader_IT::load_sample_8bits_IT_compressed(void *p_dest_buffer,int p_buffsize) { + + int8_t *dest_buffer; /* destination buffer which will be returned */ + uint16_t block_length; /* length of compressed data block in samples */ + uint16_t block_position; /* position in block */ + uint8_t bit_width; /* actual "bit width" */ + uint16_t aux_value; /* value read from file to be processed */ + int8_t d1, d2; /* integrator buffers (d2 for it2.15) */ + int8_t *dest_position; /* position in output buffer */ + int8_t v; /* sample value */ + bool it215; // is this an it215 module? + + dest_buffer = (int8_t *) p_dest_buffer; + + if (dest_buffer==NULL) + return true; + + for (int i=0;i<p_buffsize;i++) + dest_buffer[i]=0; + + + dest_position = dest_buffer; + + it215=(header.cmwt==0x215); + + /* now unpack data till the dest buffer is full */ + + while (p_buffsize) { + /* read a new block of compressed data and reset variables */ + if ( read_IT_compressed_block(false) ) { + CP_PRINTERR("Out of memory decompressing IT CPSample"); + return true; + } + + + block_length = (p_buffsize < 0x8000) ? p_buffsize : 0x8000; + + block_position = 0; + + bit_width = 9; /* start with width of 9 bits */ + + d1 = d2 = 0; /* reset integrator buffers */ + + /* now uncompress the data block */ + while ( block_position < block_length ) { + + aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */ + + if ( bit_width < 7 ) { /* method 1 (1-6 bits) */ + + if ( aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */ + + aux_value = read_n_bits_from_IT_compressed_block(3) + 1; /* yes -> read new width; */ + bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; + /* and expand it */ + continue; /* ... next value */ + } + + } else if ( bit_width < 9 ) { /* method 2 (7-8 bits) */ + + uint8_t border = (0xFF >> (9 - bit_width)) - 4; + /* lower border for width chg */ + + if ( aux_value > border && aux_value <= (border + 8) ) { + + aux_value -= border; /* convert width to 1-8 */ + bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; + /* and expand it */ + continue; /* ... next value */ + } + + + } else if ( bit_width == 9 ) { /* method 3 (9 bits) */ + + if ( aux_value & 0x100 ) { /* bit 8 set? */ + + bit_width = (aux_value + 1) & 0xff; /* new width... */ + continue; /* ... and next value */ + } + + } else { /* illegal width, abort */ + + + free_IT_compressed_block(); + CP_PRINTERR("CPSample has illegal BitWidth "); + return true; + } + + /* now expand value to signed byte */ + if ( bit_width < 8 ) { + + uint8_t tmp_shift = 8 - bit_width; + + v=(aux_value << tmp_shift); + v>>=tmp_shift; + + } else v = (int8_t) aux_value; + + /* integrate upon the sample values */ + d1 += v; + d2 += d1; + + /* ... and store it into the buffer */ + *(dest_position++) = it215 ? d2 : d1; + block_position++; + + } + + /* now subtract block lenght from total length and go on */ + free_IT_compressed_block(); + p_buffsize -= block_length; + } + + + return false; +} + +bool CPLoader_IT::load_sample_16bits_IT_compressed(void *p_dest_buffer,int p_buffsize) { + + int16_t *dest_buffer; /* destination buffer which will be returned */ + uint16_t block_length; /* length of compressed data block in samples */ + uint16_t block_position; /* position in block */ + uint8_t bit_width; /* actual "bit width" */ + uint32_t aux_value; /* value read from file to be processed */ + int16_t d1, d2; /* integrator buffers (d2 for it2.15) */ + int16_t *dest_position; /* position in output buffer */ + int16_t v; /* sample value */ + + bool it215; // is this an it215 module? + + dest_buffer = (int16_t *) p_dest_buffer; + + if (dest_buffer==NULL) + return true; + + for (int i=0;i<p_buffsize;i++) + dest_buffer[i]=0; + + dest_position = dest_buffer; + + it215=(header.cmwt==0x215); + + + while (p_buffsize) { + /* read a new block of compressed data and reset variables */ + if ( read_IT_compressed_block(true) ) { + + return true; + } + + + block_length = (p_buffsize < 0x4000) ? p_buffsize : 0x4000; + + block_position = 0; + + bit_width = 17; /* start with width of 9 bits */ + + d1 = d2 = 0; /* reset integrator buffers */ + + while ( block_position < block_length ) { + + aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */ + + if ( bit_width < 7 ) { /* method 1 (1-6 bits) */ + + if ( (signed)aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */ + + aux_value = read_n_bits_from_IT_compressed_block(4) + 1; /* yes -> read new width; */ + bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; + /* and expand it */ + continue; /* ... next value */ + } + + } else if ( bit_width < 17 ) { + + uint16_t border = (0xFFFF >> (17 - bit_width)) - 8; + + if ( (int)aux_value > (int)border && (int)aux_value <= ((int)border + 16) ) { + + aux_value -= border; /* convert width to 1-8 */ + bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; + /* and expand it */ + continue; /* ... next value */ + } + + + } else if ( bit_width == 17 ) { + + if ( aux_value & 0x10000 ) { /* bit 8 set? */ + + bit_width = (aux_value + 1) & 0xff; /* new width... */ + continue; /* ... and next value */ + } + + } else { /* illegal width, abort */ + + CP_PRINTERR("CPSample has illegal BitWidth "); + + free_IT_compressed_block(); + + return true; + } + + /* now expand value to signed byte */ + if ( bit_width < 16 ) { + + uint8_t tmp_shift = 16 - bit_width; + + v=(aux_value << tmp_shift); + v>>=tmp_shift; + + } else v = (int16_t) aux_value; + + /* integrate upon the sample values */ + d1 += v; + d2 += d1; + + /* ... and store it into the buffer */ + *(dest_position++) = it215 ? d2 : d1; + block_position++; + + } + + /* now subtract block lenght from total length and go on */ + free_IT_compressed_block(); + p_buffsize -= block_length; + } + + + return false; + +} + + + |