123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- #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
|