aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/General/gen_ml/MusicID.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/General/gen_ml/MusicID.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/General/gen_ml/MusicID.cpp')
-rw-r--r--Src/Plugins/General/gen_ml/MusicID.cpp658
1 files changed, 658 insertions, 0 deletions
diff --git a/Src/Plugins/General/gen_ml/MusicID.cpp b/Src/Plugins/General/gen_ml/MusicID.cpp
new file mode 100644
index 00000000..e55ee46a
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/MusicID.cpp
@@ -0,0 +1,658 @@
+#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, &params, 0, 0, &ret);
+ else
+ blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, &params, 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, &params, 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, &params, 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, &params, 0, 0, &ret);
+ else
+ blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, &params, 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, &params, 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, &params, 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 \ No newline at end of file