/* mpg123clr: MPEG Audio Decoder library Common Language Runtime version. copyright 2009-2011 by Malcolm Boczek - free software under the terms of the LGPL 2.1 mpg123clr.dll is a derivative work of libmpg123 - all original mpg123 licensing terms apply. All rights to this work freely assigned to the mpg123 project. */ /* libmpg123: MPEG Audio Decoder library copyright 1995-2011 by the mpg123 project - free software under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org */ /* 1.8.1.0 04-Aug-09 Initial release. 1.9.0.0 24-Sep-09 Function names harmonized with libmpg123 (mb) 1.9.0.0 01-Oct-09 Technical cleanup - subst nullptr for NULL (mb) 1.9.0.0 13-Oct-09 pin_ptr = nullptr on return (mb) 1.9.0.1 24-Nov-09 performance update - removed try/finally (mb) 1.10.0.0 30-Nov-09 release match - added mpg123_feature (mb) 1.12.0.0 14-Apr-10 release match - added framebyframe and "handle" ReplaceReaders (mb) 1.13.0.0 13-Jan-11 release match - added encsize (mb) */ // mpg123clr.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #include "mpg123clr.h" mpg123clr::mpg123::mpg123(void) { mh = nullptr; useHandleReplacement = false; lastReplacementWasHandle = false; } mpg123clr::mpg123::mpg123(mpg123_handle* mh) { this->mh = mh; useHandleReplacement = false; lastReplacementWasHandle = false; } // Destructor mpg123clr::mpg123::~mpg123(void) { // clean up code to release managed resources // call Finalizer to clean up unmanaged resources this->!mpg123(); // CLI implements SuppressFinalize - therefore not required here... //GC::SuppressFinalize(this); } // Finalizer mpg123clr::mpg123::!mpg123(void) { // clean up unmanaged resources if (mh != nullptr) { ::mpg123_delete(mh); mh = nullptr; } } void mpg123clr::mpg123::mpg123_delete(void) { if (mh != nullptr) { ::mpg123_delete(mh); mh = nullptr; } } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_init(void) { return (mpg123clr::mpg::ErrorCode) ::mpg123_init(); } void mpg123clr::mpg123::mpg123_exit(void) { ::mpg123_exit(); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_new(String ^ decoder) { const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(decoder)).ToPointer(); int err; mh = ::mpg123_new(chars, &err); Marshal::FreeHGlobal(IntPtr((void*)chars)); return (mpg123clr::mpg::ErrorCode) err; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_new(void) { int err; mh = ::mpg123_new(NULL, &err); return (mpg123clr::mpg::ErrorCode) err; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_parnew(mpg123clr::advpars^ par, String ^ decoder) { const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(decoder)).ToPointer(); int err; mh = ::mpg123_parnew(par->mp, chars, &err); Marshal::FreeHGlobal(IntPtr((void*)chars)); return (mpg123clr::mpg::ErrorCode) err; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_parnew(mpg123clr::advpars^ par) { int err; mh = ::mpg123_parnew(par->mp, NULL, &err); return (mpg123clr::mpg::ErrorCode) err; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_param(mpg123clr::mpg::parms type, int val, double fval) { return (mpg123clr::mpg::ErrorCode) ::mpg123_param(mh, (mpg123_parms)type, val, fval); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_getparam(mpg123clr::mpg::parms type, [Out] int% val, [Out] double% fval) { // Avoid need for local intermediary variables pin_ptr _val = &val; pin_ptr _fval = &fval; int ret = ::mpg123_getparam(mh, (mpg123_parms)type, (long*) _val, _fval); _fval = nullptr; _val = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } int mpg123clr::mpg123::mpg123_feature(mpg123clr::mpg::feature_set key) { return ::mpg123_feature((mpg123_feature_set) key); } String^ mpg123clr::mpg123::mpg123_strerror(void) { return gcnew String(::mpg123_strerror(mh)); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_errcode(void) { return (mpg123clr::mpg::ErrorCode) ::mpg123_errcode(mh); } array^ mpg123clr::mpg123::StringArrayFromPtr(const char** ptr) { int count = 0; // count how many strings in array by walking up the array until NULL found while (*(ptr++) != NULL){ ++count; } // create an array of the correct size array ^ str = gcnew array(count); --ptr; // loop leaves ptr +2 beyond end of array - put ptr back to just after last index // walk back down the array, extracting the strings while (count-- > 0){ str[count] = gcnew String(*(--ptr)); } return str; } array^ mpg123clr::mpg123::mpg123_decoders(void) { return StringArrayFromPtr(::mpg123_decoders()); } array^ mpg123clr::mpg123::mpg123_supported_decoders(void) { return StringArrayFromPtr(::mpg123_supported_decoders()); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_decoder(String^ name) { const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(name)).ToPointer(); int ret; // one of the few mpg123 places that fault with an exception... try { ret = ::mpg123_decoder(mh, chars); } catch(Exception^){ ret = MPG123_ERR; } Marshal::FreeHGlobal(IntPtr((void*)chars)); return (mpg123clr::mpg::ErrorCode) ret; } String^ mpg123clr::mpg123::mpg123_current_decoder(void) { return gcnew String(::mpg123_current_decoder(mh)); } array^ mpg123clr::mpg123::mpg123_rates(void) { size_t number; const long* list; ::mpg123_rates(&list, &number); array^ rList = gcnew array((int)number); int index = 0; // walk the array, extracting the rates while (index < (int)number){ rList[index++] = *(list++); } return rList; } array^ mpg123clr::mpg123::mpg123_encodings(void) { size_t number; const int* list; ::mpg123_encodings(&list, &number); // WARN 4267 - assuming that the number of encodings will never exceed 32bits array^ rList = gcnew array((int)number); int index = 0; // walk the array, extracting the rates while (index < (int)number){ rList[index++] = (mpg123clr::mpg::enc) *(list++); } return rList; } int mpg123clr::mpg123::mpg123_encsize(mpg123clr::mpg::enc encoding) { return ::mpg123_encsize((int) encoding); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_format_none(void) { return (mpg123clr::mpg::ErrorCode) ::mpg123_format_none(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_format_all(void) { return (mpg123clr::mpg::ErrorCode) ::mpg123_format_all(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_format(int rate, mpg123clr::mpg::channelcount channels, mpg123clr::mpg::enc encodings) { return (mpg123clr::mpg::ErrorCode) ::mpg123_format(mh, rate, (int) channels, (int) encodings); } mpg123clr::mpg::channelcount mpg123clr::mpg123::mpg123_format_support(int rate, mpg123clr::mpg::enc encodings) { return (mpg123clr::mpg::channelcount) ::mpg123_format_support(mh, rate, (int) encodings); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_getformat([Out] int% rate, [Out] mpg123clr::mpg::channelcount% channels, [Out] mpg123clr::mpg::enc% encodings) { // either:- // // long _rate; // int _chan, _enc; // int ret = mpg123_getformat(mh, &_rate, &_chan, &_enc); // rate = _rate; // channels = _chan; // encodings = _enc; // return (mpg123clr::mpg::ErrorCode) ret; // // or:- // use pinned pointers instead pin_ptr _rate = &rate; pin_ptr _chan = &channels; pin_ptr _enc = &encodings; int ret = ::mpg123_getformat(mh, (long*)_rate, (int*)_chan, (int*)_enc); _enc = nullptr; _chan = nullptr; _rate = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_open(String^ path) { const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(path)).ToPointer(); _ReplaceReader(); int ret = ::mpg123_open(mh, chars); Marshal::FreeHGlobal(IntPtr((void*)chars)); return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_open_fd(int filedescriptor) { _ReplaceReader(); return (mpg123clr::mpg::ErrorCode) ::mpg123_open_fd(mh, filedescriptor); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_open_handle(System::Object^ obj) { // NOTE: You must have configured callbacks using mpg123_replace_reader_handle // before calling mpg123_open_handle! _ReplaceReader(); // activate callbacks // Make sure we free this in Close functions userObjectHandle = GCHandle::Alloc(obj); // NOTE: This sends a HANDLE not an ADDRESS, no pinning required return (mpg123clr::mpg::ErrorCode) ::mpg123_open_handle(mh, (void*)GCHandle::ToIntPtr(userObjectHandle).ToPointer()); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_open_feed(void) { _ReplaceReader(); return (mpg123clr::mpg::ErrorCode) ::mpg123_open_feed(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_close(void) { // Closes 'mh' and calls Cleanup delegate if provided int ret = ::mpg123_close(mh); // See GCHandle.Free - caller must ensure Free called only once for a given handle. if (userObjectHandle.IsAllocated) userObjectHandle.Free(); return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_read(array^ buffer, [Out] size_t% count) { pin_ptr _count = &count; pin_ptr _ptr = &buffer[0]; int ret = ::mpg123_read(mh, _ptr, buffer->Length, _count); _ptr = nullptr; _count = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_read(array^ buffer, size_t offset, size_t size, [Out] size_t% count) { pin_ptr _count = &count; // WARN 4267 - clr limited to 32bit-length-size arrays!! pin_ptr _ptr = &buffer[(int)offset]; int ret = ::mpg123_read(mh, _ptr, size, _count); _ptr = nullptr; _count = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_feed(array^ inbuffer, size_t size) { pin_ptr _ptr = &inbuffer[0]; int ret = ::mpg123_feed(mh, _ptr, size); _ptr = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_decode(array^ inbuffer, size_t insize, array^ outbuffer, size_t outsize, [Out] size_t% count) { pin_ptr _count = &count; pin_ptr _inptr = &inbuffer[0]; pin_ptr _outptr = &outbuffer[0]; int ret = ::mpg123_decode(mh, _inptr, insize, _outptr, outsize, _count); _outptr = nullptr; _inptr = nullptr; _count = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_decode_frame([Out] off_t% num, [Out] IntPtr% audio, [Out] size_t% count) { pin_ptr _count = &count; pin_ptr _num = # pin_ptr _x = &audio; int ret = ::mpg123_decode_frame(mh, _num, (unsigned char**)_x, _count); _x = nullptr; _num = nullptr; _count = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_framebyframe_decode([Out] off_t% num, [Out] IntPtr% audio, [Out] size_t% count) { // NOTE: must use framebyframe_next to obtain successive frames pin_ptr _count = &count; pin_ptr _num = # pin_ptr _x = &audio; int ret = ::mpg123_framebyframe_decode(mh, _num, (unsigned char**)_x, _count); _x = nullptr; _num = nullptr; _count = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_framebyframe_next() { // mpg123lib has warning, Experimental API. Watch for updates!!! // framebyframe_decode doesn't automatically move on to next frame, // use framebyframe_next to move on to "Find, read and parse the next mp3 frame" int ret = ::mpg123_framebyframe_next(mh); return (mpg123clr::mpg::ErrorCode) ret; } long long mpg123clr::mpg123::mpg123_tell(void) { return ::mpg123_tell(mh); } long long mpg123clr::mpg123::mpg123_tellframe(void) { return ::mpg123_tellframe(mh); } long long mpg123clr::mpg123::mpg123_tell_stream(void) { return ::mpg123_tell_stream(mh); } long long mpg123clr::mpg123::mpg123_seek(long long offset, SeekOrigin origin) { return ::mpg123_seek(mh, (off_t)offset, (int)origin); } long long mpg123clr::mpg123::mpg123_feedseek(long long offset, SeekOrigin origin, [Out] long long% input_offset) { // NOTE: off_t fiddles... // pin_ptr _input_offset = &input_offset; // type mismatch between 32 and 64 bit offsets. off_t _input_offset; // type accomodation off_t ret = ::mpg123_feedseek(mh, (off_t)offset, (int)origin, &_input_offset); // ok types input_offset = _input_offset; // type conversion return ret; } long long mpg123clr::mpg123::mpg123_seek_frame(long long frameoffset, SeekOrigin origin) { return ::mpg123_seek_frame(mh, (off_t)frameoffset, (int)origin); } long long mpg123clr::mpg123::mpg123_timeframe(double seconds) { return ::mpg123_timeframe(mh, seconds); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_index([Out] array^% index, [Out] long long% step) { // remnant: only works if off_t, index and step are 32 bit - possible redo for 64 bit library // pin_ptr _step = &step; // int ret = mpg123_index(mh, &_list, (off_t*)_step, &_count); // index = gcnew array((int)_count); // Marshal::Copy((IntPtr)_list, index, 0, (int)_count); // alternate: works for 32 and 64 bit libraries - returns long long values off_t* _list; size_t _count; off_t _step; // type accomodation int ret = ::mpg123_index(mh, &_list, &_step, &_count); step = _step; // type conversion // WARN 4267 - clr limited to 32bit-length-size arrays!! index = gcnew array((int)_count); // walk the array, extracting the rates int idx = 0; // array length limited to 32bit while (idx < index->Length){ index[idx++] = *(_list++); } return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_index([Out] IntPtr% indexarr, [Out] long long% step, [Out] size_t% fill) { pin_ptr _fill = &fill; pin_ptr _x = &indexarr; // NOTE: untyped index pointer. off_t _step; // type accomodation int ret = ::mpg123_index(mh, (off_t**)_x, &_step, _fill); step = _step; // type conversion _x = nullptr; _fill = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_position( long long frameoffset, long long bufferedbytes, [Out] long long% currentframe, [Out] long long% framesleft, [Out] double% currentseconds, [Out] double% secondsleft) { // NOTE: off_t fiddles // pin_ptr _currentframe = ¤tframe; // pin_ptr _framesleft = &framesleft; pin_ptr _currentseconds = ¤tseconds; pin_ptr _secondsleft = &secondsleft; off_t _currentframe; // type accomodation off_t _framesleft; // type accomodation int ret = ::mpg123_position(mh, (off_t)frameoffset, (off_t)bufferedbytes, &_currentframe, &_framesleft, _currentseconds, _secondsleft); currentframe = _currentframe; // type conversion framesleft = _framesleft; // type conversion _secondsleft = nullptr; _currentseconds = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } #pragma region Volume and Equalizer /// \defgroup mpg123_voleq mpg123 volume and equalizer /// mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_eq(mpg123clr::mpg::channels channel, int band, double fval) { return (mpg123clr::mpg::ErrorCode) ::mpg123_eq(mh, (mpg123_channels)channel, band, fval); } double mpg123clr::mpg123::mpg123_geteq(mpg123clr::mpg::channels channel, int band) { return ::mpg123_geteq(mh, (mpg123_channels)channel, band); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_reset_eq(void) { return (mpg123clr::mpg::ErrorCode) ::mpg123_reset_eq(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_volume(double volume) { return (mpg123clr::mpg::ErrorCode) ::mpg123_volume(mh, volume); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_volume_change(double change) { return (mpg123clr::mpg::ErrorCode) ::mpg123_volume_change(mh, change); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_getvolume([Out] double% basevol, [Out] double% really, [Out] double% rva_db) { pin_ptr _basevol = &basevol; pin_ptr _really = &really; pin_ptr _rva_db = &rva_db; int ret = ::mpg123_getvolume(mh, _basevol, _really, _rva_db); _rva_db = nullptr; _really = nullptr; _basevol = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } #pragma endregion -Volume and Equalizer #pragma region Status and Information // \defgroup mpg123_status mpg123 status and information // // // // The "proper" way to manage structs... mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_safeinfo([Out]mpeg_frameinfo^% finfo) { // "out" our return reference and grab some memory finfo = gcnew mpeg_frameinfo; IntPtr pp = Marshal::AllocHGlobal(Marshal::SizeOf(finfo)); // get the info // could cast away... // int ret = mpg123_info(mh, reinterpret_cast((int)pp)); // int ret = mpg123_info(mh, static_cast((void*)pp)); // or let the compiler decide... int ret = ::mpg123_info(mh, (mpg123_frameinfo*)(void*)pp); // marshal data into return object and free temporary memory Marshal::PtrToStructure(pp, finfo); Marshal::FreeHGlobal(pp); return (mpg123clr::mpg::ErrorCode) ret; } // The "efficient" way to manage structs... mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_info([Out]mpeg_frameinfo^% finfo) { finfo = gcnew mpeg_frameinfo; pin_ptr _ptr = &finfo->version; // According to SizeOf... // The unmanaged and managed sizes of an object can differ, this would imply // that the memory layout for each would also be different, which would indicate // that using a ptr, derived from a ptr-to-a-managed type, in unmanaged code could // have unforseen results. // // In non-homogenous structs (like mpg123text) it can lead to corrupted CLR stack. // In homogenous structs (like finfo) it appears to be "managable". // // However, until it fails it'll do... and it's much faster (see mpg123_safeinfo(...)). // WARNING: // The epitome of "unsafe" as defined by CLR (using unmanaged pointers). // If we start to get CLR stack corruptions - check here first! (see SafeInfo for "safe" managed version) int ret = ::mpg123_info(mh, (mpg123_frameinfo*)_ptr); _ptr = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } size_t mpg123clr::mpg123::mpg123_safe_buffer(void) { return ::mpg123_safe_buffer(); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_scan(void) { return (mpg123clr::mpg::ErrorCode) ::mpg123_scan(mh); } long long mpg123clr::mpg123::mpg123_length(void) { return ::mpg123_length(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_set_filesize(long long size) { return (mpg123clr::mpg::ErrorCode) ::mpg123_set_filesize(mh, (off_t)size); } double mpg123clr::mpg123::mpg123_tpf(void) { return ::mpg123_tpf(mh); } long mpg123clr::mpg123::mpg123_clip(void) { return ::mpg123_clip(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_getstate(mpg123clr::mpg::state key, [Out] int% val, [Out] double% fval) { pin_ptr _val = &val; pin_ptr _fval = &fval; int ret = ::mpg123_getstate(mh, (mpg123_state)key, (long*) _val, _fval); _fval = nullptr; _val = nullptr; return (mpg123clr::mpg::ErrorCode) ret; } #pragma endregion -Status and Information #pragma region Metadata Handling // \defgroup mpg123_metadata mpg123 metadata handling // // Functions to retrieve the metadata from MPEG Audio files and streams. // Also includes string handling functions. // mpg123clr::id3::id3check mpg123clr::mpg123::mpg123_meta_check(void) { return (mpg123clr::id3::id3check ) ::mpg123_meta_check(mh); } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_id3([Out]id3::mpg123id3v1^% v1, [Out]id3::mpg123id3v2^% v2) { mpg123_id3v1* pv1; mpg123_id3v2* pv2; // doc says "pv1 and pv2 may be set to NULL when there is no corresponding data." // they may also be set to point to empty structure... int ret = ::mpg123_id3(mh, &pv1, &pv2); v1 = gcnew mpg123clr::id3::mpg123id3v1(pv1); v2 = gcnew mpg123clr::id3::mpg123id3v2(pv2); return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_icy([Out]IntPtr% icy_meta) { char* _icy_meta; int ret = ::mpg123_icy(mh, &_icy_meta); icy_meta = (IntPtr) _icy_meta; return (mpg123clr::mpg::ErrorCode) ret; } array^ mpg123clr::mpg123::mpg123_icy2utf8(IntPtr icy_text) { // TODO: Do we really need this? // char* putf8 = mpg123_icy2utf8(const_cast(reinterpret_cast(icy_text.ToPointer()))); // Or is this adequate? char* putf8 = ::mpg123_icy2utf8((const char*)(void*) icy_text); // WARN 4267 - clr limited to 32bit-length-size arrays!! array^ ary = gcnew array((int)strlen(putf8)); Marshal::Copy((IntPtr)putf8, ary, 0, ary->Length); free(putf8); return ary; } #pragma endregion -Metadata Handling #pragma region Low Level I/O mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_replace_buffer(IntPtr data, size_t size) { // "data" buffer should be fixed BEFORE calling this function. // TODO: to cast or not to cast?? // return (mpg123clr::mpg::ErrorCode) mpg123_replace_buffer(mh, reinterpret_cast(data.ToPointer()), size); return (mpg123clr::mpg::ErrorCode) ::mpg123_replace_buffer(mh, (unsigned char*)(void*) data, size); } size_t mpg123clr::mpg123::mpg123_outblock(void) { return ::mpg123_outblock(mh); } void mpg123clr::mpg123::_ReplaceReader(void) { if ((readDel == r_readDel) && (seekDel == r_seekDel) && (readHDel == r_readHDel) && (seekDel == r_seekHDel) && (cleanHDel == r_cleanHDel)) return; // readDel and seekDel are "keep alive" fields that prevent GC of the delegates. // the underlying function pointers no not require "pinning". // Note: most examples use GCHandle::Alloc to "pin" the delegate but this is usually to prevent GC // outside of function scope and is not strictly necessary here since readDel and seekDel never go // out of scope. // See MS: c++, How to: Marshal Callbacks and Delegates Using C++ Interop // for more details. readDel = r_readDel; seekDel = r_seekDel; readHDel = r_readHDel; seekHDel = r_seekHDel; cleanHDel = r_cleanHDel; // just for clarity typedef off_t (_cdecl* SEEKCB)(int, off_t, int); typedef ssize_t (_cdecl* READCB)(int, void*, size_t); typedef off_t (_cdecl* HSEEKCB)(void*, off_t, int); typedef ssize_t (_cdecl* HREADCB)(void*, void*, size_t); typedef void (_cdecl* HCLEANCB)(void*); // NOTE: GetFunctionPointerForDelegate doesn't like nullptr // NOTE: I'm not suggesting that replace_reader and replace_reader_handle should be // intermingled, just trying to maintain a clean stack if (!useHandleReplacement) { if (lastReplacementWasHandle) ::mpg123_replace_reader_handle(mh, nullptr, nullptr, nullptr); ::mpg123_replace_reader( mh, (readDel != nullptr) ? (READCB)(Marshal::GetFunctionPointerForDelegate(readDel)).ToPointer() : nullptr, (seekDel != nullptr) ? (SEEKCB)(Marshal::GetFunctionPointerForDelegate(seekDel)).ToPointer() : nullptr ); } else { if (!lastReplacementWasHandle) // can give redundant call on first use - microscopically inefficient - not catastrophic ::mpg123_replace_reader(mh, nullptr, nullptr); ::mpg123_replace_reader_handle( mh, (readHDel != nullptr) ? (HREADCB)(Marshal::GetFunctionPointerForDelegate(readHDel)).ToPointer() : nullptr, (seekHDel != nullptr) ? (HSEEKCB)(Marshal::GetFunctionPointerForDelegate(seekHDel)).ToPointer() : nullptr, (cleanHDel != nullptr) ? (HCLEANCB)(Marshal::GetFunctionPointerForDelegate(cleanHDel)).ToPointer() : nullptr ); } lastReplacementWasHandle = useHandleReplacement; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_replace_reader(ReadDelegate^ r_read, SeekDelegate^ r_lseek) { // save temporary delegates to be implemented at next 'Open' r_readDel = r_read; r_seekDel = r_lseek; r_readHDel = nullptr; r_seekHDel = nullptr; r_cleanHDel = nullptr; useHandleReplacement = false; return mpg123clr::mpg::ErrorCode::ok; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_replace_reader_handle(ReadHandleDelegate^ rh_read, SeekHandleDelegate^ rh_lseek, CleanupHandleDelegate^ rh_clean) { // save temporary delegates to be implemented at next 'Open' r_readHDel = rh_read; r_seekHDel = rh_lseek; r_cleanHDel = rh_clean; r_readDel = nullptr; r_seekDel = nullptr; useHandleReplacement = true; return mpg123clr::mpg::ErrorCode::ok; } #pragma endregion -Low Level I/O #pragma region MS Unicode Extension mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_topen(String^ path) { _ReplaceReader(); // mpg123_tOpen will replace its own reader, this is just for consistency const _TCHAR* chars = (const _TCHAR*)(Marshal::StringToHGlobalUni(path)).ToPointer(); int ret = ::mpg123_topen(mh, chars); Marshal::FreeHGlobal(IntPtr((void*)chars)); return (mpg123clr::mpg::ErrorCode) ret; } mpg123clr::mpg::ErrorCode mpg123clr::mpg123::mpg123_tclose(void) { // Not sure if t_close calls Cleanup (it shouldn't since t_open substitutes its own readers) int ret = ::mpg123_tclose(mh); // Fairly sure replace_reader_handle, topen and tclose are incompatible, just for consistency // See GCHandle.Free - caller must ensure Free called only once for a given handle. if (userObjectHandle.IsAllocated) userObjectHandle.Free(); return (mpg123clr::mpg::ErrorCode) ret; } #pragma endregion -MS Unicode Extension