{ * Copyright (c) 2012 Andrey Kemka * * This software is provided 'as-is', without any express or * implied warranty. In no event will the authors be held * liable for any damages arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; * you must not claim that you wrote the original software. * If you use this software in a product, an acknowledgment * in the product documentation would be appreciated but * is not required. * * 2. Altered source versions must be plainly marked as such, * and must not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any * source distribution. } unit zglModPlug; {$I zglCustomConfig.cfg} {$IFDEF FPC} {$MODE DELPHI} {$PACKRECORDS C} {$MINENUMSIZE 4} {$ENDIF} interface uses {$IFDEF USE_ZENGL_STATIC} zgl_types, zgl_application, zgl_main, zgl_sound, zgl_log, zgl_file, zgl_memory, zgl_utils {$ELSE} zglHeader {$ENDIF} ; const {$IFDEF WINDOWS} libmodplug = 'libmodplug.dll'; {$ENDIF} {$IFDEF LINUX} libmodplug = 'libmodplug.so'; {$ENDIF} {$IFDEF MACOSX} libmodplug = 'libmodplug.dylib'; {$ENDIF} MAX_FORMATS = 22; procedure InitModPlug; procedure FreeModPlug; implementation const MODPLUG_ENABLE_OVERSAMPLING = 1 shl 0; MODPLUG_ENABLE_NOISE_REDUCTION = 1 shl 1; MODPLUG_ENABLE_REVERB = 1 shl 2; MODPLUG_ENABLE_MEGABASS = 1 shl 3; MODPLUG_ENABLE_SURROUND = 1 shl 4; MODPLUG_RESAMPLE_NEAREST = 0; MODPLUG_RESAMPLE_LINEAR = 1; MODPLUG_RESAMPLE_SPLINE = 2; MODPLUG_RESAMPLE_FIR = 3; type PModPlugFile = ^ModPlugFile; ModPlugFile = record end; var Decoders : array[ 0..MAX_FORMATS - 1 ] of zglTSoundDecoder; FORMATS : array[ 0..MAX_FORMATS - 1 ] of UTF8String = ( 'MOD', 'IT', 'S3M', 'XM', 'IT', '669', 'AMF', 'AMS', 'DBM', 'DMF', 'DSM', 'FAR', 'MDL', 'MED', 'MTM', 'OKT', 'PTM', 'STM', 'ULT', 'UMX', 'MT2', 'PSM' ); mpLoad : Boolean; mpInit : Boolean; mpLibrary : {$IFDEF WIN32} LongWord {$ELSE} Pointer {$ENDIF}; ModPlug_Load : function(data: pointer; size: longint): PModPlugFile; cdecl; ModPlug_Unload : procedure(_file: PModPlugFile); cdecl; ModPlug_Read : function(_file: PModPlugFile; buffer: pointer; size: longint): longint; cdecl; ModPlug_Seek : procedure(_file: PModPlugFile; millisecond: longint); cdecl; ModPlug_GetLength : function(_file: PModPlugFile): longint; cdecl; function mp_DecoderOpen( var Stream : zglTSoundStream; const FileName : UTF8String ) : Boolean; var mem : zglTMemory; begin if not mpLoad Then InitModPlug(); if not mpInit Then exit; mem_LoadFromFile( mem, FileName ); PModPlugFile( Stream._data ) := ModPlug_Load( mem.Memory, mem.Size ); mem_Free( mem ); if Assigned( Stream._data ) Then begin Result := TRUE; Stream.Bits := 16; Stream.Frequency := 44100; Stream.Channels := 2; Stream.Duration := ModPlug_GetLength( PModPlugFile( Stream._data ) ); Stream.BufferSize := 64 * 1024; zgl_GetMem( Pointer( Stream.Buffer ), Stream.BufferSize ); end else Result := FALSE; end; function mp_DecoderOpenMem( var Stream : zglTSoundStream; const Memory : zglTMemory ) : Boolean; begin if not mpLoad Then InitModPlug; if not mpInit Then exit; PModPlugFile( Stream._data ) := ModPlug_Load( Memory.Memory, Memory.Size ); if Assigned( Stream._data ) Then begin Result := TRUE; Stream.Bits := 16; Stream.Frequency := 44100; Stream.Channels := 2; Stream.Duration := ModPlug_GetLength( PModPlugFile( Stream._data ) ); Stream.BufferSize := 64 * 1024; zgl_GetMem( Pointer( Stream.Buffer ), Stream.BufferSize ); end else Result := FALSE; end; function mp_DecoderRead( var Stream : zglTSoundStream; Buffer : PByteArray; Bytes : LongWord; out _End : Boolean ) : LongWord; begin if not mpInit Then exit; Result := ModPlug_Read( PModPlugFile( Stream._data ), @Buffer[ 0 ], Bytes ); _End := Result = 0; end; procedure mp_DecoderLoop( var Stream : zglTSoundStream ); begin if not mpInit Then exit; ModPlug_Seek( PModPlugFile( Stream._data ), 0 ); end; procedure mp_DecoderClose( var Stream : zglTSoundStream ); begin if not mpInit Then exit; ModPlug_Unload( PModPlugFile( Stream._data ) ); Stream._data := nil; end; procedure InitModPlug; begin {$IFDEF LINUX} {$IFNDEF ANDROID} mpLibrary := dlopen( PAnsiChar( './' + libmodplug + '.1' ), $001 ); if mpLibrary = LIB_ERROR Then mpLibrary := dlopen( PAnsiChar( libmodplug + '.1' ), $001 ); if mpLibrary = LIB_ERROR Then mpLibrary := dlopen( PAnsiChar( libmodplug + '.0' ), $001 ); {$ELSE} mpLibrary := dlopen( libmodplug, $001 ); {$ENDIF} {$ENDIF} {$IFDEF WINDOWS} mpLibrary := dlopen( libmodplug ); {$ENDIF} {$IFDEF MACOSX} mpLibrary := dlopen( PAnsiChar( PAnsiChar( zgl_Get( DIRECTORY_APPLICATION ) ) + 'Contents/Frameworks/' + libmodplug ), $001 ); {$ENDIF} if mpLibrary <> LIB_ERROR Then begin ModPlug_Load := dlsym( mpLibrary, 'ModPlug_Load' ); ModPlug_Unload := dlsym( mpLibrary, 'ModPlug_Unload' ); ModPlug_Read := dlsym( mpLibrary, 'ModPlug_Read' ); ModPlug_Seek := dlsym( mpLibrary, 'ModPlug_Seek' ); ModPlug_GetLength := dlsym( mpLibrary, 'ModPlug_GetLength' ); log_Add( 'ModPlug: Successful initialized' ); mpInit := TRUE; end else begin log_Add( 'ModPlug: Error while loading ' + libmodplug ); mpInit := FALSE; end; mpLoad := TRUE; end; procedure FreeModPlug; begin mpInit := FALSE; dlclose( mpLibrary ); end; var i : Integer; initialization for i := 0 to MAX_FORMATS - 1 do begin Decoders[ i ].Ext := FORMATS[ i ]; Decoders[ i ].Open := @mp_DecoderOpen; Decoders[ i ].OpenMem := @mp_DecoderOpenMem; Decoders[ i ].Read := @mp_DecoderRead; Decoders[ i ].Seek := nil; Decoders[ i ].Loop := @mp_DecoderLoop; Decoders[ i ].Close := @mp_DecoderClose; zgl_Reg( SND_FORMAT_EXTENSION, @FORMATS[ i, 1 ] ); zgl_Reg( SND_FORMAT_FILE_LOADER, nil ); zgl_Reg( SND_FORMAT_MEM_LOADER, nil ); zgl_Reg( SND_FORMAT_DECODER, @Decoders[ i ] ); end; finalization if mpInit Then FreeModPlug(); end.