dropped aas, moved to maxmod
[gbajam21] / tools / mmutil / samplefix.c
diff --git a/tools/mmutil/samplefix.c b/tools/mmutil/samplefix.c
new file mode 100644 (file)
index 0000000..1a60935
--- /dev/null
@@ -0,0 +1,532 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#define MAX_UNROLL_THRESHOLD   1024    // will unroll upto 1kb more of data (when fixing NDS samples)
+#define GBA_MIN_LOOP_SIZE 512
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "defs.h"
+#include "mas.h"
+#include "errors.h"
+#include "systems.h"
+#include "adpcm.h"
+
+extern int ignore_sflags;
+
+void Sample_PadStart( Sample* samp, u32 count )
+{
+       // Pad beginning of sample with zero
+       u8* newdata8;
+       u16* newdata16;
+       u32 x;
+       if( count == 0 )
+               return;         // nothing to do
+       if( samp->format & SAMPF_16BIT )
+       {
+               newdata16 = (u16*)malloc( (samp->sample_length+count)*2 );
+               for( x = 0; x < count; x++ )
+                       newdata16[x]=32768;
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata16[count+x]=((u16*)samp->data)[x];
+               free( samp->data );
+               samp->data = (void*)newdata16;
+       }
+       else
+       {
+               newdata8 = (u8*)malloc( (samp->sample_length+count) );
+               for( x = 0; x < count; x++ )
+                       newdata8[x]=128;
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata8[count+x] = ((u8*)samp->data)[x];
+               free( samp->data );
+               samp->data = (void*)newdata8;
+       }
+       samp->loop_start    += count;
+       samp->loop_end      += count;
+       samp->sample_length += count;
+}
+
+void Sample_PadEnd( Sample* samp, u32 count )
+{
+       // Pad end of sample with zero
+       u8* newdata8;
+       u16* newdata16;
+       u32 x;
+       if( count == 0 )
+               return;         // nothing to do
+       if( samp->format & SAMPF_16BIT )
+       {
+               newdata16 = malloc( (samp->sample_length+count)*2 );
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata16[x]= ((u16*)samp->data)[x];
+               for( x = 0; x < count; x++ )
+                       newdata16[samp->sample_length+x]=32768;
+               free( samp->data );
+               samp->data = (void*)newdata16;
+       }
+       else
+       {
+               newdata8 = malloc( (samp->sample_length+count) );
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata8[x]= ((u8*)samp->data)[x];
+               for( x = 0; x < count; x++ )
+                       newdata8[samp->sample_length+x]=128;
+               free( samp->data );
+               samp->data = (void*)newdata8;
+       }
+       samp->loop_end      += count;
+       samp->sample_length += count;
+}
+
+void Unroll_Sample_Loop( Sample* samp, u32 count )
+{
+       // unrolls sample loop (count) times
+       // loop end MUST equal sample length
+       u8* newdata8;
+       u16* newdata16;
+       u32 newlen;
+       u32 looplen;
+       u32 x;
+       looplen = samp->loop_end-samp->loop_start;
+       newlen = samp->sample_length + looplen*count;
+       if( samp->format & SAMPF_16BIT )
+       {
+               newdata16 = (u16*)malloc( newlen *2 );
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata16[x] = ((u16*)samp->data)[x];
+               for( x = 0; x < looplen*count; x++ )
+                       newdata16[samp->sample_length+x] = ((u16*)samp->data)[samp->loop_start+ (x%looplen)];
+               free( samp->data );
+               samp->data = (void*)newdata16;
+       }
+       else
+       {
+               newdata8 = (u8*)malloc( newlen );
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata8[x] = ((u8*)samp->data)[x];
+               for( x = 0; x < looplen*count; x++ )
+                       newdata8[samp->sample_length+x] = ((u8*)samp->data)[samp->loop_start+ (x%looplen)];
+               free( samp->data );
+               samp->data = (void*)newdata8;
+       }
+       samp->loop_end += looplen*count;
+       samp->sample_length += looplen*count;
+}
+
+void Unroll_BIDI_Sample( Sample* samp )
+{
+       // sample length MUST equal sample loop end
+       // sample MUST have loop type 2 (BIDI)
+       u8* newdata8;
+       u16* newdata16;
+       u32 newlen;
+       u32 looplen;
+       u32 x;
+
+       looplen = samp->loop_end-samp->loop_start;
+       newlen = (samp->sample_length + looplen);
+       
+       if( samp->format & SAMPF_16BIT )
+       {
+               newdata16 = malloc( newlen *2 );
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata16[x] = ((u16*)samp->data)[x];
+               for( x = 0; x < looplen; x++ )
+                       newdata16[x+samp->sample_length] = ((u16*)samp->data)[samp->loop_end-1-x];
+               free( samp->data );
+               samp->data = (void*)newdata16;
+       }
+       else
+       {
+               newdata8 = malloc( newlen );
+               for( x = 0; x < samp->sample_length; x++ )
+                       newdata8[x] = ((u8*)samp->data)[x];
+               for( x = 0; x < looplen; x++ )
+                       newdata8[x+samp->sample_length] = ((u8*)samp->data)[samp->loop_end-1-x];
+               free( samp->data );
+               samp->data = (void*)newdata8;
+       }
+       samp->loop_type = 1;
+       samp->sample_length += looplen;
+       samp->loop_end += looplen;
+}
+
+/* NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+  The following resample routine was stolen from CHIBITRACKER (http://chibitracker.berlios.de), thanks reduz!
+ NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE */
+
+void Resample( Sample* samp, u32 newsize )
+{
+       // output pointers
+       u8      *dst8 =0;
+       u16     *dst16=0;
+       u8      *src8 = (u8*)samp->data;
+       u16     *src16 = (u16*)samp->data;
+       
+       int oldlength = samp->sample_length;
+       int lpoint = samp->loop_start;
+       int i;
+       
+       bool bit16 = samp->format & SAMPF_16BIT;
+       double sign_diff;
+       
+       // allocate memory
+       if( bit16 )
+       {
+               dst16 = (u16*)malloc(newsize*2);
+               sign_diff = 32768.0;
+       }
+       else
+       {
+               dst8 = (u8*)malloc(newsize);
+               sign_diff = 128.0;
+       }
+               
+       double tscale = (double)oldlength / (double)newsize;
+       double posf;
+       
+       for( i = 0; i < newsize; i++ )
+       {
+               posf = (double)i * tscale;
+               int posi = (int)floor(posf);
+               
+               double mu = posf - (double)posi;
+               double s0, s1, s2, s3;
+               double mu2, a0, a1, a2, a3, res;
+               
+               // get previous, current, next, and after next samples
+               if( bit16 )
+               {
+                       s0 = (posi-1) < 0 ? 0 :         ((double)(src16[posi-1]));
+                       s1 =                                                    ((double)(src16[posi  ]));
+                       s2 = (posi+1) >= oldlength ? 
+                                                                       (samp->loop_type ? 
+                                                                               ((double)(src16[lpoint + (posi + 1 - oldlength)])) : 0) : 
+                                                                               ((double)(src16[posi+1]));
+                       s3 = (posi+1) >= oldlength ? 
+                                                                       (samp->loop_type ? 
+                                                                               ((double)(src16[lpoint + (posi + 2 - oldlength)])) : 0) : 
+                                                                               ((double)(src16[posi+2]));
+               }
+               else
+               {
+                       s0 = (posi-1) < 0 ? 0 :         ((double)(src8[posi-1]));
+                       s1 =                                                    ((double)(src8[posi  ]));
+                       s2 = (posi+1) >= oldlength ? 
+                                                                       (samp->loop_type ? 
+                                                                               ((double)(src8[lpoint + (posi + 1 - oldlength)])) : 0) : 
+                                                                               ((double)(src8[posi+1]));
+                       s3 = (posi+1) >= oldlength ? 
+                                                                       (samp->loop_type ? 
+                                                                               ((double)(src8[lpoint + (posi + 2 - oldlength)])) : 0) : 
+                                                                               ((double)(src8[posi+2]));
+               }
+               
+               // sign data
+               s0 -= sign_diff;
+               s1 -= sign_diff;
+               s2 -= sign_diff;
+               s3 -= sign_diff;
+               
+               mu2 = mu * mu;
+               a0 = s3 - s2 - s0 + s1;
+               a1 = s0 - s1 - a0;
+               a2 = s2 - s0;
+               a3 = s1;
+               
+               res = a0*mu*mu2 + a1*mu2 + a2*mu + a3;
+               int resi = ((int)floor(res+0.5));
+               
+               if( bit16 )
+               {
+                       if( resi < -32768 ) resi = -32768;
+                       if( resi > 32767 ) resi = 32767;
+                       dst16[i] = resi + 32768;
+               }
+               else
+               {
+                       if( resi < -128 ) resi = -128;
+                       if( resi > 127 ) resi = 127;
+                       dst8[i] = resi + 128;
+               }
+               
+       }
+       
+       free( samp->data );
+       if( bit16 )
+               samp->data = (void*)dst16;
+       else
+               samp->data = (void*)dst8;
+       
+       samp->sample_length = newsize;
+       samp->loop_end = newsize;
+       samp->loop_start = (int)(((double)samp->loop_start * (double)newsize+((double)oldlength/2))/(double)oldlength);
+       samp->frequency = (int)(((double)samp->frequency * (double)newsize+((double)oldlength/2))/(double)oldlength);
+}
+
+void Sample_8bit( Sample* samp )
+{
+       if( samp->format & SAMPF_16BIT )
+       {
+               u8* newdata;
+               u32 t;
+               newdata = (u8*)malloc( samp->sample_length );
+               for( t = 0; t < samp->sample_length; t++ )
+                       newdata[t] = ((u16*)samp->data)[t] / 256;
+               free( samp->data );
+               samp->data = newdata;
+//             samp->bit16=false;
+               samp->format &= ~SAMPF_16BIT;
+       }
+}
+
+void Sample_Sign( Sample* samp )
+{
+       // sample must be unsigned
+       u32 x;
+       if( samp->format & SAMPF_16BIT )
+       {
+               for( x =0 ; x < samp->sample_length; x++ )
+               {
+                       int a =  (( (int) ((u16*)samp->data)[x] ) - 32768);
+                       if(a < -32767) a = -32767;                                              // clamp LOW to -32767 (leave space for interpolation error)
+//                     if(a > 32765)  a = 32765;                                               // clamp HIGH to 32766
+                       ((u16*)samp->data)[x] = (u16)a ;
+               }
+       }
+       else
+       {
+               for( x =0 ; x < samp->sample_length; x++ )
+               {
+                       int a =  (( (int) ((u8*)samp->data)[x] ) - 128);
+                       if( a == -128 ) a = -127;
+                       ((u8*)samp->data)[x] = (u8) a;
+               }
+
+       }
+       samp->format |= SAMPF_SIGNED;
+}
+
+void FixSample_GBA( Sample* samp )
+{
+       // convert to 8-bit if neccesary
+       Sample_8bit( samp );
+       
+       // delete data after loop_end if loop exists
+       if( samp->loop_type != 0 )
+               samp->sample_length = samp->loop_end;
+       
+       // unroll BIDI loop
+       if( samp->loop_type == 2 )
+               Unroll_BIDI_Sample( samp );
+
+       if( samp->loop_type )
+       {
+               if( samp->loop_end-samp->loop_start < GBA_MIN_LOOP_SIZE )
+               {
+                       Unroll_Sample_Loop( samp, (GBA_MIN_LOOP_SIZE / (samp->loop_end-samp->loop_start))+1 );
+               }
+       }
+}
+
+int strcmpshit( char* str1, char* str2 )
+{
+       int x=0;
+       int f=0;
+       while( str1[x] != 0 )
+       {
+               if( str1[x] == str2[f] )f++;
+               else                                    f=0;
+               if( str2[f] == 0 )              return 1;
+               x++;
+       }
+       return 0;
+}
+
+void FixSample_NDS( Sample* samp )
+{
+       if( samp->sample_length == 0 )
+       {
+               // sample has no data
+               samp->loop_end=samp->loop_start=0;
+               return;
+       }
+       // delete data after loop_end if loop exists
+       if( samp->loop_type != 0 )
+               samp->sample_length = samp->loop_end;
+       
+       // unroll BIDI loop
+       if( samp->loop_type == 2 )
+               Unroll_BIDI_Sample( samp );
+
+       // %o option
+       if( samp->loop_type )
+       {
+               if( !ignore_sflags )
+               {
+                       if( ((strcmpshit( samp->name, "%o"  )) > 0) )
+                       {
+                               Unroll_Sample_Loop( samp, 1 );
+                               samp->loop_start += (samp->loop_end-samp->loop_start) / 2;
+                       }
+               }
+       }
+
+       if( !ignore_sflags )
+       {
+               if( ((strcmpshit( samp->name, "%c" )) > 0) )
+               {
+                       samp->format |= SAMPF_COMP;
+               }
+       }
+       
+       // Resize loop
+       if( samp->loop_type )
+       {
+               int looplen = samp->loop_end-samp->loop_start;
+               if( !(samp->format & SAMPF_COMP) )
+               {
+                       if( samp->format & SAMPF_16BIT )
+                       {
+                               if( looplen & 1 )
+                               {
+                                       int addition = (samp->loop_end - samp->loop_start);
+                                       if( addition > MAX_UNROLL_THRESHOLD )
+                                               Resample( samp, samp->sample_length +1 );
+                                       else
+                                               Unroll_Sample_Loop( samp, 1 );
+                               }
+                       }
+                       else
+                       {
+                               if( looplen & 3 )
+                               {
+                                       int count;
+                                       int addition;
+                                       count = looplen & 3;
+                                       switch( count ) {
+                                       case 0:
+                                               count=0;        break;
+                                       case 1:
+                                               count=3;        break;
+                                       case 2:
+                                               count=1;        break;
+                                       case 3:
+                                               count=3;        break;
+                                       }
+                                       addition = looplen*count;
+                                       if( addition > MAX_UNROLL_THRESHOLD )
+                                               Resample( samp, samp->sample_length + (4-(looplen & 3)) );
+                                       else
+                                               Unroll_Sample_Loop( samp, count );
+                               }
+                       }
+               }
+               else
+               {
+                       int a = looplen;
+                       int count=0, addition;
+                       while( looplen & 7 )
+                       {
+                               count++;
+                               looplen += a;
+                       }
+                       addition = looplen*count;
+                       if( addition > MAX_UNROLL_THRESHOLD )
+                               Resample( samp, samp->sample_length + (4-(looplen & 7)) );
+                       else
+                               Unroll_Sample_Loop( samp, count );
+               }
+       }
+       
+       // Align loop_start
+       if( samp->loop_type )
+       {
+               int padsize;
+               if( !(samp->format & SAMPF_COMP) )
+               {
+                       if( samp->format & SAMPF_16BIT ) {
+                               padsize = ( (2 - (samp->loop_start & 1)) & 1 );
+                       } else {
+                               padsize = ( (4 - (samp->loop_start & 3)) & 3 );
+                       }
+               }
+               else
+               {
+                       padsize = ( (8 - (samp->loop_start & 7)) & 7 );
+               }
+               Sample_PadStart( samp, padsize );
+       }
+       
+       // Pad end, only happens when loop is disabled
+       if( !(samp->format & SAMPF_COMP) )
+       {
+               if( samp->format & SAMPF_16BIT )
+               {
+                       if( samp->sample_length & 1 )
+                       {
+                               Sample_PadEnd( samp, 2-(samp->sample_length&1) );
+                       }
+               }
+               else
+               {
+                       if( samp->sample_length & 3 )
+                       {
+                               Sample_PadEnd( samp, 4-(samp->sample_length&3) );
+                       }
+               }
+       }
+       else
+       {
+               if( samp->sample_length & 7 )
+               {
+                       Sample_PadEnd( samp, 8-(samp->sample_length&7) );
+               }
+       }
+       
+       Sample_Sign( samp );    // DS hardware takes signed samples
+
+       if( samp->format & SAMPF_COMP )
+       {
+               // compress with IMA-ADPCM hunger owned
+               adpcm_compress_sample( samp );
+       }
+       else
+       {
+               
+       }
+}
+
+void FixSample( Sample* samp )
+{
+       // Clamp loop_start and loop_end (f.e. FR_TOWER.MOD)
+       samp->loop_start = CLAMP(samp->loop_start, 0, samp->sample_length);
+       samp->loop_end = CLAMP(samp->loop_end, 0, samp->sample_length);
+
+       if( target_system == SYSTEM_GBA )
+               FixSample_GBA( samp );
+       else if( target_system == SYSTEM_NDS )
+               FixSample_NDS( samp );
+}
+