// Project: FLDEVOX by Xaby // Created: 2017-04-06 /* converted from C to AGK2 by Xaby aka Folker Linstedt // Source: http://faculty.salina.k-state.edu/tim/software/vox/vox.html#the-standard // by Tim Bower /* File: adpcm.c Description: Routines to convert 12 bit linear samples to the Dialogic or Oki ADPCM coding format. I copied the algorithms out of the book "PC Telephony - The complete guide to designing, building and programming systems using Dialogic and Related Hardware" by Bob Edgar. pg 272-276. Note: Edgar's book says that the second to last value is 1408; however, * The standard says it is 1411. * Changed on 1/17/2003. */ // show all errors SetErrorMode(2) // set window properties SetWindowTitle( "FLDEVOX" ) SetWindowSize( 320, 200, 0 ) // set display properties SetVirtualResolution( 320, 200 ) SetOrientationAllowed( 1, 1, 1, 1 ) SetSyncRate( 30, 0 ) // 30fps instead of 60 to save battery UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts Type adpcm_status last as integer // short -127..128 step_index as integer // short -127..128 EndType //Global coder_stat as adpcm_status // global, because no classes? Function adpcm_init( stat ref as adpcm_status) stat.last = 0 stat.step_index = 0 // //also see here https://wiki.multimedia.cx/index.php/Dialogic_IMA_ADPCM // coder_stat.last = 0 // only here because of global variable // coder_stat.step_index = 0 // only here because of global variable EndFunction Global adpcm_step_size as integer[49] = [ 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552] Global ima_step_table as integer[89] = [ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767] Global ima_index_table as integer[16] = [ // not in use -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 ] Function step_adjust(code as integer) value = 0 code = mod(code,8) // also for IMA, not only VOX select( code && 0x07 ) case 0x00: value=-1 endcase case 0x01: value=-1 endcase case 0x02: value=-1 endcase case 0x03: value=-1 endcase case 0x04: value=2 endcase case 0x05: value=4 endcase case 0x06: value=6 endcase case 0x07: value=8 endcase endselect Endfunction value /* * Encode linear to ADPCM */ Function adpcm_encode( samp, cstat ref as adpcm_status, vox as integer) code as integer diff as integer E as integer SS as integer //SS = adpcm_step_size[cstat.step_index] if vox talpha = 8 else talpha = 0 endif SS = ima_step_table[cstat.step_index + talpha] code = 0x00 diff = samp - cstat.last if( diff < 0 ) code = 0x08 endif if diff<0 E=-diff else E=diff endif //E = diff < 0 ? -diff : diff; if( E >= SS ) code = code || 0x04 E = E-SS endif if( E >= SS/2 ) code = code || 0x02 E = E-SS/2 endif if( E >= SS/4 ) code = code || 0x01 endif /* stat->step_index += step_adjust( code ); if( stat->step_index < 0 ) stat->step_index = 0; if( stat->step_index > 48 ) stat->step_index = 48; */ /* * Use the decoder to set the estimate of last sample. * It also will adjust the step_index for us. */ cstat.last = adpcm_decode(code, cstat, vox) //code = 3 EndFunction code /* * Decode Linear to ADPCM */ function adpcm_decode( code as integer, stat ref as adpcm_status, vox) // without global: function adpcm_decode( code as integer, stat as adpcm_status) diff as integer E as integer SS as integer samp as integer if vox talpha = 9 boundp = 2047 boundn = -2048 lastdex = 48 // SS = adpcm_step_size[stat.step_index] else talpha = 0 // IMA ADPCM full table boundp = 32767 boundn = -32768 lastdex = 88 endif SS = ima_step_table[stat.step_index + talpha] E = SS/8 if ( code && 0x01 ) E = E+SS/4 endif if ( code && 0x02 ) E =E+ SS/2 endif if ( code && 0x04 ) E = E+SS endif if (code && 0x08) > 0 diff = -E else diff = E endif samp = stat.last + diff /* * Clip the values to +(2^11)-1 to -2^11. (12 bits 2's * compelement) * Note: previous version errantly clipped at +2048, which could * cause a 2's complement overflow and was likely the source of * clipping problems in the previous version. Thanks to Frank * van Dijk for the correction. TLB 3/30/04 */ if( samp > boundp ) samp = boundp endif if( samp < boundn ) samp = boundn endif stat.last = samp stat.step_index = stat.step_index + step_adjust( code ) if( stat.step_index < 0 ) : stat.step_index = 0 : endif if( stat.step_index > lastdex ) : stat.step_index = lastdex : endif Endfunction samp Function LoadSoundVOX(filename$,frequenz,vox) Channels = 1 Bits = 16 stat as adpcm_status // Mono, 16 Bit if vox dbits = 16 else dbits = 1 endif adpcm_init( stat ) // it's global, don't know, how to make a reference pointer *coder_stat SoundID = -1 adpcmem = CreateMemblockFromFile(filename$) // vox without header RAW 4 Bit // 1 Byte contains 2 Samples. "1 Byte contains 4 Bytes" mox = CreateMemblock(GetMemblockSize(adpcmem)*4+12) // +12 for Header SetMemblockByte(mox,0,Channels) // mono SetMemblockByte(mox,2,Bits) // 16 Bit SetMemblockInt (mox,4,frequenz) // 44100 SetMemblockInt (mox,8,GetMemblockSize(adpcmem)*2) // 4 // buffer12 is mox for i=0 to GetMemblockSize(adpcmem)-1 SetMemblockShort(mox,12+i*4+0,adpcm_decode( (GetMemblockByte(adpcmem,i)>>4)&&0x0f,stat,vox )*dbits) SetMemblockShort(mox,12+i*4+2,adpcm_decode( GetMemblockByte(adpcmem,i)&&0x0f,stat,vox )*dbits) Next //--- create SoundHeader --- soundID = CreateSoundFromMemblock(mox) // was for test only, is not a real WAVE-RIFF File, but works in AudaCity as RAW //CreateFileFromMemblock("testfile_44100_16Bit_Mono.raw",mox) DeleteMemblock(mox) DeleteMemblock(adpcmem) EndFunction soundID Function CreateMemBlockVOX(memblAudio,vox) // creates a MemBlock 4 Bit from 16 Bit Audio // 16 Bit Mono is supported stat as adpcm_status header = 12 voxmem = CreateMemblock((GetMemblockSize(memblAudio)-header)/4) // //adpcm as Integer[] adpcm_init( stat ) if vox dbits = 16 else dbits = 1 endif j=-1 For i=0 to GetMemBlockSize(voxmem)-1 inc j sample1 = GetMemblockShort(memblAudio,header+2*j) / dbits // 16 Bit to 12 Bit for vox SetMemblockByte(voxmem,i,adpcm_encode( sample1, stat,vox )<<4) if( j > (GetMemblockSize(memblAudio)-header)) /* only true for last sample when n is odd */ SetMemblockByte(voxmem,i,GetMemblockByte(voxmem,i)||adpcm_encode(0, stat,vox)) else inc j sample2 = GetMemblockShort(memblAudio,header+2*j)/dbits SetMemblockByte(voxmem,i,GetMemblockByte(voxmem,i)||adpcm_encode(sample2, stat,vox)) endif Next EndFunction voxmem voxid = LoadSoundVOX("woman_16-bit_44KHz_master.vox",44100,1) // Mono Only supported, but could be changed easily //memid = CreateMemblockFromSound(voxid) //voxcompmem = CreateMemBlockVOX(memid) //CreateFileFromMemblock("output2.vox",voxcompmem) //CreateFileFromMemblock("output.wav",memid) //soid = LoadSound("tide_16-bit_44KHz_master.wav") /* soid = LoadSound("woman_16-bit_44KHz_master.wav") memid = CreateMemblockFromSound(soid) voxcompmem = CreateMemBlockVOX(memid,1) CreateFileFromMemblock("musv.vox",voxcompmem) */ If voxid <> -1 PlaySound(voxid) EndIf do Print( "DeVOX, plays VOX-Files, ADPCM 4 Bit converts to 16 Bit FPS: "+Str(ScreenFPS(),2) ) // Print(voxid) Sync() loop