658 lines
16 KiB
C++
658 lines
16 KiB
C++
|
#include "main.h"
|
||
|
|
||
|
#include "MusicID.h"
|
||
|
#include <vector>
|
||
|
#include "../nu/AutoLock.h"
|
||
|
|
||
|
#include <atlbase.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "api__gen_ml.h"
|
||
|
|
||
|
static Nullsoft::Utility::LockGuard musicIDGuard;
|
||
|
|
||
|
// {113D413A-5D1F-4f4c-8AB7-5BDED46033A4}
|
||
|
static const GUID developerConfigGroupGUID=
|
||
|
{ 0x113d413a, 0x5d1f, 0x4f4c, { 0x8a, 0xb7, 0x5b, 0xde, 0xd4, 0x60, 0x33, 0xa4 } };
|
||
|
|
||
|
class MyEventHandler;
|
||
|
|
||
|
#ifndef IGNORE_API_GRACENOTE
|
||
|
|
||
|
static void TestAddRef(IDispatch *d)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
ULONG l = d->AddRef();
|
||
|
#ifdef _DEBUG
|
||
|
char t[55] = {0};
|
||
|
sprintf(t, "+ %p %x\n", d, l);
|
||
|
OutputDebugStringA(t);
|
||
|
#endif
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void TestRelease(IDispatch *d)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
ULONG l = d->Release();
|
||
|
#ifdef _DEBUG
|
||
|
char t[55] = {0};
|
||
|
sprintf(t, "- %p %x\n", d, l);
|
||
|
OutputDebugStringA(t);
|
||
|
#endif
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static IConnectionPoint *GetConnectionPoint(IUnknown *punk, REFIID riid)
|
||
|
{
|
||
|
if (!punk)
|
||
|
return 0;
|
||
|
|
||
|
IConnectionPointContainer *pcpc;
|
||
|
IConnectionPoint *pcp = 0;
|
||
|
|
||
|
HRESULT hr = punk->QueryInterface(IID_IConnectionPointContainer, (void **) & pcpc);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pcpc->FindConnectionPoint(riid, &pcp);
|
||
|
pcpc->Release();
|
||
|
}
|
||
|
punk->Release();
|
||
|
return pcp;
|
||
|
}
|
||
|
|
||
|
// TODO: implement proper reference count so we don't leak the event handler & musicID objects
|
||
|
|
||
|
static HANDLE DuplicateCurrentThread()
|
||
|
{
|
||
|
HANDLE fakeHandle = GetCurrentThread();
|
||
|
HANDLE copiedHandle = 0;
|
||
|
HANDLE processHandle = GetCurrentProcess();
|
||
|
DuplicateHandle(processHandle, fakeHandle, processHandle, &copiedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||
|
return copiedHandle;
|
||
|
}
|
||
|
|
||
|
static HRESULT FillTag(ICddbFileInfo *info, BSTR filename)
|
||
|
{
|
||
|
ICddbID3TagPtr infotag = NULL;
|
||
|
infotag.CreateInstance(CLSID_CddbID3Tag);
|
||
|
|
||
|
ICddbFileTag2_5Ptr tag2_5 = NULL;
|
||
|
infotag->QueryInterface(&tag2_5);
|
||
|
itemRecordW *record = 0;
|
||
|
|
||
|
if (AGAVE_API_MLDB)
|
||
|
record = AGAVE_API_MLDB->GetFile(filename);
|
||
|
if (record && infotag && tag2_5)
|
||
|
{
|
||
|
wchar_t itemp[64] = {0};
|
||
|
if (record->artist)
|
||
|
infotag->put_LeadArtist(record->artist);
|
||
|
|
||
|
if (record->album)
|
||
|
infotag->put_Album(record->album);
|
||
|
|
||
|
if (record->title)
|
||
|
infotag->put_Title(record->title);
|
||
|
|
||
|
if (record->genre)
|
||
|
infotag->put_Genre(record->genre);
|
||
|
|
||
|
if (record->track > 0)
|
||
|
infotag->put_TrackPosition(_itow(record->track, itemp, 10));
|
||
|
|
||
|
// TODO: if (record->tracks > 0)
|
||
|
if (record->year > 0)
|
||
|
infotag->put_Year(_itow(record->year, itemp, 10));
|
||
|
|
||
|
if (record->publisher)
|
||
|
infotag->put_Label(record->publisher);
|
||
|
|
||
|
/*
|
||
|
if (GetFileInfo(filename, L"ISRC", meta, 512) && meta[0])
|
||
|
infotag->put_ISRC(meta);
|
||
|
*/
|
||
|
|
||
|
if (record->disc > 0)
|
||
|
infotag->put_PartOfSet(_itow(record->disc, itemp, 10));
|
||
|
|
||
|
if (record->albumartist)
|
||
|
tag2_5->put_DiscArtist(record->albumartist);
|
||
|
|
||
|
if (record->composer)
|
||
|
tag2_5->put_Composer(record->composer);
|
||
|
|
||
|
if (record->length > 0)
|
||
|
tag2_5->put_LengthMS(_itow(record->length*1000, itemp, 10));
|
||
|
|
||
|
if (record->bpm > 0)
|
||
|
infotag->put_BeatsPerMinute(_itow(record->bpm, itemp, 10));
|
||
|
|
||
|
/*
|
||
|
if (GetFileInfo(filename, L"conductor", meta, 512) && meta[0])
|
||
|
tag2_5->put_Conductor(meta);
|
||
|
*/
|
||
|
AGAVE_API_MLDB->FreeRecord(record);
|
||
|
}
|
||
|
|
||
|
if (info) info->put_Tag(infotag);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
struct Blah
|
||
|
{
|
||
|
IDispatch *callback;
|
||
|
CComBSTR filename, tagID, artist;
|
||
|
};
|
||
|
|
||
|
static VOID CALLBACK InvokeAPC(ULONG_PTR param)
|
||
|
{
|
||
|
Blah *blah = (Blah *)param;
|
||
|
VARIANT arguments[2];
|
||
|
VariantInit(&arguments[0]);
|
||
|
arguments[0].vt = VT_BSTR;
|
||
|
arguments[0].bstrVal = blah->tagID;
|
||
|
|
||
|
VariantInit(&arguments[1]);
|
||
|
arguments[1].vt = VT_BSTR;
|
||
|
arguments[1].bstrVal = blah->filename;
|
||
|
|
||
|
DISPPARAMS params;
|
||
|
params.cArgs = 2;
|
||
|
params.cNamedArgs = 0;
|
||
|
params.rgdispidNamedArgs = 0;
|
||
|
params.rgvarg = arguments;
|
||
|
unsigned int ret;
|
||
|
|
||
|
if (!ieDisableSEH)
|
||
|
{
|
||
|
ieDisableSEH = AGAVE_API_CONFIG->GetItem(developerConfigGroupGUID, L"no_ieseh");
|
||
|
}
|
||
|
|
||
|
OLECHAR *setID = L"SetID", *setArtist = L"SetArtist", *onFinish=L"OnFinish";
|
||
|
DISPID dispid;
|
||
|
if (ieDisableSEH->GetBool() == false)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setID, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||
|
blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
else
|
||
|
blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
|
||
|
arguments[0].bstrVal = blah->artist;
|
||
|
if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setArtist, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||
|
blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
|
||
|
arguments[0].bstrVal = blah->filename;
|
||
|
params.cArgs=1;
|
||
|
if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &onFinish, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||
|
blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setID, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||
|
blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
else
|
||
|
blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
|
||
|
arguments[0].bstrVal = blah->artist;
|
||
|
if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setArtist, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||
|
blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
|
||
|
arguments[0].bstrVal = blah->filename;
|
||
|
params.cArgs=1;
|
||
|
if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &onFinish, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||
|
blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||
|
}
|
||
|
|
||
|
delete blah;
|
||
|
}
|
||
|
|
||
|
class MyEventHandler : public _ICDDBMusicIDManagerEvents
|
||
|
{
|
||
|
public:
|
||
|
MyEventHandler(BSTR _filename, IDispatch *_callback, HANDLE _handle) : callback(_callback), threadHandle(_handle)
|
||
|
{
|
||
|
filename = SysAllocString(_filename);
|
||
|
refCount = 1;
|
||
|
TestAddRef(callback);
|
||
|
}
|
||
|
|
||
|
BSTR filename;
|
||
|
ICDDBMusicIDManager3 *musicID;
|
||
|
HANDLE threadHandle;
|
||
|
IDispatch *callback;
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE AddRef(void)
|
||
|
{
|
||
|
return InterlockedIncrement(&refCount);
|
||
|
}
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE Release(void)
|
||
|
{
|
||
|
LONG lRef = InterlockedDecrement(&refCount);
|
||
|
if (lRef == 0)
|
||
|
delete this;
|
||
|
return lRef;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
~MyEventHandler()
|
||
|
{
|
||
|
SysFreeString(filename);
|
||
|
CloseHandle(threadHandle);
|
||
|
TestRelease(callback);
|
||
|
}
|
||
|
LONG refCount;
|
||
|
|
||
|
STDMETHODIMP STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject)
|
||
|
{
|
||
|
if (!ppvObject)
|
||
|
return E_POINTER;
|
||
|
|
||
|
else if (IsEqualIID(riid, __uuidof(_ICDDBMusicIDManagerEvents)))
|
||
|
*ppvObject = (_ICDDBMusicIDManagerEvents *)this;
|
||
|
else if (IsEqualIID(riid, IID_IDispatch))
|
||
|
*ppvObject = (IDispatch *)this;
|
||
|
else if (IsEqualIID(riid, IID_IUnknown))
|
||
|
*ppvObject = this;
|
||
|
else
|
||
|
{
|
||
|
*ppvObject = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
|
||
|
{
|
||
|
switch (dispid)
|
||
|
{
|
||
|
case 1: // OnTrackIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long* Abort
|
||
|
break;
|
||
|
case 2: // OnAlbumIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long current_file, long total_files, long* Abort
|
||
|
{
|
||
|
//long *abort = pdispparams->rgvarg[0].plVal;
|
||
|
//long total_files = pdispparams->rgvarg[1].lVal;
|
||
|
//long current_file= pdispparams->rgvarg[2].lVal;
|
||
|
CddbMusicIDStatus status = (CddbMusicIDStatus)pdispparams->rgvarg[4].lVal;
|
||
|
BSTR filename = pdispparams->rgvarg[3].bstrVal;
|
||
|
}
|
||
|
break;
|
||
|
case 3: // OnTrackIDComplete, params: LONG match_code, ICddbFileInfo* pInfoIn, ICddbFileInfoList* pListOut
|
||
|
{
|
||
|
IDispatch *disp1 =pdispparams->rgvarg[0].pdispVal;
|
||
|
IDispatch *disp2 =pdispparams->rgvarg[1].pdispVal;
|
||
|
//long match_code = pdispparams->rgvarg[2].lVal;
|
||
|
ICddbFileInfoPtr pInfoIn;
|
||
|
ICddbFileInfoListPtr matchList;
|
||
|
disp1->QueryInterface(&matchList);
|
||
|
disp2->QueryInterface(&pInfoIn);
|
||
|
}
|
||
|
break;
|
||
|
case 4: // OnAlbumIDComplete, params: LONG match_code, ICddbFileInfoList* pListIn, ICddbFileInfoLists* pListsOut
|
||
|
{
|
||
|
IDispatch *disp1 =pdispparams->rgvarg[0].pdispVal;
|
||
|
IDispatch *disp2 =pdispparams->rgvarg[1].pdispVal;
|
||
|
//long match_code = pdispparams->rgvarg[2].lVal;
|
||
|
ICddbFileInfoListPtr pListIn;
|
||
|
ICddbFileInfoListsPtr pListsOut;
|
||
|
disp1->QueryInterface(&pListsOut);
|
||
|
disp2->QueryInterface(&pListIn);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 10: // OnGetFingerprintInfo
|
||
|
{
|
||
|
long *abort = pdispparams->rgvarg[0].plVal;
|
||
|
IDispatch *disp = pdispparams->rgvarg[1].pdispVal;
|
||
|
BSTR filename = pdispparams->rgvarg[2].bstrVal;
|
||
|
|
||
|
ICddbFileInfo *info;
|
||
|
disp->QueryInterface(&info);
|
||
|
return AGAVE_API_GRACENOTE->CreateFingerprint(musicID, AGAVE_API_DECODE, info, filename, abort);
|
||
|
}
|
||
|
break;
|
||
|
case 11: // OnGetTagInfo
|
||
|
{
|
||
|
//long *Abort = pdispparams->rgvarg[0].plVal;
|
||
|
IDispatch *disp = pdispparams->rgvarg[1].pdispVal;
|
||
|
BSTR filename = pdispparams->rgvarg[2].bstrVal;
|
||
|
|
||
|
ICddbFileInfo *info;
|
||
|
disp->QueryInterface(&info);
|
||
|
return FillTag(info, filename);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return DISP_E_MEMBERNOTFOUND;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
|
||
|
{
|
||
|
*rgdispid = DISPID_UNKNOWN;
|
||
|
return DISP_E_UNKNOWNNAME;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int FAR * pctinfo)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
static VOID CALLBACK ReleaseAPC(ULONG_PTR param)
|
||
|
{
|
||
|
MyEventHandler *handler = (MyEventHandler *)param;
|
||
|
handler->Release();
|
||
|
}
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
DISPATCH_MUSICID_GETID = 777,
|
||
|
};
|
||
|
|
||
|
|
||
|
static std::vector<MyEventHandler*> tracks;
|
||
|
static HANDLE musicid_killswitch=0, musicid_trigger=0;
|
||
|
static ThreadID *musicIdThread=0;
|
||
|
|
||
|
class MusicIDContext
|
||
|
{
|
||
|
public:
|
||
|
MusicIDContext()
|
||
|
{
|
||
|
musicID=0;
|
||
|
com_initted=false;
|
||
|
}
|
||
|
bool Init();
|
||
|
void Tick();
|
||
|
void Quit();
|
||
|
ICDDBMusicIDManager3 *musicID;
|
||
|
bool com_initted;
|
||
|
};
|
||
|
static MusicIDContext context;
|
||
|
|
||
|
bool MusicIDContext::Init()
|
||
|
{
|
||
|
if (SUCCEEDED(CoInitialize(0)))
|
||
|
com_initted=true;
|
||
|
|
||
|
musicID=AGAVE_API_GRACENOTE->GetMusicID();
|
||
|
return !!musicID;
|
||
|
}
|
||
|
|
||
|
void MusicIDContext::Tick()
|
||
|
{
|
||
|
musicIDGuard.Lock();
|
||
|
if (tracks.empty())
|
||
|
{
|
||
|
musicIDGuard.Unlock();
|
||
|
return;
|
||
|
}
|
||
|
MyEventHandler *track = tracks.front();
|
||
|
tracks.pop_front();
|
||
|
musicIDGuard.Unlock();
|
||
|
|
||
|
if (!musicID)
|
||
|
{
|
||
|
Blah *blah = new Blah;
|
||
|
blah->filename = SysAllocString(track->filename);
|
||
|
blah->tagID = L"";
|
||
|
blah->artist = L"";
|
||
|
blah->callback=track->callback;
|
||
|
|
||
|
QueueUserAPC(InvokeAPC, track->threadHandle, (ULONG_PTR)blah);
|
||
|
QueueUserAPC(ReleaseAPC, track->threadHandle, (ULONG_PTR)track);
|
||
|
return;
|
||
|
}
|
||
|
track->musicID = musicID;
|
||
|
IConnectionPoint *icp = GetConnectionPoint(musicID, DIID__ICDDBMusicIDManagerEvents);
|
||
|
|
||
|
DWORD m_dwCookie=0;
|
||
|
|
||
|
if (icp)
|
||
|
{
|
||
|
icp->Advise(static_cast<IDispatch *>(track), &m_dwCookie);
|
||
|
//icp->Release();
|
||
|
}
|
||
|
|
||
|
ICddbFileInfoPtr info;
|
||
|
info.CreateInstance(CLSID_CddbFileInfo);
|
||
|
if (info == 0)
|
||
|
{
|
||
|
QueueUserAPC(ReleaseAPC, track->threadHandle, (ULONG_PTR)track);
|
||
|
return ;
|
||
|
}
|
||
|
info->put_Filename(track->filename);
|
||
|
ICddbFileInfoListPtr matchList;
|
||
|
long match_code=666;
|
||
|
|
||
|
musicID->TrackID(info, MUSICID_RETURN_SINGLE|MUSICID_GET_TAG_FROM_APP|MUSICID_GET_FP_FROM_APP|MUSICID_PREFER_WF_MATCHES, &match_code, &matchList);
|
||
|
|
||
|
if (matchList)
|
||
|
{
|
||
|
long matchcount;
|
||
|
matchList->get_Count(&matchcount);
|
||
|
assert(matchcount==1);
|
||
|
for (int j = 1;j <= matchcount;j++)
|
||
|
{
|
||
|
ICddbFileInfoPtr match;
|
||
|
matchList->GetFileInfo(j, &match);
|
||
|
|
||
|
Blah *blah = new Blah;
|
||
|
|
||
|
match->get_Filename(&blah->filename);
|
||
|
ICddbFileTagPtr tag;
|
||
|
match->get_Tag(&tag);
|
||
|
tag->get_FileId(&blah->tagID);
|
||
|
tag->get_LeadArtist(&blah->artist);
|
||
|
blah->callback=track->callback;
|
||
|
if (blah->tagID && AGAVE_API_MLDB)
|
||
|
AGAVE_API_MLDB->SetField(blah->filename, "GracenoteFileID", blah->tagID);
|
||
|
|
||
|
QueueUserAPC(InvokeAPC, track->threadHandle, (ULONG_PTR)blah);
|
||
|
|
||
|
if (AGAVE_API_MLDB)
|
||
|
AGAVE_API_MLDB->Sync();
|
||
|
//TODO: optionally write metadata to file permanently
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (icp)
|
||
|
{
|
||
|
icp->Unadvise(m_dwCookie);
|
||
|
}
|
||
|
QueueUserAPC(ReleaseAPC, track->threadHandle, (ULONG_PTR)track);
|
||
|
}
|
||
|
|
||
|
void MusicIDContext::Quit()
|
||
|
{
|
||
|
if (musicID)
|
||
|
{
|
||
|
musicID->Shutdown();
|
||
|
musicID->Release();
|
||
|
musicID=0;
|
||
|
}
|
||
|
if (com_initted)
|
||
|
{
|
||
|
CoUninitialize();
|
||
|
com_initted=false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int MusicIDTickThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
|
||
|
{
|
||
|
MusicIDContext *context = (MusicIDContext *)user_data;
|
||
|
context->Tick();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int MusicIDInitThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
|
||
|
{
|
||
|
MusicIDContext *context = (MusicIDContext *)user_data;
|
||
|
if (context->Init())
|
||
|
{
|
||
|
AGAVE_API_THREADPOOL->AddHandle(musicIdThread, musicid_trigger, MusicIDTickThreadPoolFunc, user_data, id, 0);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int MusicIDQuitThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
|
||
|
{
|
||
|
MusicIDContext *context = (MusicIDContext *)user_data;
|
||
|
AGAVE_API_THREADPOOL->RemoveHandle(musicIdThread, musicid_trigger);
|
||
|
context->Quit();
|
||
|
SetEvent(musicid_killswitch);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT MusicIDCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
|
||
|
{
|
||
|
bool unknowns = false;
|
||
|
for (unsigned int i = 0;i != cNames;i++)
|
||
|
{
|
||
|
if (wcscmp(rgszNames[i], L"GetID") == 0)
|
||
|
rgdispid[i] = DISPATCH_MUSICID_GETID;
|
||
|
else
|
||
|
{
|
||
|
rgdispid[i] = DISPID_UNKNOWN;
|
||
|
unknowns = true;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
if (unknowns)
|
||
|
return DISP_E_UNKNOWNNAME;
|
||
|
else
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT MusicIDCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT MusicIDCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT MusicIDCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
|
||
|
{
|
||
|
if (dispid == DISPATCH_MUSICID_GETID)
|
||
|
{
|
||
|
if (pdispparams->cArgs != 2)
|
||
|
return DISP_E_BADPARAMCOUNT;
|
||
|
|
||
|
if (pdispparams->rgvarg[0].vt != VT_DISPATCH)
|
||
|
return DISP_E_TYPEMISMATCH;
|
||
|
|
||
|
if (pdispparams->rgvarg[1].vt != VT_BSTR)
|
||
|
return DISP_E_TYPEMISMATCH;
|
||
|
|
||
|
if (!AGAVE_API_GRACENOTE)
|
||
|
return E_FAIL;
|
||
|
|
||
|
if (!musicIdThread)
|
||
|
{
|
||
|
musicIDGuard.Lock();
|
||
|
if (!musicIdThread)
|
||
|
{
|
||
|
musicIdThread = AGAVE_API_THREADPOOL->ReserveThread(api_threadpool::FLAG_REQUIRE_COM_STA);
|
||
|
musicid_trigger = CreateSemaphore(0, 0, 65536, 0);
|
||
|
AGAVE_API_THREADPOOL->RunFunction(musicIdThread, MusicIDInitThreadPoolFunc, &context, 0, 0);
|
||
|
}
|
||
|
musicIDGuard.Unlock();
|
||
|
}
|
||
|
|
||
|
MyEventHandler *event = new MyEventHandler(pdispparams->rgvarg[1].bstrVal, pdispparams->rgvarg[0].pdispVal, DuplicateCurrentThread());
|
||
|
|
||
|
musicIDGuard.Lock();
|
||
|
tracks.push_back(event);
|
||
|
musicIDGuard.Unlock();
|
||
|
ReleaseSemaphore(musicid_trigger, 1, 0);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return DISP_E_MEMBERNOTFOUND;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP MusicIDCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
|
||
|
{
|
||
|
if (!ppvObject)
|
||
|
return E_POINTER;
|
||
|
|
||
|
else if (IsEqualIID(riid, IID_IDispatch))
|
||
|
*ppvObject = (IDispatch *)this;
|
||
|
else if (IsEqualIID(riid, IID_IUnknown))
|
||
|
*ppvObject = this;
|
||
|
else
|
||
|
{
|
||
|
*ppvObject = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
ULONG MusicIDCOM::AddRef(void)
|
||
|
{
|
||
|
// return ++m_cRefs;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ULONG MusicIDCOM::Release(void)
|
||
|
{ /*
|
||
|
if (--m_cRefs)
|
||
|
return m_cRefs;
|
||
|
|
||
|
delete this;
|
||
|
return 0;*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void MusicIDCOM::Quit()
|
||
|
{
|
||
|
if (musicIdThread)
|
||
|
{
|
||
|
musicid_killswitch = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
AGAVE_API_THREADPOOL->RunFunction(musicIdThread, MusicIDQuitThreadPoolFunc, &context, 0, 0);
|
||
|
if (NULL != musicid_killswitch)
|
||
|
{
|
||
|
WaitForSingleObject(musicid_killswitch, INFINITE);
|
||
|
CloseHandle(musicid_killswitch);
|
||
|
}
|
||
|
|
||
|
CloseHandle(musicid_trigger);
|
||
|
AGAVE_API_THREADPOOL->ReleaseThread(musicIdThread);
|
||
|
}
|
||
|
}
|
||
|
#endif
|