/*
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<int> _val = &val;
pin_ptr<double> _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<String^>^ 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<String^> ^ str = gcnew array<String^>(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<String^>^ mpg123clr::mpg123::mpg123_decoders(void)
{
return StringArrayFromPtr(::mpg123_decoders());
}
array<String^>^ 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<long>^ mpg123clr::mpg123::mpg123_rates(void)
{
size_t number;
const long* list;
::mpg123_rates(&list, &number);
array<long>^ rList = gcnew array<long>((int)number);
int index = 0;
// walk the array, extracting the rates
while (index < (int)number){ rList[index++] = *(list++); }
return rList;
}
array<mpg123clr::mpg::enc>^ 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<mpg123clr::mpg::enc>^ rList = gcnew array<mpg123clr::mpg::enc>((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<int> _rate = &rate;
pin_ptr<mpg123clr::mpg::channelcount> _chan = &channels;
pin_ptr<mpg123clr::mpg::enc> _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<unsigned char>^ buffer, [Out] size_t% count)
{
pin_ptr<size_t> _count = &count;
pin_ptr<unsigned char> _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<unsigned char>^ buffer, size_t offset, size_t size, [Out] size_t% count)
{
pin_ptr<size_t> _count = &count;
// WARN 4267 - clr limited to 32bit-length-size arrays!!
pin_ptr<unsigned char> _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<unsigned char>^ inbuffer, size_t size)
{
pin_ptr<unsigned char> _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<unsigned char>^ inbuffer, size_t insize, array<unsigned char>^ outbuffer, size_t outsize, [Out] size_t% count)
{
pin_ptr<size_t> _count = &count;
pin_ptr<const unsigned char> _inptr = &inbuffer[0];
pin_ptr<unsigned char> _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<size_t> _count = &count;
pin_ptr<off_t> _num = #
pin_ptr<IntPtr> _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<size_t> _count = &count;
pin_ptr<off_t> _num = #
pin_ptr<IntPtr> _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<int> _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<long long>^% 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<int> _step = &step;
// int ret = mpg123_index(mh, &_list, (off_t*)_step, &_count);
// index = gcnew array<int>((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<long long>((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<size_t> _fill = &fill;
pin_ptr<IntPtr> _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<int> _currentframe = ¤tframe;
// pin_ptr<int> _framesleft = &framesleft;
pin_ptr<double> _currentseconds = ¤tseconds;
pin_ptr<double> _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<double> _basevol = &basevol;
pin_ptr<double> _really = &really;
pin_ptr<double> _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<mpg123_frameinfo*>((int)pp));
// int ret = mpg123_info(mh, static_cast<mpg123_frameinfo*>((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<mpg123clr::mpg::mpeg_version> _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<int> _val = &val;
pin_ptr<double> _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<unsigned char>^ mpg123clr::mpg123::mpg123_icy2utf8(IntPtr icy_text)
{
// TODO: Do we really need this?
// char* putf8 = mpg123_icy2utf8(const_cast<char*>(reinterpret_cast<char*>(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<unsigned char>^ ary = gcnew array<unsigned char>((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<unsigned char*>(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