my attempt at libmodplug bindings in python

This forum is currently in read-only mode.
  • Heya everybody.

    In anticipation of support for Python returning to Construct 1.0, I was looking for creating some bindings for modplug.dll, aka libmodplug, a GPL mod replayer engine which is probably better than libmikmod (although maybe not as well-documented)..... I appreciate all the work Rich and Ashley did (especially granting my request for mod support in Construct), however, the solution which was implemented ended up being less than ideal for handling Impulse Tracker files

    Since I am kinda new to python (and know little to no C++), getting this far was a pain in the butt, but I think using some specific tools (gccxml + xml2py from Python's ctypeslib), I got the beginnings of the proper bindings. This was all based on the source headers available with libmodplug, specifically, modplug.h. I'm not sure how to plug it into an audio output quite yet, but I'll let you guys know if I do! Here is what I have so far:

    modplug.py

    from ctypes import *
    
    _libraries = {}
    _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'] = CDLL('C:\\python26\\scripts\\modplug\\modplug.dll')
    STRING = c_char_p
    
    MODPLUG_ENABLE_NOISE_REDUCTION = 2
    MODPLUG_RESAMPLE_NEAREST = 0
    MODPLUG_RESAMPLE_SPLINE = 2
    MODPLUG_ENABLE_SURROUND = 16
    MODPLUG_ENABLE_MEGABASS = 8
    MODPLUG_ENABLE_REVERB = 4
    MODPLUG_ENABLE_OVERSAMPLING = 1
    MODPLUG_RESAMPLE_FIR = 3
    MODPLUG_RESAMPLE_LINEAR = 1
    class _ModPlugFile(Structure):
        pass
    _ModPlugFile._fields_ = [
    ]
    ModPlugFile = _ModPlugFile
    ModPlug_Load = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_Load
    ModPlug_Load.restype = POINTER(ModPlugFile)
    ModPlug_Load.argtypes = [c_void_p, c_int]
    ModPlug_Unload = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_Unload
    ModPlug_Unload.restype = None
    ModPlug_Unload.argtypes = [POINTER(ModPlugFile)]
    ModPlug_Read = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_Read
    ModPlug_Read.restype = c_int
    ModPlug_Read.argtypes = [POINTER(ModPlugFile), c_void_p, c_int]
    ModPlug_GetName = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_GetName
    ModPlug_GetName.restype = STRING
    ModPlug_GetName.argtypes = [POINTER(ModPlugFile)]
    ModPlug_GetLength = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_GetLength
    ModPlug_GetLength.restype = c_int
    ModPlug_GetLength.argtypes = [POINTER(ModPlugFile)]
    ModPlug_Seek = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_Seek
    ModPlug_Seek.restype = None
    ModPlug_Seek.argtypes = [POINTER(ModPlugFile), c_int]
    
    # values for enumeration '_ModPlug_Flags'
    _ModPlug_Flags = c_int # enum
    
    # values for enumeration '_ModPlug_ResamplingMode'
    _ModPlug_ResamplingMode = c_int # enum
    class _ModPlug_Settings(Structure):
        pass
    _ModPlug_Settings._fields_ = [
        ('mFlags', c_int),
        ('mChannels', c_int),
        ('mBits', c_int),
        ('mFrequency', c_int),
        ('mResamplingMode', c_int),
        ('mReverbDepth', c_int),
        ('mReverbDelay', c_int),
        ('mBassAmount', c_int),
        ('mBassRange', c_int),
        ('mSurroundDepth', c_int),
        ('mSurroundDelay', c_int),
        ('mLoopCount', c_int),
    ]
    ModPlug_Settings = _ModPlug_Settings
    ModPlug_GetSettings = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_GetSettings
    ModPlug_GetSettings.restype = None
    ModPlug_GetSettings.argtypes = [POINTER(ModPlug_Settings)]
    ModPlug_SetSettings = _libraries['C:\\python26\\scripts\\modplug\\modplug.dll'].ModPlug_SetSettings
    ModPlug_SetSettings.restype = None
    ModPlug_SetSettings.argtypes = [POINTER(ModPlug_Settings)]
    __all__ = ['_ModPlug_Flags', 'MODPLUG_RESAMPLE_LINEAR',
               'MODPLUG_ENABLE_OVERSAMPLING', 'MODPLUG_ENABLE_MEGABASS',
               'MODPLUG_ENABLE_REVERB', '_ModPlug_Settings',
               'ModPlug_Settings', 'ModPlug_SetSettings',
               'ModPlug_GetName', 'MODPLUG_ENABLE_NOISE_REDUCTION',
               'MODPLUG_RESAMPLE_NEAREST', 'ModPlug_Load',
               '_ModPlug_ResamplingMode', 'ModPlug_Read',
               'ModPlug_Unload', 'MODPLUG_ENABLE_SURROUND', 'ModPlugFile',
               'MODPLUG_RESAMPLE_FIR', 'ModPlug_GetLength',
               '_ModPlugFile', 'MODPLUG_RESAMPLE_SPLINE', 'ModPlug_Seek',
               'ModPlug_GetSettings']
    [/code:ahony2oi]
    
    You'll probably need to replace the hardcoded paths to a dynamic path, replacing with modplug.dll for windows, and libmodplug.so for Linux.  What's not finished yet is the code to call the proper function to play the song.  I'm hoping someone smarter than me might be able to figure that out!
    
    You can get Modplug.dll by downloading [url=http://alister.eu/jazz/oj/download.php]OpenJazz[/url] (an open source Jazz Jackrabbit clone).  From there, this source file can presumably be called to load the functions.  Whew....
  • oh, for anyone who wants to help, it might be of assistance to post the corresponding C++ header file, which is much much much better commented:

    modplug.h

    /*
     * This source code is public domain.
     *
     * Authors: Kenton Varda <<>rmk@gauge3d.org> (C interface wrapper)
     */
    
    #ifndef MODPLUG_H__INCLUDED
    #define MODPLUG_H__INCLUDED
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    struct _ModPlugFile;
    typedef struct _ModPlugFile ModPlugFile;
    
    /* Load a mod file.  [data] should point to a block of memory containing the complete
     * file, and [size] should be the size of that block.
     * Return the loaded mod file on success, or NULL on failure. */
    ModPlugFile* ModPlug_Load(const void* data, int size);
    /* Unload a mod file. */
    void ModPlug_Unload(ModPlugFile* file);
    
    /* Read sample data into the buffer.  Returns the number of bytes read.  If the end
     * of the mod has been reached, zero is returned. */
    int  ModPlug_Read(ModPlugFile* file, void* buffer, int size);
    
    /* Get the name of the mod.  The returned buffer is stored within the ModPlugFile
     * structure and will remain valid until you unload the file. */
    const char* ModPlug_GetName(ModPlugFile* file);
    
    /* Get the length of the mod, in milliseconds.  Note that this result is not always
     * accurate, especially in the case of mods with loops. */
    int ModPlug_GetLength(ModPlugFile* file);
    
    /* Seek to a particular position in the song.  Note that seeking and MODs don't mix very
     * well.  Some mods will be missing instruments for a short time after a seek, as ModPlug
     * does not scan the sequence backwards to find out which instruments were supposed to be
     * playing at that time.  (Doing so would be difficult and not very reliable.)  Also,
     * note that seeking is not very exact in some mods -- especially those for which
     * ModPlug_GetLength() does not report the full length. */
    void ModPlug_Seek(ModPlugFile* file, int millisecond);
    
    enum _ModPlug_Flags
    {
    	MODPLUG_ENABLE_OVERSAMPLING     = 1 << 0,  /* Enable oversampling (*highly* recommended) */
    	MODPLUG_ENABLE_NOISE_REDUCTION  = 1 << 1,  /* Enable noise reduction */
    	MODPLUG_ENABLE_REVERB           = 1 << 2,  /* Enable reverb */
    	MODPLUG_ENABLE_MEGABASS         = 1 << 3,  /* Enable megabass */
    	MODPLUG_ENABLE_SURROUND         = 1 << 4   /* Enable surround sound. */
    };
    
    enum _ModPlug_ResamplingMode
    {
    	MODPLUG_RESAMPLE_NEAREST = 0,  /* No interpolation (very fast, extremely bad sound quality) */
    	MODPLUG_RESAMPLE_LINEAR  = 1,  /* Linear interpolation (fast, good quality) */
    	MODPLUG_RESAMPLE_SPLINE  = 2,  /* Cubic spline interpolation (high quality) */
    	MODPLUG_RESAMPLE_FIR     = 3   /* 8-tap fir filter (extremely high quality) */
    };
    
    typedef struct _ModPlug_Settings
    {
    	int mFlags;  /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */
    	
    	/* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then
    	 * down-mixes to the settings you choose. */
    	int mChannels;       /* Number of channels - 1 for mono or 2 for stereo */
    	int mBits;           /* Bits per sample - 8, 16, or 32 */
    	int mFrequency;      /* Sampling rate - 11025, 22050, or 44100 */
    	int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */
    	
    	int mReverbDepth;    /* Reverb level 0(quiet)-100(loud)      */
    	int mReverbDelay;    /* Reverb delay in ms, usually 40-200ms */
    	int mBassAmount;     /* XBass level 0(quiet)-100(loud)       */
    	int mBassRange;      /* XBass cutoff in Hz 10-100            */
    	int mSurroundDepth;  /* Surround level 0(quiet)-100(heavy)   */
    	int mSurroundDelay;  /* Surround delay in ms, usually 5-40ms */
    	int mLoopCount;      /* Number of times to loop.  Zero prevents looping.
    	                        -1 loops forever. */
    } ModPlug_Settings;
    
    /* Get and set the mod decoder settings.  All options, except for channels, bits-per-sample,
     * sampling rate, and loop count, will take effect immediately.  Those options which don't
     * take effect immediately will take effect the next time you load a mod. */
    void ModPlug_GetSettings(ModPlug_Settings* settings);
    void ModPlug_SetSettings(const ModPlug_Settings* settings);
    
    #ifdef __cplusplus
    } /* extern "C" */
    #endif
    
    #endif
    [/code:2os8heku]
  • hmm, the work is still incomplete; it apparently relies on some types from sndfile.h, which I haven't been able to convert to python using gccxml yet due to errors. I was informed of this on another forum, so I guess more research is necessary before this can be made useful just yet...

  • In my opinion, I guess you could find more help on actual programming forums..

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • breakthrough

    http://sharebee.com/bc8e1f08

    It works!!!!! This uses python's standard wave library to output a mod file to wave. With proper access to a Construct sound buffer, the output could easily be routed to our games as well

    However, I don't know why, but the writing function is pretty slow! It probably has to do with all the appending of sample_data in the read looping function. When the file's played in real-time, it probably won't be much of a problem. However, please give this script 20-30 seconds to process a 3-5 minute song before the wave file is written.

    I guess I can say now this is my first functional Python program, I hope it is helpful to anyone here �3

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)