NowPlayingPlugin:

- Fixed that PlayerName=, TrackChangeAction=, and DisableLeadingZero= were global (i.e. only usable from the first loaded skin)
- Code refactoring and cleanup
This commit is contained in:
Birunthan Mohanathas 2011-06-17 19:07:06 +00:00
parent 67cc4c7c6c
commit 6aa004eb22
26 changed files with 1251 additions and 1714 deletions

View File

@ -0,0 +1,332 @@
/*
Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "StdAfx.h"
#include "Cover.h"
extern std::wstring g_CachePath;
/*
** GetCover
**
** Default implementation for getting cover.
**
*/
void CCover::GetCover(const std::wstring& artist, const std::wstring& title, const std::wstring& file, std::wstring& target)
{
if (!GetCachedCover(artist, title, target))
{
TagLib::FileRef fr(file.c_str());
if (fr.isNull() || !fr.tag() || !GetEmbeddedCover(fr, target))
{
std::wstring trackFolder = GetFileFolder(file);
if (!GetLocalCover(L"cover", trackFolder, target) &&
!GetLocalCover(L"folder", trackFolder, target))
{
// Nothing found
target.clear();
}
}
}
}
/*
** GetCachedArt
**
** Checks if cover art is in cache.
**
*/
bool CCover::GetCachedCover(const std::wstring& artist, const std::wstring& title, std::wstring& target)
{
target = g_CachePath;
if (artist.empty() || title.empty())
{
target += L"temp.art";
}
else
{
// Otherwise, save it as "Artist - Title.art"
std::wstring name = artist;
name += L" - ";
name += title;
// Replace reserved chars with _
std::wstring::size_type pos = 0;
while ((pos = name.find_first_of(L"\\/:*?\"<>|", pos)) != std::wstring::npos) name[pos] = L'_';
target += name;
target += L".art";
if (_waccess(target.c_str(), 0) == 0)
{
// Art found in cache
return true;
}
}
return false;
}
/*
** GetLocalArt
**
** Attemps to find local cover art in various formats.
**
*/
bool CCover::GetLocalCover(std::wstring filename, const std::wstring& folder, std::wstring& target)
{
std::wstring testPath = folder;
testPath += filename;
testPath += L".";
std::wstring::size_type origLen = testPath.length();
const int extCount = 4;
LPCTSTR extName[extCount] = { L"jpg", L"jpeg", L"png", L"bmp" };
for (int i = 0; i < extCount; ++i)
{
testPath += extName[i];
if (_waccess(testPath.c_str(), 0) == 0)
{
target = testPath;
return true;
}
else
{
// Get rid of the added extension
testPath.resize(origLen);
}
}
return false;
}
/*
** GetEmbeddedArt
**
** Attempts to extract cover art from audio files.
**
*/
bool CCover::GetEmbeddedCover(const TagLib::FileRef& fr, std::wstring& target)
{
bool found = false;
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fr.file()))
{
if (file->ID3v2Tag())
{
found = ExtractID3(file->ID3v2Tag(), target);
}
if (!found && file->APETag())
{
found = ExtractAPE(file->APETag(), target);
}
}
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fr.file()))
{
if (file->tag())
{
found = ExtractMP4(file, target);
}
}
else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fr.file()))
{
found = ExtractFLAC(file, target);
if (!found && file->ID3v2Tag())
{
found = ExtractID3(file->ID3v2Tag(), target);
}
}
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fr.file()))
{
found = ExtractASF(file, target);
}
else if (TagLib::APE::File* file = dynamic_cast<TagLib::APE::File*>(fr.file()))
{
if (file->APETag())
{
found = ExtractAPE(file->APETag(), target);
}
}
else if (TagLib::MPC::File* file = dynamic_cast<TagLib::MPC::File*>(fr.file()))
{
if (file->APETag())
{
found = ExtractAPE(file->APETag(), target);
}
}
else if (TagLib::WavPack::File* file = dynamic_cast<TagLib::WavPack::File*>(fr.file()))
{
if (file->APETag())
{
found = ExtractAPE(file->APETag(), target);
}
}
return found;
}
/*
** GetFileFolder
**
** Returns path without filename.
**
*/
std::wstring CCover::GetFileFolder(const std::wstring& file)
{
std::wstring::size_type pos = file.find_last_of(L'\\');
if (pos != std::wstring::npos)
{
return file.substr(0, ++pos);
}
return file;
}
/*
** ExtractAPE
**
** Extracts cover art embedded in APE tags.
**
*/
bool CCover::ExtractAPE(TagLib::APE::Tag* tag, const std::wstring& target)
{
const TagLib::APE::ItemListMap& listMap = tag->itemListMap();
if (listMap.contains("COVER ART (FRONT)"))
{
const TagLib::ByteVector nullStringTerminator(1, 0);
TagLib::ByteVector item = listMap["COVER ART (FRONT)"].value();
int pos = item.find(nullStringTerminator); // Skip the filename
if (++pos > 0)
{
const TagLib::ByteVector& pic = item.mid(pos);
return WriteCover(pic, target);
}
}
return false;
}
/*
** ExtractID3
**
** Extracts cover art embedded in ID3v2 tags.
**
*/
bool CCover::ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target)
{
const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC");
if (!frameList.isEmpty())
{
// Grab the first image
TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front());
return WriteCover(frame->picture(), target);
}
return false;
}
/*
** ExtractASF
**
** Extracts cover art embedded in ASF/WMA files.
**
*/
bool CCover::ExtractASF(TagLib::ASF::File* file, const std::wstring& target)
{
const TagLib::ASF::AttributeListMap& attrListMap = file->tag()->attributeListMap();
if (attrListMap.contains("WM/Picture"))
{
const TagLib::ASF::AttributeList& attrList = attrListMap["WM/Picture"];
if (!attrList.isEmpty())
{
// Let's grab the first cover. TODO: Check/loop for correct type
TagLib::ASF::Picture wmpic = attrList[0].toPicture();
if (wmpic.isValid())
{
return WriteCover(wmpic.picture(), target);
}
}
}
return false;
}
/*
** ExtractFLAC
**
** Extracts cover art embedded in FLAC files.
**
*/
bool CCover::ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target)
{
const TagLib::List<TagLib::FLAC::Picture*>& picList = file->pictureList();
if (!picList.isEmpty())
{
// Let's grab the first image
TagLib::FLAC::Picture* pic = picList[0];
return WriteCover(pic->data(), target);
}
return false;
}
/*
** ExtractMP4
**
** Extracts cover art embedded in MP4-like files.
**
*/
bool CCover::ExtractMP4(TagLib::MP4::File* file, const std::wstring& target)
{
TagLib::MP4::Tag* tag = file->tag();
if (tag->itemListMap().contains("covr"))
{
TagLib::MP4::CoverArtList coverList = tag->itemListMap()["covr"].toCoverArtList();
if (coverList[0].data().size() > 0)
{
return WriteCover(coverList[0].data(), target);
}
}
return false;
}
/*
** WriteCover
**
** Write cover data to file.
**
*/
bool CCover::WriteCover(const TagLib::ByteVector& data, const std::wstring& target)
{
bool written = false;
FILE* f = _wfopen(target.c_str(), L"wb");
if (f)
{
written = (fwrite(data.data(), 1, data.size(), f) == data.size());
fclose(f);
}
return written;
}

View File

@ -0,0 +1,58 @@
/*
Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __COVER_H__
#define __COVER_H__
#include "apefile.h"
#include "apetag.h"
#include "asffile.h"
#include "attachedpictureframe.h"
#include "commentsframe.h"
#include "flacfile.h"
#include "id3v1genres.h"
#include "id3v2tag.h"
#include "mpcfile.h"
#include "mp4file.h"
#include "mpegfile.h"
#include "tag.h"
#include "taglib.h"
#include "textidentificationframe.h"
#include "tstring.h"
#include "vorbisfile.h"
#include "wavpackfile.h"
class CCover
{
public:
static void GetCover(const std::wstring& artist, const std::wstring& title, const std::wstring& file, std::wstring& target);
static bool GetCachedCover(const std::wstring& artist, const std::wstring& title, std::wstring& target);
static bool GetLocalCover(std::wstring filename, const std::wstring& folder, std::wstring& target);
static bool GetEmbeddedCover(const TagLib::FileRef& fr, std::wstring& target);
static std::wstring GetFileFolder(const std::wstring& file);
private:
static bool ExtractAPE(TagLib::APE::Tag* tag, const std::wstring& target);
static bool ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target);
static bool ExtractASF(TagLib::ASF::File* file, const std::wstring& target);
static bool ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target);
static bool ExtractMP4(TagLib::MP4::File* file, const std::wstring& target);
static bool WriteCover(const TagLib::ByteVector& data, const std::wstring& target);
};
#endif

View File

@ -37,31 +37,11 @@ CPlayer* g_Winamp = NULL;
CPlayer* g_WLM = NULL; CPlayer* g_WLM = NULL;
CPlayer* g_WMP = NULL; CPlayer* g_WMP = NULL;
static MeasureMap g_Values; static std::map<UINT, ChildMeasure*> g_Measures;
static bool g_DisableLeazingZero = false; static bool g_DisableLeazingZero = false;
std::wstring g_CachePath; std::wstring g_CachePath;
std::wstring g_SettingsFile; std::wstring g_SettingsFile;
void SecondsToTime(UINT seconds, WCHAR* buffer)
{
int hours = seconds;
int mins = seconds;
hours /= 3600;
mins %= 3600;
int secs = mins;
mins /= 60;
secs %= 60;
if (hours)
{
_snwprintf_s(buffer, 32, _TRUNCATE, g_DisableLeazingZero ? L"%i:%02i:%02i" : L"%02i:%02i:%02i", hours, mins, secs);
}
else
{
_snwprintf_s(buffer, 32, _TRUNCATE, g_DisableLeazingZero ? L"%i:%02i" : L"%02i:%02i", mins, secs);
}
}
/* /*
** Initialize ** Initialize
** **
@ -70,7 +50,7 @@ void SecondsToTime(UINT seconds, WCHAR* buffer)
*/ */
UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id) UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
if (g_Values.empty()) if (g_Measures.empty())
{ {
// Get path to temporary folder (for cover art cache) // Get path to temporary folder (for cover art cache)
WCHAR buffer[MAX_PATH]; WCHAR buffer[MAX_PATH];
@ -87,10 +67,17 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
g_SettingsFile = PluginBridge(L"getvariable", str.c_str()); g_SettingsFile = PluginBridge(L"getvariable", str.c_str());
g_SettingsFile += L"Plugins.ini"; g_SettingsFile += L"Plugins.ini";
} }
else
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Unable to get path to Plugins.ini.");
}
} }
// Data is stored in two structs: ChildMeasure and ParentMeasure. ParentMeasure is created for measures
// with PlayerName=someplayer. ChildMeasure is created for all measures and points to ParentMeasure as
// referenced in PlayerName=[section].
ChildMeasure* child = new ChildMeasure;
UINT maxValue = 0; UINT maxValue = 0;
MeasureData* data = new MeasureData;
// Read settings from the ini-file // Read settings from the ini-file
LPCTSTR str = ReadConfigString(section, L"PlayerName", NULL); LPCTSTR str = ReadConfigString(section, L"PlayerName", NULL);
@ -98,36 +85,43 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
if (str[0] == L'[') if (str[0] == L'[')
{ {
// PlayerName starts with [ so use referenced section
int len = wcslen(str) - 2; int len = wcslen(str) - 2;
if (len > 0) if (len > 0)
{ {
MeasureMap::iterator it = g_Values.begin(); std::map<UINT, ChildMeasure*>::iterator it = g_Measures.begin();
for ( ; it != g_Values.end(); ++it) for ( ; it != g_Measures.end(); ++it)
{ {
if (wcsncmp(&str[1], it->second->section.c_str(), len) == 0 && if (wcsncmp(&str[1], it->second->parent->name.c_str(), len) == 0 &&
wcscmp(iniFile, it->second->iniFile.c_str()) == 0) wcscmp(iniFile, it->second->parent->iniFile.c_str()) == 0)
{ {
// Use same player instance as pointed section // Use same ParentMeasure as referenced section
data->player = it->second->player; child->parent = it->second->parent;
++child->parent->childCount;
break;
} }
} }
if (!data->player) if (!child->parent)
{ {
// The referenced section doesn't exist
std::wstring error = L"NowPlayingPlugin: PlayerName="; std::wstring error = L"NowPlayingPlugin: PlayerName=";
error += str; error += str;
error += L" in section ["; error += L" in [";
error += section; error += section;
error += L"] does not exist."; error += L"] does not exist.";
LSLog(LOG_WARNING, L"Rainmeter", error.c_str()); LSLog(LOG_WARNING, L"Rainmeter", error.c_str());
delete child;
return maxValue; return maxValue;
} }
} }
} }
else else
{ {
data->section = section; // ParentMeasure is created when PlayerName is an actual player (and not a reference)
data->iniFile = iniFile; ParentMeasure* parent = new ParentMeasure;
parent->name = section;
parent->iniFile = iniFile;
if (_wcsicmp(L"AIMP", str) == 0) if (_wcsicmp(L"AIMP", str) == 0)
{ {
@ -135,7 +129,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_AIMP = new CPlayerAIMP(); g_AIMP = new CPlayerAIMP();
} }
data->player = g_AIMP; parent->player = g_AIMP;
} }
else if (_wcsicmp(L"CAD", str) == 0) else if (_wcsicmp(L"CAD", str) == 0)
{ {
@ -143,7 +137,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_CAD = new CPlayerCAD(); g_CAD = new CPlayerCAD();
} }
data->player = g_CAD; parent->player = g_CAD;
} }
else if (_wcsicmp(L"foobar2000", str) == 0) else if (_wcsicmp(L"foobar2000", str) == 0)
{ {
@ -151,7 +145,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_Foobar = new CPlayerFoobar(); g_Foobar = new CPlayerFoobar();
} }
data->player = g_Foobar; parent->player = g_Foobar;
} }
else if (_wcsicmp(L"iTunes", str) == 0) else if (_wcsicmp(L"iTunes", str) == 0)
{ {
@ -159,7 +153,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_iTunes = new CPlayerITunes(); g_iTunes = new CPlayerITunes();
} }
data->player = g_iTunes; parent->player = g_iTunes;
} }
else if (_wcsicmp(L"MediaMonkey", str) == 0) else if (_wcsicmp(L"MediaMonkey", str) == 0)
{ {
@ -167,7 +161,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_Winamp = new CPlayerWinamp(WA_MEDIAMONKEY); g_Winamp = new CPlayerWinamp(WA_MEDIAMONKEY);
} }
data->player = g_Winamp; parent->player = g_Winamp;
} }
else if (_wcsicmp(L"Spotify", str) == 0) else if (_wcsicmp(L"Spotify", str) == 0)
{ {
@ -175,7 +169,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_Spotify = new CPlayerSpotify(); g_Spotify = new CPlayerSpotify();
} }
data->player = g_Spotify; parent->player = g_Spotify;
} }
else if (_wcsicmp(L"WinAmp", str) == 0) else if (_wcsicmp(L"WinAmp", str) == 0)
{ {
@ -183,7 +177,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_Winamp = new CPlayerWinamp(WA_WINAMP); g_Winamp = new CPlayerWinamp(WA_WINAMP);
} }
data->player = g_Winamp; parent->player = g_Winamp;
} }
else if (_wcsicmp(L"WLM", str) == 0) else if (_wcsicmp(L"WLM", str) == 0)
{ {
@ -191,7 +185,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_WLM = new CPlayerWLM(); g_WLM = new CPlayerWLM();
} }
data->player = g_WLM; parent->player = g_WLM;
} }
else if (_wcsicmp(L"WMP", str) == 0) else if (_wcsicmp(L"WMP", str) == 0)
{ {
@ -199,7 +193,7 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
g_WMP = new CPlayerWMP(); g_WMP = new CPlayerWMP();
} }
data->player = g_WMP; parent->player = g_WMP;
} }
else else
{ {
@ -215,27 +209,31 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
error += section; error += section;
error += L"] is not valid."; error += L"] is not valid.";
LSLog(LOG_ERROR, L"Rainmeter", error.c_str()); LSLog(LOG_ERROR, L"Rainmeter", error.c_str());
delete data; delete parent;
delete child;
return maxValue; return maxValue;
} }
str = ReadConfigString(section, L"PlayerPath", NULL); parent->id = id;
if (str && *str) parent->childCount = 1;
parent->player->AddInstance();
parent->playerPath = ReadConfigString(section, L"PlayerPath", L"");
parent->trackChangeAction = ReadConfigString(section, L"TrackChangeAction", L"");
if (!parent->trackChangeAction.empty())
{ {
data->player->SetPlayerPath(str); // Get window handle to send the bang later on
parent->window = FindMeterWindow(parent->iniFile);
parent->trackCount = 1;
} }
str = ReadConfigString(section, L"DisableLeadingZero", NULL); str = ReadConfigString(section, L"DisableLeadingZero", L"0");
if (str && *str) if (str)
{ {
g_DisableLeazingZero = (1 == _wtoi(str)); parent->disableLeadingZero = (1 == _wtoi(str));
} }
str = ReadConfigString(section, L"TrackChangeAction", NULL); child->parent = parent;
if (str && *str)
{
data->player->SetTrackChangeAction(str);
}
} }
} }
@ -244,55 +242,69 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
{ {
if (_wcsicmp(L"ARTIST", str) == 0) if (_wcsicmp(L"ARTIST", str) == 0)
{ {
data->measure = MEASURE_ARTIST; child->type = MEASURE_ARTIST;
} }
else if (_wcsicmp(L"TITLE", str) == 0) else if (_wcsicmp(L"TITLE", str) == 0)
{ {
data->measure = MEASURE_TITLE; child->type = MEASURE_TITLE;
} }
else if (_wcsicmp(L"ALBUM", str) == 0) else if (_wcsicmp(L"ALBUM", str) == 0)
{ {
data->measure = MEASURE_ALBUM; child->type = MEASURE_ALBUM;
}
else if (_wcsicmp(L"LYRICS", str) == 0)
{
child->type = MEASURE_LYRICS;
} }
else if (_wcsicmp(L"COVER", str) == 0) else if (_wcsicmp(L"COVER", str) == 0)
{ {
data->measure = MEASURE_COVER; child->type = MEASURE_COVER;
} }
else if (_wcsicmp(L"DURATION", str) == 0) else if (_wcsicmp(L"DURATION", str) == 0)
{ {
data->measure = MEASURE_DURATION; child->type = MEASURE_DURATION;
} }
else if (_wcsicmp(L"POSITION", str) == 0) else if (_wcsicmp(L"POSITION", str) == 0)
{ {
data->measure = MEASURE_POSITION; child->type = MEASURE_POSITION;
} }
else if (_wcsicmp(L"PROGRESS", str) == 0) else if (_wcsicmp(L"PROGRESS", str) == 0)
{ {
data->measure = MEASURE_PROGRESS; child->type = MEASURE_PROGRESS;
maxValue = 100; maxValue = 100;
} }
else if (_wcsicmp(L"RATING", str) == 0) else if (_wcsicmp(L"RATING", str) == 0)
{ {
data->measure = MEASURE_RATING; child->type = MEASURE_RATING;
maxValue = 5; maxValue = 5;
} }
else if (_wcsicmp(L"STATE", str) == 0) else if (_wcsicmp(L"STATE", str) == 0)
{ {
data->measure = MEASURE_STATE; child->type = MEASURE_STATE;
} }
else if (_wcsicmp(L"VOLUME", str) == 0) else if (_wcsicmp(L"VOLUME", str) == 0)
{ {
data->measure = MEASURE_VOLUME; child->type = MEASURE_VOLUME;
maxValue = 100; maxValue = 100;
} }
else if (_wcsicmp(L"FILE", str) == 0) else if (_wcsicmp(L"FILE", str) == 0)
{ {
data->measure = MEASURE_FILE; child->type = MEASURE_FILE;
} }
else
{
std::wstring error = L"NowPlayingPlugin: PlayerType=";
error += str;
error += L" in section [";
error += section;
error += L"] is not valid.";
LSLog(LOG_WARNING, L"Rainmeter", error.c_str());
}
child->parent->player->AddMeasure(child->type);
} }
data->player->AddInstance(data->measure); g_Measures[id] = child;
g_Values[id] = data;
return maxValue; return maxValue;
} }
@ -304,16 +316,26 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
*/ */
void Finalize(HMODULE instance, UINT id) void Finalize(HMODULE instance, UINT id)
{ {
MeasureMap::iterator i = g_Values.find(id); std::map<UINT, ChildMeasure*>::iterator i = g_Measures.find(id);
if (i != g_Values.end()) if (i != g_Measures.end())
{ {
(*i).second->player->RemoveInstance(); ChildMeasure* child = (*i).second;
delete (*i).second; ParentMeasure* parent = child->parent;
g_Values.erase(i); CPlayer* player = parent->player;
if (--parent->childCount == 0)
{
player->RemoveInstance();
delete parent;
}
delete child;
g_Measures.erase(i);
} }
} }
/* /*
** Update ** Update
** **
** Called on each update. ** Called on each update.
@ -321,18 +343,29 @@ void Finalize(HMODULE instance, UINT id)
*/ */
UINT Update(UINT id) UINT Update(UINT id)
{ {
MeasureMap::iterator i = g_Values.find(id); std::map<UINT, ChildMeasure*>::iterator i = g_Measures.find(id);
if (i != g_Values.end()) if (i != g_Measures.end())
{ {
if (!(*i).second->section.empty()) ChildMeasure* child = (*i).second;
ParentMeasure* parent = child->parent;
CPlayer* player = parent->player;
if (parent->id == id)
{ {
// Only allow main measure to update player->UpdateMeasure();
(*i).second->player->UpdateData();
// Execute TrackChangeAction= if necessary
if (!parent->trackChangeAction.empty() &&
parent->trackCount != player->GetTrackCount())
{
ExecuteCommand(parent->trackChangeAction, parent->window);
// TODO: First is true..
parent->trackCount = player->GetTrackCount();
}
} }
CPlayer* player = (*i).second->player; switch (child->type)
switch ((*i).second->measure)
{ {
case MEASURE_DURATION: case MEASURE_DURATION:
return player->GetDuration(); return player->GetDuration();
@ -351,7 +384,7 @@ UINT Update(UINT id)
return player->GetRating(); return player->GetRating();
case MEASURE_STATE: case MEASURE_STATE:
return (int)player->GetState(); return (UINT)player->GetState();
case MEASURE_VOLUME: case MEASURE_VOLUME:
return player->GetVolume(); return player->GetVolume();
@ -371,13 +404,15 @@ UINT Update(UINT id)
*/ */
LPCTSTR GetString(UINT id, UINT flags) LPCTSTR GetString(UINT id, UINT flags)
{ {
MeasureMap::iterator i = g_Values.find(id); std::map<UINT, ChildMeasure*>::iterator i = g_Measures.find(id);
if (i != g_Values.end()) if (i != g_Measures.end())
{ {
CPlayer* player = (*i).second->player; ChildMeasure* child = (*i).second;
ParentMeasure* parent = child->parent;
CPlayer* player = parent->player;
static WCHAR buffer[32]; static WCHAR buffer[32];
switch ((*i).second->measure) switch (child->type)
{ {
case MEASURE_ARTIST: case MEASURE_ARTIST:
return player->GetArtist(); return player->GetArtist();
@ -392,21 +427,16 @@ LPCTSTR GetString(UINT id, UINT flags)
return player->GetCoverPath(); return player->GetCoverPath();
case MEASURE_DURATION: case MEASURE_DURATION:
SecondsToTime(player->GetDuration(), buffer); SecondsToTime(player->GetDuration(), parent->disableLeadingZero, buffer);
return buffer; return buffer;
case MEASURE_POSITION: case MEASURE_POSITION:
SecondsToTime(player->GetPosition(), buffer); SecondsToTime(player->GetPosition(), parent->disableLeadingZero, buffer);
return buffer; return buffer;
case MEASURE_PROGRESS: case MEASURE_PROGRESS:
if (player->GetDuration()) _itow(player->GetDuration() ? ((player->GetPosition() * 100) / player->GetDuration()) : 0, buffer, 10);
{ return buffer;
int res = (player->GetPosition() * 100) / player->GetDuration();
_itow(res, buffer, 10);
return buffer;
}
return L"0";
case MEASURE_RATING: case MEASURE_RATING:
_itow(player->GetRating(), buffer, 10); _itow(player->GetRating(), buffer, 10);
@ -426,8 +456,7 @@ LPCTSTR GetString(UINT id, UINT flags)
} }
else else
{ {
// For invalid PlayerName= return L"Error: Invalid player.";
return L"0";
} }
return L""; return L"";
@ -441,12 +470,21 @@ LPCTSTR GetString(UINT id, UINT flags)
*/ */
void ExecuteBang(LPCTSTR bang, UINT id) void ExecuteBang(LPCTSTR bang, UINT id)
{ {
MeasureMap::iterator i = g_Values.find(id); std::map<UINT, ChildMeasure*>::iterator i = g_Measures.find(id);
if (i != g_Values.end()) if (i != g_Measures.end())
{ {
CPlayer* player = (*i).second->player; ChildMeasure* child = (*i).second;
ParentMeasure* parent = child->parent;
CPlayer* player = parent->player;
if (_wcsicmp(bang, L"Pause") == 0) if (!player->IsInitialized())
{
if (_wcsicmp(bang, L"OpenPlayer") == 0 || _wcsicmp(bang, L"TogglePlayer") == 0)
{
player->OpenPlayer(parent->playerPath);
}
}
else if (_wcsicmp(bang, L"Pause") == 0)
{ {
player->Pause(); player->Pause();
} }
@ -456,7 +494,7 @@ void ExecuteBang(LPCTSTR bang, UINT id)
} }
else if (_wcsicmp(bang, L"PlayPause") == 0) else if (_wcsicmp(bang, L"PlayPause") == 0)
{ {
player->PlayPause(); (player->GetState() != PLAYER_PLAYING) ? player->Play() : player->Pause();
} }
else if (_wcsicmp(bang, L"Stop") == 0) else if (_wcsicmp(bang, L"Stop") == 0)
{ {
@ -470,24 +508,18 @@ void ExecuteBang(LPCTSTR bang, UINT id)
{ {
player->Previous(); player->Previous();
} }
else if (_wcsicmp(bang, L"ClosePlayer") == 0) else if (_wcsicmp(bang, L"ClosePlayer") == 0 || _wcsicmp(bang, L"TogglePlayer") == 0)
{ {
player->ClosePlayer(); player->ClosePlayer();
} }
else if (_wcsicmp(bang, L"OpenPlayer") == 0)
{
player->OpenPlayer();
}
else if (_wcsicmp(bang, L"TogglePlayer") == 0)
{
player->TogglePlayer();
}
else else
{ {
LPCTSTR arg = wcschr(bang, L' '); LPCTSTR arg = wcschr(bang, L' ');
if (++arg) // Skip the space if (arg)
{ {
++arg; // Skip the space
if (wcsnicmp(bang, L"SetPosition", 11) == 0) if (wcsnicmp(bang, L"SetPosition", 11) == 0)
{ {
int position = (_wtoi(arg) * player->GetDuration()) / 100; int position = (_wtoi(arg) * player->GetDuration()) / 100;
@ -544,3 +576,71 @@ LPCTSTR GetPluginAuthor()
{ {
return L"Birunthan Mohanathas (www.poiru.net)"; return L"Birunthan Mohanathas (www.poiru.net)";
} }
void SecondsToTime(UINT seconds, bool leadingZero, WCHAR* buffer)
{
int hours = seconds;
int mins = seconds;
hours /= 3600;
mins %= 3600;
int secs = mins;
mins /= 60;
secs %= 60;
if (hours)
{
_snwprintf_s(buffer, 32, _TRUNCATE, leadingZero ? L"%i:%02i:%02i" : L"%02i:%02i:%02i", hours, mins, secs);
}
else
{
_snwprintf_s(buffer, 32, _TRUNCATE, leadingZero ? L"%i:%02i" : L"%02i:%02i", mins, secs);
}
}
void ExecuteCommand(std::wstring& command, HWND wnd)
{
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = (DWORD)(command.size() + 1) * sizeof(WCHAR);
cds.lpData = (void*)command.c_str();
// Send bang to the Rainmeter window
SendMessage(wnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
}
bool BelongToSameProcess(HWND wnd)
{
DWORD procId = 0;
GetWindowThreadProcessId(wnd, &procId);
return (procId == GetCurrentProcessId());
}
HWND FindMeterWindow(HWND parent)
{
HWND wnd = NULL;
while (wnd = FindWindowEx(parent, wnd, L"RainmeterMeterWindow", NULL))
{
if (BelongToSameProcess(wnd))
{
return wnd;
}
}
return NULL;
}
HWND FindMeterWindow(const std::wstring& iniFile)
{
std::wstring str = PluginBridge(L"getconfig", iniFile.c_str());
if (!str.empty())
{
str = PluginBridge(L"getwindow", str.c_str());
if (str != L"error")
{
return (HWND)UlongToPtr(wcstoul(str.c_str(), NULL, 10));
}
}
return FindMeterWindow(NULL); // Use old way to find
}

View File

@ -21,20 +21,36 @@
#include "Player.h" #include "Player.h"
struct MeasureData struct ParentMeasure
{ {
std::wstring iniFile; UINT id;
std::wstring section; UINT childCount;
MEASURETYPE measure; UINT trackCount;
CPlayer* player; CPlayer* player;
HANDLE thread;
HWND window;
std::wstring name;
std::wstring iniFile;
std::wstring trackChangeAction;
std::wstring playerPath;
bool disableLeadingZero;
MeasureData() : ParentMeasure() : player(NULL), thread(NULL) {}
player(NULL)
{
}
}; };
typedef std::map<UINT, MeasureData*> MeasureMap; struct ChildMeasure
{
MEASURETYPE type;
ParentMeasure* parent;
ChildMeasure() : parent(NULL) {}
};
void SecondsToTime(UINT seconds, bool leadingZero, WCHAR* buffer);
void ExecuteCommand(std::wstring& command, HWND wnd);
bool BelongToSameProcess(HWND wnd);
HWND FindMeterWindow(HWND parent);
HWND FindMeterWindow(const std::wstring& iniFile);
/* The exported functions */ /* The exported functions */
extern "C" extern "C"

View File

@ -16,13 +16,9 @@
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
// Common functions for the players
#include "StdAfx.h" #include "StdAfx.h"
#include "Player.h" #include "Player.h"
extern std::wstring g_CachePath;
/* /*
** CPlayer ** CPlayer
** **
@ -30,13 +26,17 @@ extern std::wstring g_CachePath;
** **
*/ */
CPlayer::CPlayer() : CPlayer::CPlayer() :
m_Initialized(false),
m_HasCoverMeasure(false),
m_HasLyricsMeasure(false),
m_InstanceCount(), m_InstanceCount(),
m_UpdateCount(),
m_TrackCount(),
m_State(), m_State(),
m_Duration(), m_Duration(),
m_Position(), m_Position(),
m_Rating(), m_Rating(),
m_Volume(), m_Volume()
m_TrackChanged(false)
{ {
} }
@ -51,35 +51,72 @@ CPlayer::~CPlayer()
} }
/* /*
** ExecuteTrackChangeAction ** AddInstance
** **
** Called from player implementation on track change. ** Called during initialization of main measure.
** **
*/ */
void CPlayer::ExecuteTrackChangeAction() void CPlayer::AddInstance()
{ {
if (!m_TrackChangeAction.empty()) ++m_InstanceCount;
{ }
HWND wnd = FindWindow(L"RainmeterMeterWindow", NULL);
if (wnd != NULL)
{
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = (DWORD)(m_TrackChangeAction.size() + 1) * sizeof(WCHAR);
cds.lpData = (void*)m_TrackChangeAction.c_str();
// Send the bang to the Rainmeter window /*
SendMessage(wnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); ** RemoveInstance
} **
** Called during destruction of main measure.
**
*/
void CPlayer::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
delete this;
} }
} }
/* /*
** ClearInfo ** AddMeasure
**
** Called during initialization of any measure.
**
*/
void CPlayer::AddMeasure(MEASURETYPE measure)
{
switch (measure)
{
case MEASURE_LYRICS:
m_HasLyricsMeasure = true;
break;
case MEASURE_COVER:
m_HasCoverMeasure = true;
break;
}
}
/*
** UpdateMeasure
**
** Called during update of main measure.
**
*/
void CPlayer::UpdateMeasure()
{
if (++m_UpdateCount == m_InstanceCount)
{
UpdateData();
m_UpdateCount = 0;
}
}
/*
** ClearData
** **
** Clear track information. ** Clear track information.
** **
*/ */
void CPlayer::ClearInfo() void CPlayer::ClearData()
{ {
m_Duration = 0; m_Duration = 0;
m_Position = 0; m_Position = 0;
@ -91,297 +128,3 @@ void CPlayer::ClearInfo()
m_FilePath.clear(); m_FilePath.clear();
m_CoverPath.clear(); m_CoverPath.clear();
} }
/*
** GetCachedArt
**
** Checks if cover art is in cache.
**
*/
bool CPlayer::GetCachedArt()
{
m_CoverPath = g_CachePath;
if (m_Artist.empty() || m_Title.empty())
{
m_CoverPath += L"temp.art";
}
else
{
// Otherwise, save it as "Artist - Title.art"
std::wstring name = m_Artist;
name += L" - ";
name += m_Title;
// Replace reserved chars with _
std::wstring::size_type pos = 0;
while ((pos = name.find_first_of(L"\\/:*?\"<>|", pos)) != std::wstring::npos) name[pos] = L'_';
m_CoverPath += name;
m_CoverPath += L".art";
if (_waccess(m_CoverPath.c_str(), 0) == 0)
{
// Art found in cache
return true;
}
}
return false;
}
/*
** GetLocalArt
**
** Attemps to find local cover art in various formats.
**
*/
bool CPlayer::GetLocalArt(std::wstring& folder, std::wstring filename)
{
std::wstring testPath = folder;
testPath += filename;
testPath += L".";
std::wstring::size_type origLen = testPath.length();
const int extCount = 4;
LPCTSTR extName[extCount] = { L"jpg", L"jpeg", L"png", L"bmp" };
for (int i = 0; i < extCount; ++i)
{
testPath += extName[i];
if (_waccess(testPath.c_str(), 0) == 0)
{
m_CoverPath = testPath;
return true;
}
else
{
// Get rid of the added extension
testPath.resize(origLen);
}
}
return false;
}
/*
** GetEmbeddedArt
**
** Attempts to extract cover art from audio files.
**
*/
bool CPlayer::GetEmbeddedArt(const TagLib::FileRef& fr)
{
bool found = false;
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fr.file()))
{
if (file->ID3v2Tag())
{
found = GetArtID3(file->ID3v2Tag());
}
if (!found && file->APETag())
{
found = GetArtAPE(file->APETag());
}
}
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fr.file()))
{
if (file->tag())
{
found = GetArtMP4(file);
}
}
else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fr.file()))
{
found = GetArtFLAC(file);
if (!found && file->ID3v2Tag())
{
found = GetArtID3(file->ID3v2Tag());
}
}
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fr.file()))
{
found = GetArtASF(file);
}
else if (TagLib::APE::File* file = dynamic_cast<TagLib::APE::File*>(fr.file()))
{
if (file->APETag())
{
found = GetArtAPE(file->APETag());
}
}
else if (TagLib::MPC::File* file = dynamic_cast<TagLib::MPC::File*>(fr.file()))
{
if (file->APETag())
{
found = GetArtAPE(file->APETag());
}
}
else if (TagLib::WavPack::File* file = dynamic_cast<TagLib::WavPack::File*>(fr.file()))
{
if (file->APETag())
{
found = GetArtAPE(file->APETag());
}
}
return found;
}
/*
** GetArtAPE
**
** Extracts cover art embedded in APE tags.
**
*/
bool CPlayer::GetArtAPE(TagLib::APE::Tag* tag)
{
bool ret = false;
const TagLib::APE::ItemListMap& listMap = tag->itemListMap();
if (listMap.contains("COVER ART (FRONT)"))
{
const TagLib::ByteVector nullStringTerminator(1, 0);
TagLib::ByteVector item = listMap["COVER ART (FRONT)"].value();
int pos = item.find(nullStringTerminator); // Skip the filename
if (++pos > 0)
{
const TagLib::ByteVector& pic = item.mid(pos);
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
if (f)
{
ret = (fwrite(pic.data(), 1, pic.size(), f) == pic.size());
fclose(f);
}
}
}
return ret;
}
/*
** GetArtID3
**
** Extracts cover art embedded in ID3v2 tags.
**
*/
bool CPlayer::GetArtID3(TagLib::ID3v2::Tag* tag)
{
bool ret = false;
const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC");
if (!frameList.isEmpty())
{
// Grab the first image
TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front());
TagLib::uint size = frame->picture().size();
if (size > 0)
{
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
if (f)
{
ret = (fwrite(frame->picture().data(), 1, size, f) == size);
fclose(f);
}
}
}
return ret;
}
/*
** GetArtASF
**
** Extracts cover art embedded in ASF/WMA files.
**
*/
bool CPlayer::GetArtASF(TagLib::ASF::File* file)
{
bool ret = false;
const TagLib::ASF::AttributeListMap& attrListMap = file->tag()->attributeListMap();
if (attrListMap.contains("WM/Picture"))
{
const TagLib::ASF::AttributeList& attrList = attrListMap["WM/Picture"];
if (!attrList.isEmpty())
{
// Let's grab the first cover. TODO: Check/loop for correct type
TagLib::ASF::Picture wmpic = attrList[0].toPicture();
if (wmpic.isValid())
{
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
if (f)
{
ret = (fwrite(wmpic.picture().data(), 1, wmpic.picture().size(), f) == wmpic.picture().size());
fclose(f);
}
}
}
}
return ret;
}
/*
** GetArtFLAC
**
** Extracts cover art embedded in FLAC files.
**
*/
bool CPlayer::GetArtFLAC(TagLib::FLAC::File* file)
{
bool ret = false;
const TagLib::List<TagLib::FLAC::Picture*>& picList = file->pictureList();
if (!picList.isEmpty())
{
// Let's grab the first image
TagLib::FLAC::Picture* pic = picList[0];
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
if (f)
{
ret = (fwrite(pic->data().data(), 1, pic->data().size(), f) == pic->data().size());
fclose(f);
}
}
return ret;
}
/*
** GetArtMP4
**
** Extracts cover art embedded in MP4-like files.
**
*/
bool CPlayer::GetArtMP4(TagLib::MP4::File* file)
{
bool ret = false;
TagLib::MP4::Tag* tag = file->tag();
if (tag->itemListMap().contains("covr"))
{
TagLib::MP4::CoverArtList coverList = tag->itemListMap()["covr"].toCoverArtList();
TagLib::uint size = coverList[0].data().size();
if (size > 0)
{
FILE* f = _wfopen(m_CoverPath.c_str(), L"wb");
if (f)
{
ret = (fwrite(coverList[0].data().data(), 1, size, f) == size);
fclose(f);
}
}
}
return ret;
}

View File

@ -19,26 +19,10 @@
#ifndef __PLAYER_H__ #ifndef __PLAYER_H__
#define __PLAYER_H__ #define __PLAYER_H__
// TagLib
#include "apefile.h"
#include "apetag.h"
#include "asffile.h"
#include "attachedpictureframe.h"
#include "commentsframe.h"
#include "flacfile.h"
#include "id3v1genres.h"
#include "id3v2tag.h"
#include "mpcfile.h"
#include "mp4file.h"
#include "mpegfile.h"
#include "tag.h" #include "tag.h"
#include "taglib.h" #include "Cover.h"
#include "textidentificationframe.h"
#include "tstring.h"
#include "vorbisfile.h"
#include "wavpackfile.h"
enum PLAYERSTATE enum PLAYSTATE
{ {
PLAYER_STOPPED, PLAYER_STOPPED,
PLAYER_PLAYING, PLAYER_PLAYING,
@ -50,6 +34,7 @@ enum MEASURETYPE
MEASURE_ARTIST, MEASURE_ARTIST,
MEASURE_TITLE, MEASURE_TITLE,
MEASURE_ALBUM, MEASURE_ALBUM,
MEASURE_LYRICS,
MEASURE_COVER, MEASURE_COVER,
MEASURE_DURATION, MEASURE_DURATION,
MEASURE_POSITION, MEASURE_POSITION,
@ -60,72 +45,66 @@ enum MEASURETYPE
MEASURE_FILE MEASURE_FILE
}; };
class CPlayer class CPlayer :
public CCover
{ {
public: public:
CPlayer(); CPlayer();
virtual ~CPlayer(); virtual ~CPlayer() = 0;
void AddInstance();
void RemoveInstance();
void UpdateMeasure();
virtual void AddMeasure(MEASURETYPE measure);
virtual void UpdateData() = 0;
bool IsInitialized() { return m_Initialized; }
UINT GetTrackCount() { return m_TrackCount; }
virtual void Pause() {} virtual void Pause() {}
virtual void Play() {} virtual void Play() {}
virtual void PlayPause() {}
virtual void Stop() {} virtual void Stop() {}
virtual void Next() {} virtual void Next() {}
virtual void Previous() {} virtual void Previous() {}
virtual void SetPosition(int position) {} virtual void SetPosition(int position) {}
virtual void SetRating(int rating) {} virtual void SetRating(int rating) {}
virtual void SetVolume(int volume) {} virtual void SetVolume(int volume) {}
virtual void OpenPlayer() {} virtual void OpenPlayer(std::wstring& path) {}
virtual void ClosePlayer() {} virtual void ClosePlayer() {}
virtual void TogglePlayer() {}
virtual void AddInstance(MEASURETYPE type) = 0; PLAYSTATE GetState() { return m_State; }
virtual void RemoveInstance() = 0;
virtual void UpdateData() = 0;
PLAYERSTATE GetState() { return m_State; }
LPCTSTR GetArtist() { return m_Artist.c_str(); } LPCTSTR GetArtist() { return m_Artist.c_str(); }
LPCTSTR GetAlbum() { return m_Album.c_str(); } LPCTSTR GetAlbum() { return m_Album.c_str(); }
LPCTSTR GetTitle() { return m_Title.c_str(); } LPCTSTR GetTitle() { return m_Title.c_str(); }
LPCTSTR GetFilePath() { return m_FilePath.c_str(); } LPCTSTR GetLyrics() { return m_Lyrics.c_str(); }
LPCTSTR GetCoverPath() { return m_CoverPath.c_str(); } LPCTSTR GetCoverPath() { return m_CoverPath.c_str(); }
LPCTSTR GetPlayerPath() { return m_PlayerPath.c_str(); } LPCTSTR GetFilePath() { return m_FilePath.c_str(); }
UINT GetDuration() { return m_Duration; } UINT GetDuration() { return m_Duration; }
UINT GetPosition() { return m_Position; } UINT GetPosition() { return m_Position; }
UINT GetRating() { return m_Rating; } UINT GetRating() { return m_Rating; }
UINT GetVolume() { return m_Volume; } UINT GetVolume() { return m_Volume; }
void SetPlayerPath(LPCTSTR path) { m_PlayerPath = path; }
void SetTrackChangeAction(LPCTSTR action) { m_TrackChangeAction = action; }
void ExecuteTrackChangeAction();
void ClearInfo();
bool GetCachedArt();
bool GetLocalArt(std::wstring& folder, std::wstring filename);
bool GetEmbeddedArt(const TagLib::FileRef& fr);
bool GetArtAPE(TagLib::APE::Tag* tag);
bool GetArtID3(TagLib::ID3v2::Tag* tag);
bool GetArtASF(TagLib::ASF::File* file);
bool GetArtFLAC(TagLib::FLAC::File* file);
bool GetArtMP4(TagLib::MP4::File* file);
protected: protected:
int m_InstanceCount; void ClearData();
bool m_TrackChanged;
PLAYERSTATE m_State; bool m_Initialized;
bool m_HasCoverMeasure;
bool m_HasLyricsMeasure;
UINT m_InstanceCount;
UINT m_UpdateCount;
UINT m_TrackCount;
PLAYSTATE m_State;
std::wstring m_Artist; std::wstring m_Artist;
std::wstring m_Album;
std::wstring m_Title; std::wstring m_Title;
std::wstring m_FilePath; // Path to playing file std::wstring m_Album;
std::wstring m_Lyrics;
std::wstring m_CoverPath; // Path to cover art image std::wstring m_CoverPath; // Path to cover art image
std::wstring m_PlayerPath; // Path to player executable std::wstring m_FilePath; // Path to playing file
UINT m_Duration; // Track duration in seconds UINT m_Duration; // Track duration in seconds
UINT m_Position; // Current position in seconds UINT m_Position; // Current position in seconds
UINT m_Rating; // Track rating from 0 to 100 UINT m_Rating; // Track rating from 0 to 100
UINT m_Volume; // Volume from 0 to 100 UINT m_Volume; // Volume from 0 to 100
std::wstring m_TrackChangeAction;
}; };
#endif #endif

View File

@ -30,7 +30,6 @@ extern CPlayer* g_AIMP;
** **
*/ */
CPlayerAIMP::CPlayerAIMP() : CPlayer(), CPlayerAIMP::CPlayerAIMP() : CPlayer(),
m_HasCoverMeasure(false),
m_FileMap(), m_FileMap(),
m_FileMapHandle(), m_FileMapHandle(),
m_Window(), m_Window(),
@ -46,41 +45,17 @@ CPlayerAIMP::CPlayerAIMP() : CPlayer(),
*/ */
CPlayerAIMP::~CPlayerAIMP() CPlayerAIMP::~CPlayerAIMP()
{ {
g_AIMP = NULL;
if (m_FileMap) UnmapViewOfFile(m_FileMap); if (m_FileMap) UnmapViewOfFile(m_FileMap);
if (m_FileMapHandle) CloseHandle(m_FileMapHandle); if (m_FileMapHandle) CloseHandle(m_FileMapHandle);
} }
/* /*
** AddInstance ** Initialize
** **
** Called during initialization of each measure. ** Find AIMP window and mapped object.
** **
*/ */
void CPlayerAIMP::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
if (type == MEASURE_COVER)
{
m_HasCoverMeasure = true;
}
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerAIMP::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_AIMP = NULL;
delete this;
}
}
bool CPlayerAIMP::Initialize() bool CPlayerAIMP::Initialize()
{ {
m_Window = FindWindow(L"AIMP2_RemoteInfo", L"AIMP2_RemoteInfo"); m_Window = FindWindow(L"AIMP2_RemoteInfo", L"AIMP2_RemoteInfo");
@ -118,7 +93,7 @@ bool CPlayerAIMP::CheckActive()
m_WinampWindow = NULL; m_WinampWindow = NULL;
if (m_FileMap) UnmapViewOfFile(m_FileMap); if (m_FileMap) UnmapViewOfFile(m_FileMap);
if (m_FileMapHandle) CloseHandle(m_FileMapHandle); if (m_FileMapHandle) CloseHandle(m_FileMapHandle);
ClearInfo(); ClearData();
return false; return false;
} }
@ -162,24 +137,18 @@ void CPlayerAIMP::UpdateData()
return; return;
} }
m_State = (PLAYERSTATE)SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_Player); m_State = (PLAYSTATE)SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_Player);
if (m_State == PLAYER_STOPPED) if (m_State == PLAYER_STOPPED)
{ {
if (oldFileSize != 0) if (oldFileSize != 0)
{ {
oldFileSize = 0; oldFileSize = 0;
oldTitleLen = 0; oldTitleLen = 0;
ClearInfo(); ClearData();
} }
return; return;
} }
if (m_TrackChanged)
{
ExecuteTrackChangeAction();
m_TrackChanged = false;
}
m_Position = SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_POS); m_Position = SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_POS);
m_Volume = SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_VOLUME); m_Volume = SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_STATUS_GET, AIMP_STS_VOLUME);
@ -217,37 +186,12 @@ void CPlayerAIMP::UpdateData()
if (filepath != m_FilePath) if (filepath != m_FilePath)
{ {
m_FilePath = filepath; m_FilePath = filepath;
m_TrackChanged = true; ++m_TrackCount;
// Find cover if needed
if (m_HasCoverMeasure) if (m_HasCoverMeasure)
{ {
if (GetCachedArt()) GetCover(m_Artist, m_Title, m_FilePath, m_CoverPath);
{
// Cover is in cache, lets use the that
return;
}
TagLib::FileRef fr(m_FilePath.c_str());
if (!fr.isNull() && fr.tag() && GetEmbeddedArt(fr))
{
// Embedded art found
return;
}
// Get rid of the name and extension from filename
std::wstring trackFolder = m_FilePath;
std::wstring::size_type pos = trackFolder.find_last_of(L'\\');
if (pos == std::wstring::npos) return;
trackFolder.resize(++pos);
if (GetLocalArt(trackFolder, L"cover") || GetLocalArt(trackFolder, L"folder"))
{
// Local art found
return;
}
// Nothing found
m_CoverPath.clear();
} }
} }
} }
@ -261,10 +205,7 @@ void CPlayerAIMP::UpdateData()
*/ */
void CPlayerAIMP::Pause() void CPlayerAIMP::Pause()
{ {
if (m_Window) SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_PAUSE);
{
SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_PAUSE);
}
} }
/* /*
@ -275,21 +216,7 @@ void CPlayerAIMP::Pause()
*/ */
void CPlayerAIMP::Play() void CPlayerAIMP::Play()
{ {
if (m_Window) SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_PLAY);
{
SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_PLAY);
}
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerAIMP::PlayPause()
{
(m_State == PLAYER_STOPPED) ? Play() : Pause();
} }
/* /*
@ -300,10 +227,7 @@ void CPlayerAIMP::PlayPause()
*/ */
void CPlayerAIMP::Stop() void CPlayerAIMP::Stop()
{ {
if (m_Window) SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_STOP);
{
SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_STOP);
}
} }
/* /*
@ -314,10 +238,7 @@ void CPlayerAIMP::Stop()
*/ */
void CPlayerAIMP::Next() void CPlayerAIMP::Next()
{ {
if (m_Window) SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_NEXT);
{
SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_NEXT);
}
} }
/* /*
@ -328,10 +249,7 @@ void CPlayerAIMP::Next()
*/ */
void CPlayerAIMP::Previous() void CPlayerAIMP::Previous()
{ {
if (m_Window) SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_PREV);
{
SendMessage(m_Window, WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_PREV);
}
} }
/* /*
@ -354,7 +272,7 @@ void CPlayerAIMP::SetPosition(int position)
void CPlayerAIMP::SetRating(int rating) void CPlayerAIMP::SetRating(int rating)
{ {
// Set rating through the AIMP Winamp API // Set rating through the AIMP Winamp API
if (m_WinampWindow && (m_State == PLAYER_PLAYING || m_State == PLAYER_PAUSED)) if (m_State != PLAYER_STOPPED)
{ {
if (rating < 0) if (rating < 0)
{ {
@ -389,10 +307,7 @@ void CPlayerAIMP::SetVolume(int volume)
*/ */
void CPlayerAIMP::ClosePlayer() void CPlayerAIMP::ClosePlayer()
{ {
if (m_Window) SendMessage(m_Window, WM_CLOSE, 0, 0);
{
SendMessage(m_Window, WM_CLOSE, 0, 0);
}
} }
/* /*
@ -401,9 +316,9 @@ void CPlayerAIMP::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerAIMP::OpenPlayer() void CPlayerAIMP::OpenPlayer(std::wstring& path)
{ {
if (m_PlayerPath.empty()) if (path.empty())
{ {
// Check for AIMP2 first // Check for AIMP2 first
DWORD size = 512; DWORD size = 512;
@ -427,7 +342,7 @@ void CPlayerAIMP::OpenPlayer()
if (type == REG_SZ) if (type == REG_SZ)
{ {
ShellExecute(NULL, L"open", data, NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", data, NULL, NULL, SW_SHOW);
m_PlayerPath = data; path = data;
} }
} }
else else
@ -449,11 +364,10 @@ void CPlayerAIMP::OpenPlayer()
{ {
if (type == REG_SZ) if (type == REG_SZ)
{ {
std::wstring path = data; path = data;
path.resize(path.find_last_of(L'\\') + 1); path.resize(path.find_last_of(L'\\') + 1);
path += L"AIMP3.exe"; path += L"AIMP3.exe";
ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
m_PlayerPath = path;
} }
} }
} }
@ -463,17 +377,6 @@ void CPlayerAIMP::OpenPlayer()
} }
else else
{ {
ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
} }
} }
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerAIMP::TogglePlayer()
{
m_Window ? ClosePlayer() : OpenPlayer();
}

View File

@ -27,9 +27,10 @@ public:
CPlayerAIMP(); CPlayerAIMP();
~CPlayerAIMP(); ~CPlayerAIMP();
virtual void UpdateData();
virtual void Pause(); virtual void Pause();
virtual void Play(); virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
@ -37,18 +38,12 @@ public:
virtual void SetRating(int rating); virtual void SetRating(int rating);
virtual void SetVolume(int volume); virtual void SetVolume(int volume);
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
bool Initialize(); bool Initialize();
bool CheckActive(); bool CheckActive();
bool m_HasCoverMeasure;
LPVOID m_FileMap; LPVOID m_FileMap;
HANDLE m_FileMapHandle; HANDLE m_FileMapHandle;
HWND m_Window; // AIMP window HWND m_Window; // AIMP window

View File

@ -33,7 +33,6 @@ extern std::wstring g_SettingsFile;
** **
*/ */
CPlayerCAD::CPlayerCAD() : CPlayer(), CPlayerCAD::CPlayerCAD() : CPlayer(),
m_HasCoverMeasure(false),
m_Window(), m_Window(),
m_PlayerWindow() m_PlayerWindow()
{ {
@ -48,40 +47,10 @@ CPlayerCAD::CPlayerCAD() : CPlayer(),
*/ */
CPlayerCAD::~CPlayerCAD() CPlayerCAD::~CPlayerCAD()
{ {
g_CAD = NULL;
Uninitialize(); Uninitialize();
} }
/*
** AddInstance
**
** Called during initialization of each measure.
**
*/
void CPlayerCAD::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
if (type == MEASURE_COVER)
{
m_HasCoverMeasure = true;
}
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerCAD::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_CAD = NULL;
delete this;
}
}
/* /*
** Initialize ** Initialize
** **
@ -145,7 +114,7 @@ void CPlayerCAD::Initialize()
if (m_PlayerWindow) if (m_PlayerWindow)
{ {
SendMessage(m_PlayerWindow, WM_USER, (WPARAM)m_Window, IPC_SET_CALLBACK_HWND); SendMessage(m_PlayerWindow, WM_USER, (WPARAM)m_Window, IPC_SET_CALLBACK_HWND);
m_State = (PLAYERSTATE)SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE); m_State = (PLAYSTATE)SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
if (m_State != PLAYER_STOPPED) if (m_State != PLAYER_STOPPED)
{ {
@ -195,16 +164,16 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
break; break;
case IPC_PLAYER_STATE_CHANGED_NOTIFICATION: case IPC_PLAYER_STATE_CHANGED_NOTIFICATION:
p->m_State = (PLAYERSTATE)wParam; p->m_State = (PLAYSTATE)wParam;
if (p->m_State == PLAYER_STOPPED) if (p->m_State == PLAYER_STOPPED)
{ {
p->ClearInfo(); p->ClearData();
} }
break; break;
case IPC_SHUTDOWN_NOTIFICATION: case IPC_SHUTDOWN_NOTIFICATION:
p->m_PlayerWindow = NULL; p->m_PlayerWindow = NULL;
p->ClearInfo(); p->ClearData();
break; break;
} }
return 0; return 0;
@ -214,7 +183,9 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
PCOPYDATASTRUCT cds = (PCOPYDATASTRUCT)lParam; PCOPYDATASTRUCT cds = (PCOPYDATASTRUCT)lParam;
if (cds->dwData == IPC_CURRENT_TRACK_INFO) if (cds->dwData == IPC_CURRENT_TRACK_INFO)
{ {
p->m_TrackChanged = true; // TODO: Sent on track update?
++p->m_TrackCount;
std::wstring data = (WCHAR*)cds->lpData; std::wstring data = (WCHAR*)cds->lpData;
std::wstring::size_type len = data.find_first_of(L'\t'); std::wstring::size_type len = data.find_first_of(L'\t');
p->m_Title.assign(data, 0, len); p->m_Title.assign(data, 0, len);
@ -282,7 +253,7 @@ LRESULT CALLBACK CPlayerCAD::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
if (p->m_PlayerWindow) if (p->m_PlayerWindow)
{ {
p->m_State = (PLAYERSTATE)SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE); p->m_State = (PLAYSTATE)SendMessage(p->m_PlayerWindow, WM_USER, 0, IPC_GET_PLAYER_STATE);
if (p->m_State != PLAYER_STOPPED) if (p->m_State != PLAYER_STOPPED)
{ {
@ -311,12 +282,6 @@ void CPlayerCAD::UpdateData()
{ {
m_Position = SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_POSITION); m_Position = SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_POSITION);
m_Volume = SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_VOLUME); m_Volume = SendMessage(m_PlayerWindow, WM_USER, 0, IPC_GET_VOLUME);
if (m_TrackChanged)
{
ExecuteTrackChangeAction();
m_TrackChanged = false;
}
} }
} }
@ -328,10 +293,7 @@ void CPlayerCAD::UpdateData()
*/ */
void CPlayerCAD::Pause() void CPlayerCAD::Pause()
{ {
if (m_PlayerWindow) SendMessage(m_PlayerWindow, WM_USER, 0, IPC_FORCEPAUSE);
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_FORCEPAUSE);
}
} }
/* /*
@ -342,25 +304,7 @@ void CPlayerCAD::Pause()
*/ */
void CPlayerCAD::Play() void CPlayerCAD::Play()
{ {
if (m_PlayerWindow) SendMessage(m_PlayerWindow, WM_USER, 0, IPC_PLAY);
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_PLAY);
}
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerCAD::PlayPause()
{
if (m_PlayerWindow)
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_PLAYPAUSE);
}
} }
/* /*
@ -371,10 +315,7 @@ void CPlayerCAD::PlayPause()
*/ */
void CPlayerCAD::Stop() void CPlayerCAD::Stop()
{ {
if (m_PlayerWindow) SendMessage(m_PlayerWindow, WM_USER, 0, IPC_STOP);
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_STOP);
}
} }
/* /*
@ -385,10 +326,7 @@ void CPlayerCAD::Stop()
*/ */
void CPlayerCAD::Next() void CPlayerCAD::Next()
{ {
if (m_PlayerWindow) SendMessage(m_PlayerWindow, WM_USER, 0, IPC_NEXT);
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_NEXT);
}
} }
/* /*
@ -399,10 +337,7 @@ void CPlayerCAD::Next()
*/ */
void CPlayerCAD::Previous() void CPlayerCAD::Previous()
{ {
if (m_PlayerWindow) SendMessage(m_PlayerWindow, WM_USER, 0, IPC_PREVIOUS);
{
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_PREVIOUS);
}
} }
/* /*
@ -424,12 +359,9 @@ void CPlayerCAD::SetPosition(int position)
*/ */
void CPlayerCAD::SetRating(int rating) void CPlayerCAD::SetRating(int rating)
{ {
if (m_PlayerWindow) m_Rating = rating;
{ rating *= 2; // From 0 - 5 to 0 - 10
m_Rating = rating; SendMessage(m_PlayerWindow, WM_USER, rating, IPC_RATING_CHANGED_NOTIFICATION);
rating *= 2; // From 0 - 5 to 0 - 10
SendMessage(m_PlayerWindow, WM_USER, rating, IPC_RATING_CHANGED_NOTIFICATION);
}
} }
/* /*
@ -440,18 +372,15 @@ void CPlayerCAD::SetRating(int rating)
*/ */
void CPlayerCAD::SetVolume(int volume) void CPlayerCAD::SetVolume(int volume)
{ {
if (m_PlayerWindow) if (volume < 0)
{ {
if (volume < 0) volume = 0;
{
volume = 0;
}
else if (volume > 100)
{
volume = 100;
}
SendMessage(m_PlayerWindow, WM_USER, volume, IPC_SET_VOLUME);
} }
else if (volume > 100)
{
volume = 100;
}
SendMessage(m_PlayerWindow, WM_USER, volume, IPC_SET_VOLUME);
} }
/* /*
@ -464,7 +393,7 @@ void CPlayerCAD::ClosePlayer()
{ {
SendMessage(m_PlayerWindow, WM_USER, 0, IPC_CLOSE_PLAYER); SendMessage(m_PlayerWindow, WM_USER, 0, IPC_CLOSE_PLAYER);
m_PlayerWindow = NULL; m_PlayerWindow = NULL;
ClearInfo(); ClearData();
} }
/* /*
@ -473,21 +402,14 @@ void CPlayerCAD::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerCAD::OpenPlayer() void CPlayerCAD::OpenPlayer(std::wstring& path)
{ {
if (!m_PlayerPath.empty()) if (!path.empty())
{
ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
}
else if (!m_PlayerPath.empty())
{ {
ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW);
} }
} }
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerCAD::TogglePlayer()
{
m_PlayerWindow ? ClosePlayer() : OpenPlayer();
}

View File

@ -27,9 +27,10 @@ public:
CPlayerCAD(); CPlayerCAD();
~CPlayerCAD(); ~CPlayerCAD();
virtual void UpdateData();
virtual void Pause(); virtual void Pause();
virtual void Play(); virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
@ -37,21 +38,16 @@ public:
virtual void SetRating(int rating); virtual void SetRating(int rating);
virtual void SetVolume(int volume); virtual void SetVolume(int volume);
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
void Initialize(); void Initialize();
void Uninitialize(); void Uninitialize();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool m_HasCoverMeasure;
HWND m_Window; HWND m_Window;
HWND m_PlayerWindow; HWND m_PlayerWindow;
std::wstring m_PlayerPath;
}; };
#endif #endif

View File

@ -28,7 +28,6 @@ extern CPlayer* g_Foobar;
** **
*/ */
CPlayerFoobar::CPlayerFoobar() : CPlayer(), CPlayerFoobar::CPlayerFoobar() : CPlayer(),
m_HasCoverMeasure(false),
m_Window(), m_Window(),
m_FooWindow() m_FooWindow()
{ {
@ -43,40 +42,10 @@ CPlayerFoobar::CPlayerFoobar() : CPlayer(),
*/ */
CPlayerFoobar::~CPlayerFoobar() CPlayerFoobar::~CPlayerFoobar()
{ {
g_Foobar = NULL;
Uninitialize(); Uninitialize();
} }
/*
** AddInstance
**
** Called during initialization of each measure.
**
*/
void CPlayerFoobar::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
if (type == MEASURE_COVER)
{
m_HasCoverMeasure = true;
}
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerFoobar::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_Foobar = NULL;
delete this;
}
}
/* /*
** Initialize ** Initialize
** **
@ -150,7 +119,7 @@ void CPlayerFoobar::Uninitialize()
*/ */
LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
static CPlayerFoobar* foobar; static CPlayerFoobar* player;
switch (msg) switch (msg)
{ {
@ -158,7 +127,7 @@ LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
{ {
// Get pointer to the CPlayerFoobar class from the CreateWindow call // Get pointer to the CPlayerFoobar class from the CreateWindow call
LPVOID params = ((CREATESTRUCT*)lParam)->lpCreateParams; LPVOID params = ((CREATESTRUCT*)lParam)->lpCreateParams;
foobar = (CPlayerFoobar*)params; player = (CPlayerFoobar*)params;
return 0; return 0;
} }
@ -170,33 +139,33 @@ LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
case FOO_STATECHANGE: case FOO_STATECHANGE:
{ {
PLAYERSTATE ps = (PLAYERSTATE)wParam; PLAYSTATE ps = (PLAYSTATE)wParam;
if (ps == PLAYER_STOPPED) if (ps == PLAYER_STOPPED)
{ {
foobar->ClearInfo(); player->ClearData();
} }
else else
{ {
foobar->m_State = ps; player->m_State = ps;
} }
} }
break; break;
case FOO_TIMECHANGE: case FOO_TIMECHANGE:
foobar->m_Position = (UINT)wParam; player->m_Position = (UINT)wParam;
break; break;
case FOO_VOLUMECHANGE: case FOO_VOLUMECHANGE:
foobar->m_Volume = (UINT)wParam; player->m_Volume = (UINT)wParam;
break; break;
case FOO_PLAYERSTART: case FOO_PLAYERSTART:
foobar->m_FooWindow = (HWND)wParam; player->m_FooWindow = (HWND)wParam;
break; break;
case FOO_PLAYERQUIT: case FOO_PLAYERQUIT:
foobar->m_FooWindow = NULL; player->m_FooWindow = NULL;
foobar->ClearInfo(); player->ClearData();
break; break;
} }
return 0; return 0;
@ -207,51 +176,52 @@ LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
if (cds->dwData == FOO_TRACKCHANGE) if (cds->dwData == FOO_TRACKCHANGE)
{ {
if (foobar->m_State != PLAYER_PLAYING) if (player->m_State != PLAYER_PLAYING)
{ {
foobar->m_State = PLAYER_PLAYING; player->m_State = PLAYER_PLAYING;
} }
// In the format "TITLE ARTIST ALBUM LENGTH RATING" (seperated by \t) // In the format "TITLE ARTIST ALBUM LENGTH RATING" (seperated by \t)
WCHAR buffer[1024]; WCHAR buffer[1024];
MultiByteToWideChar(CP_UTF8, 0, (char*)cds->lpData, cds->cbData, buffer, 1024); MultiByteToWideChar(CP_UTF8, 0, (char*)cds->lpData, cds->cbData, buffer, 1024);
foobar->m_Artist = buffer; player->m_Artist = buffer;
WCHAR* token = wcstok(buffer, L"\t"); WCHAR* token = wcstok(buffer, L"\t");
if (token) if (token)
{ {
foobar->m_Title = token; player->m_Title = token;
} }
token = wcstok(NULL, L"\t"); token = wcstok(NULL, L"\t");
if (token) if (token)
{ {
foobar->m_Artist = token; player->m_Artist = token;
} }
token = wcstok(NULL, L"\t"); token = wcstok(NULL, L"\t");
if (token) if (token)
{ {
foobar->m_Album = token; player->m_Album = token;
} }
token = wcstok(NULL, L"\t"); token = wcstok(NULL, L"\t");
if (token) if (token)
{ {
foobar->m_Duration = _wtoi(token); player->m_Duration = _wtoi(token);
} }
token = wcstok(NULL, L"\t"); token = wcstok(NULL, L"\t");
if (token) if (token)
{ {
foobar->m_Rating = _wtoi(token); player->m_Rating = _wtoi(token);
} }
token = wcstok(NULL, L"\t"); token = wcstok(NULL, L"\t");
if (token) if (token && wcscmp(token, player->m_FilePath.c_str()) != 0)
{ {
if (wcscmp(token, foobar->m_FilePath.c_str()) != 0) // If different file
++player->m_TrackCount;
player->m_FilePath = token;
player->m_Position = 0;
if (player->m_HasCoverMeasure || player->m_InstanceCount == 0)
{ {
// If different file GetCover(player->m_Artist, player->m_Title, player->m_FilePath, player->m_CoverPath);
foobar->m_FilePath = token;
foobar->m_TrackChanged = true;
foobar->m_Position = 0;
foobar->GetCoverArt(token);
} }
} }
} }
@ -263,47 +233,6 @@ LRESULT CALLBACK CPlayerFoobar::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
} }
} }
/*
** GetCoverArt
**
** Try to find cover art for file.
**
*/
void CPlayerFoobar::GetCoverArt(LPTSTR filename)
{
// TODO: Fix temp solution
if (m_HasCoverMeasure || m_InstanceCount == 0)
{
if (GetCachedArt())
{
// Cover is in cache, lets use the that
return;
}
TagLib::FileRef fr(filename);
if (!fr.isNull() && fr.tag() && GetEmbeddedArt(fr))
{
// Embedded art found
return;
}
// Get rid of the name and extension from filename
std::wstring trackFolder = filename;
std::wstring::size_type pos = trackFolder.find_last_of(L'\\');
if (pos == std::wstring::npos) return;
trackFolder.resize(++pos);
if (GetLocalArt(trackFolder, L"cover") || GetLocalArt(trackFolder, L"folder"))
{
// Local art found
return;
}
// Nothing found
m_CoverPath.clear();
}
}
/* /*
** UpdateData ** UpdateData
** **
@ -312,11 +241,6 @@ void CPlayerFoobar::GetCoverArt(LPTSTR filename)
*/ */
void CPlayerFoobar::UpdateData() void CPlayerFoobar::UpdateData()
{ {
if (m_TrackChanged)
{
ExecuteTrackChangeAction();
m_TrackChanged = false;
}
} }
/* /*
@ -327,10 +251,7 @@ void CPlayerFoobar::UpdateData()
*/ */
void CPlayerFoobar::Pause() void CPlayerFoobar::Pause()
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, 0, FOO_PAUSE);
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_PAUSE);
}
} }
/* /*
@ -341,24 +262,7 @@ void CPlayerFoobar::Pause()
*/ */
void CPlayerFoobar::Play() void CPlayerFoobar::Play()
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, 0, FOO_PLAY);
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_PLAY);
}
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerFoobar::PlayPause()
{
if (m_FooWindow)
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_PLAYPAUSE);
}
} }
/* /*
@ -369,10 +273,7 @@ void CPlayerFoobar::PlayPause()
*/ */
void CPlayerFoobar::Stop() void CPlayerFoobar::Stop()
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, 0, FOO_STOP);
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_STOP);
}
} }
/* /*
@ -383,10 +284,7 @@ void CPlayerFoobar::Stop()
*/ */
void CPlayerFoobar::Next() void CPlayerFoobar::Next()
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, 0, FOO_NEXT);
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_NEXT);
}
} }
/* /*
@ -397,10 +295,7 @@ void CPlayerFoobar::Next()
*/ */
void CPlayerFoobar::Previous() void CPlayerFoobar::Previous()
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, 0, FOO_PREVIOUS);
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_PREVIOUS);
}
} }
/* /*
@ -411,10 +306,7 @@ void CPlayerFoobar::Previous()
*/ */
void CPlayerFoobar::SetPosition(int position) void CPlayerFoobar::SetPosition(int position)
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, position, FOO_SETPOSITION);
{
SendMessage(m_FooWindow, WM_USER, position, FOO_SETPOSITION);
}
} }
/* /*
@ -425,10 +317,7 @@ void CPlayerFoobar::SetPosition(int position)
*/ */
void CPlayerFoobar::SetVolume(int volume) void CPlayerFoobar::SetVolume(int volume)
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, volume, FOO_SETVOLUME);
{
SendMessage(m_FooWindow, WM_USER, volume, FOO_SETVOLUME);
}
} }
/* /*
@ -439,10 +328,7 @@ void CPlayerFoobar::SetVolume(int volume)
*/ */
void CPlayerFoobar::ClosePlayer() void CPlayerFoobar::ClosePlayer()
{ {
if (m_FooWindow) SendMessage(m_FooWindow, WM_USER, 0, FOO_QUITPLAYER);
{
SendMessage(m_FooWindow, WM_USER, 0, FOO_QUITPLAYER);
}
} }
/* /*
@ -451,11 +337,11 @@ void CPlayerFoobar::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerFoobar::OpenPlayer() void CPlayerFoobar::OpenPlayer(std::wstring& path)
{ {
if (!m_FooWindow) if (!m_FooWindow)
{ {
if (m_PlayerPath.empty()) if (path.empty())
{ {
// Gotta figure out where foobar2000 is located at // Gotta figure out where foobar2000 is located at
HKEY hKey; HKEY hKey;
@ -478,7 +364,7 @@ void CPlayerFoobar::OpenPlayer()
{ {
if (type == REG_SZ && data[0] == L'\"') if (type == REG_SZ && data[0] == L'\"')
{ {
std::wstring path = data; path = data;
path.erase(0, 1); // Get rid of the leading quote path.erase(0, 1); // Get rid of the leading quote
std::wstring::size_type pos = path.find_first_of(L'\"'); std::wstring::size_type pos = path.find_first_of(L'\"');
@ -486,7 +372,11 @@ void CPlayerFoobar::OpenPlayer()
{ {
path.resize(pos); // Get rid the last quote and everything after it path.resize(pos); // Get rid the last quote and everything after it
ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
m_PlayerPath = path; path = path;
}
else
{
path.clear();
} }
} }
} }
@ -496,7 +386,7 @@ void CPlayerFoobar::OpenPlayer()
} }
else else
{ {
ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
} }
} }
else else
@ -504,14 +394,3 @@ void CPlayerFoobar::OpenPlayer()
SendMessage(m_FooWindow, WM_USER, 0, FOO_SHOWPLAYER); SendMessage(m_FooWindow, WM_USER, 0, FOO_SHOWPLAYER);
} }
} }
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerFoobar::TogglePlayer()
{
m_FooWindow ? ClosePlayer() : OpenPlayer();
}

View File

@ -27,9 +27,10 @@ public:
CPlayerFoobar(); CPlayerFoobar();
~CPlayerFoobar(); ~CPlayerFoobar();
virtual void UpdateData();
virtual void Pause(); virtual void Pause();
virtual void Play(); virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
@ -37,12 +38,7 @@ public:
virtual void SetRating(int rating) {} virtual void SetRating(int rating) {}
virtual void SetVolume(int volume); virtual void SetVolume(int volume);
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
enum FOOMESSAGE enum FOOMESSAGE
@ -76,9 +72,7 @@ private:
void Initialize(); void Initialize();
void Uninitialize(); void Uninitialize();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void GetCoverArt(LPTSTR filename);
bool m_HasCoverMeasure;
HWND m_Window; // Our reciever window HWND m_Window; // Our reciever window
HWND m_FooWindow; // Foobar receiver window HWND m_FooWindow; // Foobar receiver window
}; };

View File

@ -114,10 +114,7 @@ HRESULT STDMETHODCALLTYPE CPlayerITunes::CEventHandler::Invoke(DISPID dispidMemb
** **
*/ */
CPlayerITunes::CPlayerITunes() : CPlayer(), CPlayerITunes::CPlayerITunes() : CPlayer(),
m_Initialized(false),
m_UserQuitPrompt(false), m_UserQuitPrompt(false),
m_HasCoverMeasure(false),
m_Window(),
m_iTunes(), m_iTunes(),
m_iTunesEvent() m_iTunesEvent()
{ {
@ -132,41 +129,11 @@ CPlayerITunes::CPlayerITunes() : CPlayer(),
*/ */
CPlayerITunes::~CPlayerITunes() CPlayerITunes::~CPlayerITunes()
{ {
g_iTunes = NULL;
Uninitialize(); Uninitialize();
CoUninitialize(); CoUninitialize();
} }
/*
** AddInstance
**
** Called during initialization of each measure.
**
*/
void CPlayerITunes::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
if (type == MEASURE_COVER)
{
m_HasCoverMeasure = true;
}
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerITunes::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_iTunes = NULL;
delete this;
}
}
/* /*
** Initialize ** Initialize
** **
@ -256,11 +223,17 @@ void CPlayerITunes::Uninitialize()
delete m_iTunesEvent; delete m_iTunesEvent;
} }
ClearInfo(); ClearData();
} }
} }
bool CPlayerITunes::CheckActive() /*
** CheckWindow
**
** Try to find iTunes periodically.
**
*/
bool CPlayerITunes::CheckWindow()
{ {
static DWORD oldTime = 0; static DWORD oldTime = 0;
DWORD time = GetTickCount(); DWORD time = GetTickCount();
@ -268,36 +241,9 @@ bool CPlayerITunes::CheckActive()
if (time - oldTime > 5000) if (time - oldTime > 5000)
{ {
oldTime = time; oldTime = time;
m_Window = FindWindow(L"iTunes", L"iTunes");
return m_Window ? true : false;
}
return false; HWND wnd = FindWindow(L"iTunes", L"iTunes");
} if (wnd)
/*
** UpdateData
**
** Called during each update of the main measure.
**
*/
void CPlayerITunes::UpdateData()
{
if (m_Initialized)
{
if (m_TrackChanged)
{
ExecuteTrackChangeAction();
m_TrackChanged = false;
}
long position;
m_iTunes->get_PlayerPosition(&position);
m_Position = (UINT)position;
}
else
{
if (CheckActive())
{ {
if (!m_UserQuitPrompt) if (!m_UserQuitPrompt)
{ {
@ -309,6 +255,24 @@ void CPlayerITunes::UpdateData()
m_UserQuitPrompt = false; m_UserQuitPrompt = false;
} }
} }
return m_Initialized;
}
/*
** UpdateData
**
** Called during each update of the main measure.
**
*/
void CPlayerITunes::UpdateData()
{
if ((m_Initialized || CheckWindow()) && m_State != PLAYER_STOPPED)
{
long position;
m_iTunes->get_PlayerPosition(&position);
m_Position = (UINT)position;
}
} }
/* /*
@ -352,50 +316,47 @@ void CPlayerITunes::OnTrackChange()
file->Release(); file->Release();
if (tmpStr && wcscmp(tmpStr, m_FilePath.c_str()) != 0) if (tmpStr && wcscmp(tmpStr, m_FilePath.c_str()) != 0)
{ {
++m_TrackCount;
m_FilePath = tmpStr; m_FilePath = tmpStr;
m_TrackChanged = true;
if (m_HasCoverMeasure) if (m_HasCoverMeasure && !GetCachedCover(m_Artist, m_Title, m_CoverPath))
{ {
if (!GetCachedArt()) // Art not in cache, check for embedded art
IITArtworkCollection* artworkCollection;
hr = track->get_Artwork(&artworkCollection);
if (SUCCEEDED(hr))
{ {
// Art not in cache, check for embedded art long count;
IITArtworkCollection* artworkCollection; artworkCollection->get_Count(&count);
hr = track->get_Artwork(&artworkCollection);
if (SUCCEEDED(hr)) if (count > 0)
{ {
long count; IITArtwork* artwork;
artworkCollection->get_Count(&count); hr = artworkCollection->get_Item(1, &artwork);
if (count > 0) if (SUCCEEDED(hr))
{ {
IITArtwork* artwork; tmpStr = m_CoverPath.c_str();
hr = artworkCollection->get_Item(1, &artwork); hr = artwork->SaveArtworkToFile(tmpStr);
if (FAILED(hr))
if (SUCCEEDED(hr))
{ {
tmpStr = m_CoverPath.c_str(); m_CoverPath.clear();
hr = artwork->SaveArtworkToFile(tmpStr);
if (FAILED(hr))
{
m_CoverPath.clear();
}
artwork->Release();
} }
}
else
{
m_CoverPath.clear();
}
artworkCollection->Release(); artwork->Release();
}
} }
else else
{ {
m_CoverPath.clear(); m_CoverPath.clear();
} }
artworkCollection->Release();
}
else
{
m_CoverPath.clear();
} }
} }
} }
@ -405,7 +366,7 @@ void CPlayerITunes::OnTrackChange()
} }
else else
{ {
ClearInfo(); ClearData();
} }
} }
@ -447,10 +408,7 @@ void CPlayerITunes::OnVolumeChange(int volume)
*/ */
void CPlayerITunes::Pause() void CPlayerITunes::Pause()
{ {
if (m_Initialized) m_iTunes->Pause();
{
m_iTunes->Pause();
}
} }
/* /*
@ -461,24 +419,7 @@ void CPlayerITunes::Pause()
*/ */
void CPlayerITunes::Play() void CPlayerITunes::Play()
{ {
if (m_Initialized) m_iTunes->Play();
{
m_iTunes->Play();
}
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerITunes::PlayPause()
{
if (m_Initialized)
{
m_iTunes->PlayPause();
}
} }
/* /*
@ -489,10 +430,7 @@ void CPlayerITunes::PlayPause()
*/ */
void CPlayerITunes::Stop() void CPlayerITunes::Stop()
{ {
if (m_Initialized) m_iTunes->Stop();
{
m_iTunes->Stop();
}
} }
/* /*
@ -503,10 +441,7 @@ void CPlayerITunes::Stop()
*/ */
void CPlayerITunes::Next() void CPlayerITunes::Next()
{ {
if (m_Initialized) m_iTunes->NextTrack();
{
m_iTunes->NextTrack();
}
} }
/* /*
@ -517,10 +452,7 @@ void CPlayerITunes::Next()
*/ */
void CPlayerITunes::Previous() void CPlayerITunes::Previous()
{ {
if (m_Initialized) m_iTunes->PreviousTrack();
{
m_iTunes->PreviousTrack();
}
} }
/* /*
@ -531,10 +463,7 @@ void CPlayerITunes::Previous()
*/ */
void CPlayerITunes::SetPosition(int position) void CPlayerITunes::SetPosition(int position)
{ {
if (m_Initialized) m_iTunes->put_PlayerPosition((long)position);
{
m_iTunes->put_PlayerPosition((long)position);
}
} }
/* /*
@ -545,17 +474,14 @@ void CPlayerITunes::SetPosition(int position)
*/ */
void CPlayerITunes::SetRating(int rating) void CPlayerITunes::SetRating(int rating)
{ {
if (m_Initialized) IITTrack* track;
HRESULT hr = m_iTunes->get_CurrentTrack(&track);
if (SUCCEEDED(hr))
{ {
rating *= 20; rating *= 20;
IITTrack* track; track->put_Rating((long)rating);
HRESULT hr = m_iTunes->get_CurrentTrack(&track); track->Release();
if (SUCCEEDED(hr))
{
track->put_Rating((long)rating);
track->Release();
}
} }
} }
@ -567,10 +493,7 @@ void CPlayerITunes::SetRating(int rating)
*/ */
void CPlayerITunes::SetVolume(int volume) void CPlayerITunes::SetVolume(int volume)
{ {
if (m_Initialized) m_iTunes->put_SoundVolume((long)volume);
{
m_iTunes->put_SoundVolume((long)volume);
}
} }
/* /*
@ -581,12 +504,9 @@ void CPlayerITunes::SetVolume(int volume)
*/ */
void CPlayerITunes::ClosePlayer() void CPlayerITunes::ClosePlayer()
{ {
if (m_Initialized) m_UserQuitPrompt = true;
{ m_iTunes->Quit();
m_UserQuitPrompt = true; Uninitialize();
m_iTunes->Quit();
Uninitialize();
}
} }
/* /*
@ -595,18 +515,7 @@ void CPlayerITunes::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerITunes::OpenPlayer() void CPlayerITunes::OpenPlayer(std::wstring& path)
{ {
ShellExecute(NULL, L"open", m_PlayerPath.empty() ? L"iTunes.exe" : m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.empty() ? L"iTunes.exe" : path.c_str(), NULL, NULL, SW_SHOW);
}
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerITunes::TogglePlayer()
{
m_Initialized ? ClosePlayer() : OpenPlayer();
} }

View File

@ -38,9 +38,10 @@ public:
CPlayerITunes(); CPlayerITunes();
~CPlayerITunes(); ~CPlayerITunes();
virtual void UpdateData();
virtual void Pause(); virtual void Pause();
virtual void Play(); virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
@ -48,12 +49,7 @@ public:
virtual void SetRating(int rating); virtual void SetRating(int rating);
virtual void SetVolume(int volume); virtual void SetVolume(int volume);
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
class CEventHandler : public _IiTunesEvents class CEventHandler : public _IiTunesEvents
@ -85,13 +81,9 @@ private:
void OnTrackChange(); void OnTrackChange();
void OnStateChange(bool playing); void OnStateChange(bool playing);
void OnVolumeChange(int volume); void OnVolumeChange(int volume);
bool CheckActive(); bool CheckWindow();
bool m_Initialized;
bool m_UserQuitPrompt; bool m_UserQuitPrompt;
bool m_HasCoverMeasure;
HWND m_Window;
IiTunes* m_iTunes; IiTunes* m_iTunes;
CEventHandler* m_iTunesEvent; CEventHandler* m_iTunesEvent;
}; };

View File

@ -30,7 +30,6 @@ extern CPlayer* g_Spotify;
CPlayerSpotify::CPlayerSpotify() : CPlayer(), CPlayerSpotify::CPlayerSpotify() : CPlayer(),
m_Window() m_Window()
{ {
GetWindow();
} }
/* /*
@ -41,32 +40,33 @@ CPlayerSpotify::CPlayerSpotify() : CPlayer(),
*/ */
CPlayerSpotify::~CPlayerSpotify() CPlayerSpotify::~CPlayerSpotify()
{ {
g_Spotify = NULL;
} }
/* /*
** AddInstance ** CheckWindow
** **
** Called during initialization of each measure. ** Try to find Spotify periodically.
** **
*/ */
void CPlayerSpotify::AddInstance(MEASURETYPE type) bool CPlayerSpotify::CheckWindow()
{ {
++m_InstanceCount; static DWORD oldTime = 0;
} DWORD time = GetTickCount();
/* // Try to find Spotify window every 5 seconds
** RemoveInstance if (time - oldTime > 5000)
**
** Called during destruction of each measure.
**
*/
void CPlayerSpotify::RemoveInstance()
{
if (--m_InstanceCount == 0)
{ {
g_Spotify = NULL; oldTime = time;
delete this;
m_Window = FindWindow(L"SpotifyMainWindow", NULL);
if (m_Window)
{
m_Initialized = true;
}
} }
return m_Initialized;
} }
/* /*
@ -77,59 +77,52 @@ void CPlayerSpotify::RemoveInstance()
*/ */
void CPlayerSpotify::UpdateData() void CPlayerSpotify::UpdateData()
{ {
if (GetWindow()) if (m_Initialized || CheckWindow())
{ {
if (m_TrackChanged) // Parse title and artist from window title
{
ExecuteTrackChangeAction();
m_TrackChanged = false;
}
// Get window text
WCHAR buffer[256]; WCHAR buffer[256];
buffer[0] = 0; if (GetWindowText(m_Window, buffer, 256) > 10)
GetWindowText(m_Window, buffer, 256);
std::wstring title = buffer;
title.erase(0, 10); // Get rid of "Spotify - "
std::wstring::size_type pos = title.find(L" ");
if (pos != std::wstring::npos)
{ {
std::wstring artist = title.substr(0, pos); std::wstring title = buffer;
std::wstring track = title.substr(pos + 3); title.erase(0, 10); // Get rid of "Spotify - "
if (track != m_Title && artist != m_Artist) std::wstring::size_type pos = title.find(L" ");
if (pos != std::wstring::npos)
{ {
m_Title = track; m_State = PLAYER_PLAYING;
m_Artist = artist; std::wstring artist = title.substr(0, pos);
m_TrackChanged = true; std::wstring track = title.substr(pos + 3);
if (track != m_Title && artist != m_Artist)
{
m_Title = track;
m_Artist = artist;
++m_TrackCount;
}
return;
} }
return; }
else if (IsWindow(m_Window))
{
m_State = PLAYER_PAUSED;
}
else
{
ClearData();
m_Initialized = false;
} }
} }
ClearInfo();
}
bool CPlayerSpotify::GetWindow()
{
m_Window = FindWindow(L"SpotifyMainWindow", NULL);
return m_Window ? true : false;
} }
/* /*
** PlayPause ** Play
** **
** Handles the PlayPause bang. ** Handles the Play bang.
** **
*/ */
void CPlayerSpotify::PlayPause() void CPlayerSpotify::Play()
{ {
if (m_Window) SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_PLAYPAUSE);
{
SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_PLAYPAUSE);
}
} }
/* /*
@ -140,10 +133,7 @@ void CPlayerSpotify::PlayPause()
*/ */
void CPlayerSpotify::Stop() void CPlayerSpotify::Stop()
{ {
if (m_Window) SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_STOP);
{
SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_STOP);
}
} }
/* /*
@ -154,10 +144,7 @@ void CPlayerSpotify::Stop()
*/ */
void CPlayerSpotify::Next() void CPlayerSpotify::Next()
{ {
if (m_Window) SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_NEXT);
{
SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_NEXT);
}
} }
/* /*
@ -168,10 +155,7 @@ void CPlayerSpotify::Next()
*/ */
void CPlayerSpotify::Previous() void CPlayerSpotify::Previous()
{ {
if (m_Window) SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_PREV);
{
SendMessage(m_Window, WM_APPCOMMAND, 0, SPOTIFY_PREV);
}
} }
@ -183,17 +167,14 @@ void CPlayerSpotify::Previous()
*/ */
void CPlayerSpotify::ClosePlayer() void CPlayerSpotify::ClosePlayer()
{ {
if (m_Window) // A little harsh...
DWORD pID;
GetWindowThreadProcessId(m_Window, &pID);
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pID);
if (hProcess)
{ {
// A little harsh... TerminateProcess(hProcess, 0);
DWORD pID; CloseHandle(hProcess);
GetWindowThreadProcessId(m_Window, &pID);
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pID);
if (hProcess)
{
TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
}
} }
} }
@ -203,11 +184,11 @@ void CPlayerSpotify::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerSpotify::OpenPlayer() void CPlayerSpotify::OpenPlayer(std::wstring& path)
{ {
if (!m_Window) if (!m_Initialized)
{ {
if (m_PlayerPath.empty()) if (path.empty())
{ {
// Gotta figure out where Winamp is located at // Gotta figure out where Winamp is located at
HKEY hKey; HKEY hKey;
@ -230,11 +211,10 @@ void CPlayerSpotify::OpenPlayer()
{ {
if (type == REG_SZ) if (type == REG_SZ)
{ {
std::wstring path = data; path = data;
path.erase(0, 1); // Get rid of the leading quote path.erase(0, 1); // Get rid of the leading quote
path.resize(path.length() - 3); // And the ",0 at the end path.resize(path.length() - 3); // And the ",0 at the end
ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
m_PlayerPath = path;
} }
} }
@ -243,7 +223,7 @@ void CPlayerSpotify::OpenPlayer()
} }
else else
{ {
ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
} }
} }
else else
@ -253,14 +233,3 @@ void CPlayerSpotify::OpenPlayer()
BringWindowToTop(m_Window); BringWindowToTop(m_Window);
} }
} }
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerSpotify::TogglePlayer()
{
m_Window ? ClosePlayer() : OpenPlayer();
}

View File

@ -21,38 +21,36 @@
#include "Player.h" #include "Player.h"
#define SPOTIFY_PLAYPAUSE 917504
#define SPOTIFY_NEXT 720896
#define SPOTIFY_PREV 786432
#define SPOTIFY_STOP 851968
#define SPOTIFY_MUTE 524288
#define SPOTIFY_VOLUMEDOWN 589824
#define SPOTIFY_VOLUMEUP 655360
class CPlayerSpotify : public CPlayer class CPlayerSpotify : public CPlayer
{ {
public: public:
CPlayerSpotify(); CPlayerSpotify();
~CPlayerSpotify(); ~CPlayerSpotify();
virtual void Pause() { return PlayPause(); } virtual void Pause() { return Play(); }
virtual void Play() { return PlayPause(); } virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData(); virtual void UpdateData();
private: private:
bool GetWindow(); enum SPOTIFYCOMMAND
{
SPOTIFY_MUTE = 524288,
SPOTIFY_VOLUMEDOWN = 589824,
SPOTIFY_VOLUMEUP = 655360,
SPOTIFY_NEXT = 720896,
SPOTIFY_PREV = 786432,
SPOTIFY_STOP = 851968,
SPOTIFY_PLAYPAUSE = 917504
};
HWND m_Window; // Spotify window bool CheckWindow();
HWND m_Window;
}; };
#endif #endif

View File

@ -22,7 +22,7 @@
extern CPlayer* g_WLM; extern CPlayer* g_WLM;
// This player emulates the MSN/WLM Messenger 'Listening to' interface, which is // This player emulates the MSN/WLM Messenger 'Listening to' interface, which is
// supported by OpenPandora, Last.fm, Media Player Classic, TTPlayer, etc. // supported by OpenPandora, Last.fm, Media Player Classic, TTPlayer, Zune, etc.
/* /*
** CPlayerWLM ** CPlayerWLM
@ -64,36 +64,11 @@ CPlayerWLM::CPlayerWLM() : CPlayer(),
*/ */
CPlayerWLM::~CPlayerWLM() CPlayerWLM::~CPlayerWLM()
{ {
g_WLM = NULL;
DestroyWindow(m_Window); DestroyWindow(m_Window);
UnregisterClass(L"MsnMsgrUIManager", GetModuleHandle(NULL)); UnregisterClass(L"MsnMsgrUIManager", GetModuleHandle(NULL));
} }
/*
** AddInstance
**
** Called during initialization of each measure.
**
*/
void CPlayerWLM::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerWLM::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_WLM = NULL;
delete this;
}
}
LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
static CPlayerWLM* player; static CPlayerWLM* player;
@ -127,6 +102,7 @@ LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM
player->m_State = PLAYER_PLAYING; player->m_State = PLAYER_PLAYING;
data.erase(0, 3); // Get rid of the status data.erase(0, 3); // Get rid of the status
// TODO: Handle invalid
len = data.find_first_of(L'\\'); len = data.find_first_of(L'\\');
len += 2; len += 2;
data.erase(0, len); // Get rid of the format data.erase(0, len); // Get rid of the format
@ -146,7 +122,7 @@ LRESULT CALLBACK CPlayerWLM::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM
} }
else else
{ {
player->ClearInfo(); player->ClearData();
} }
return 0; return 0;
@ -180,12 +156,12 @@ void CPlayerWLM::UpdateData()
} }
/* /*
** PlayPause ** Play
** **
** Handles the PlayPause bang. ** Handles the Play bang.
** **
*/ */
void CPlayerWLM::PlayPause() void CPlayerWLM::Play()
{ {
SendKeyInput(VK_MEDIA_PLAY_PAUSE); SendKeyInput(VK_MEDIA_PLAY_PAUSE);
} }

View File

@ -27,22 +27,19 @@ public:
CPlayerWLM(); CPlayerWLM();
~CPlayerWLM(); ~CPlayerWLM();
virtual void Pause() { return PlayPause(); } virtual void UpdateData();
virtual void Play() { return PlayPause(); }
virtual void PlayPause(); virtual void Pause() { return Play(); }
virtual void Play();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void SendKeyInput(WORD key); void SendKeyInput(WORD key);
HWND m_Window; // Spotify window HWND m_Window;
}; };
#endif #endif

View File

@ -111,7 +111,7 @@ void CPlayerWMP::CRemoteHost::PlayStateChange(long NewState)
{ {
case wmppsStopped: case wmppsStopped:
case wmppsMediaEnded: case wmppsMediaEnded:
m_Player->ClearInfo(); m_Player->ClearData();
break; break;
case wmppsPaused: case wmppsPaused:
@ -139,7 +139,7 @@ void CPlayerWMP::CRemoteHost::PlayStateChange(long NewState)
*/ */
void CPlayerWMP::CRemoteHost::SwitchedToControl() void CPlayerWMP::CRemoteHost::SwitchedToControl()
{ {
m_Player->ClearInfo(); m_Player->ClearData();
m_Player->Uninitialize(); m_Player->Uninitialize();
} }
@ -150,8 +150,8 @@ void CPlayerWMP::CRemoteHost::SwitchedToControl()
** **
*/ */
CPlayerWMP::CPlayerWMP() : CPlayer(), CPlayerWMP::CPlayerWMP() : CPlayer(),
m_Initialized(false), m_TrackChanged(false),
m_HasCoverMeasure(false), m_Window(),
m_ComModule(), m_ComModule(),
m_AxWindow(), m_AxWindow(),
m_IPlayer(), m_IPlayer(),
@ -170,41 +170,11 @@ CPlayerWMP::CPlayerWMP() : CPlayer(),
*/ */
CPlayerWMP::~CPlayerWMP() CPlayerWMP::~CPlayerWMP()
{ {
g_WMP = NULL;
Uninitialize(); Uninitialize();
m_ComModule.Term(); m_ComModule.Term();
} }
/*
** AddInstance
**
** Called during initialization of each measure.
**
*/
void CPlayerWMP::AddInstance(MEASURETYPE type)
{
++m_InstanceCount;
if (type == MEASURE_COVER)
{
m_HasCoverMeasure = true;
}
}
/*
** RemoveInstance
**
** Called during destruction of each measure.
**
*/
void CPlayerWMP::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_WMP = NULL;
delete this;
}
}
/* /*
** Initialize ** Initialize
** **
@ -410,7 +380,9 @@ void CPlayerWMP::UpdateData()
if (m_TrackChanged) if (m_TrackChanged)
{ {
++m_TrackCount;
m_TrackChanged = false; m_TrackChanged = false;
CComPtr<IWMPMedia> spMedia; CComPtr<IWMPMedia> spMedia;
m_IPlayer->get_currentMedia(&spMedia); m_IPlayer->get_currentMedia(&spMedia);
@ -462,7 +434,8 @@ void CPlayerWMP::UpdateData()
{ {
m_FilePath = targetPath; m_FilePath = targetPath;
// TODO: Better solution for this // Find cover if needed
// TODO: Fix temp solution
if (m_HasCoverMeasure || m_InstanceCount == 0) if (m_HasCoverMeasure || m_InstanceCount == 0)
{ {
spMedia->getItemInfo(CComBSTR("WM/WMCollectionID"), &val); spMedia->getItemInfo(CComBSTR("WM/WMCollectionID"), &val);
@ -477,37 +450,9 @@ void CPlayerWMP::UpdateData()
} }
else else
{ {
if (GetCachedArt()) GetCover(m_Artist, m_Title, m_FilePath, m_CoverPath);
{
// Cover is in cache, lets use the that
return;
}
TagLib::FileRef fr(url.m_str);
if (!fr.isNull() && fr.tag() && GetEmbeddedArt(fr))
{
// Embedded art found
return;
}
// Get rid of the name and extension from filename
std::wstring trackFolder = url;
std::wstring::size_type pos = trackFolder.find_last_of(L'\\');
if (pos == std::wstring::npos) return;
trackFolder.resize(++pos);
if (GetLocalArt(trackFolder, L"cover") || GetLocalArt(trackFolder, L"folder"))
{
// Local art found
return;
}
// Nothing found
m_CoverPath.clear();
} }
} }
ExecuteTrackChangeAction();
} }
} }
} }
@ -538,10 +483,7 @@ void CPlayerWMP::UpdateData()
*/ */
void CPlayerWMP::Pause() void CPlayerWMP::Pause()
{ {
if (m_IPlayer) m_IControls->pause();
{
m_IControls->pause();
}
} }
/* /*
@ -552,21 +494,7 @@ void CPlayerWMP::Pause()
*/ */
void CPlayerWMP::Play() void CPlayerWMP::Play()
{ {
if (m_IPlayer) m_IControls->play();
{
m_IControls->play();
}
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerWMP::PlayPause()
{
(m_State == PLAYER_PLAYING) ? Pause() : Play();
} }
/* /*
@ -577,11 +505,9 @@ void CPlayerWMP::PlayPause()
*/ */
void CPlayerWMP::Stop() void CPlayerWMP::Stop()
{ {
if (m_IPlayer) m_IControls->stop();
{ // TODO: FIXME
m_IControls->stop(); m_State = PLAYER_STOPPED;
m_State = PLAYER_STOPPED;
}
} }
/* /*
@ -592,10 +518,7 @@ void CPlayerWMP::Stop()
*/ */
void CPlayerWMP::Next() void CPlayerWMP::Next()
{ {
if (m_IPlayer) m_IControls->next();
{
m_IControls->next();
}
} }
/* /*
@ -606,10 +529,7 @@ void CPlayerWMP::Next()
*/ */
void CPlayerWMP::Previous() void CPlayerWMP::Previous()
{ {
if (m_IPlayer) m_IControls->previous();
{
m_IControls->previous();
}
} }
/* /*
@ -620,10 +540,7 @@ void CPlayerWMP::Previous()
*/ */
void CPlayerWMP::SetPosition(int position) void CPlayerWMP::SetPosition(int position)
{ {
if (m_IPlayer) m_IControls->put_currentPosition((double)position);
{
m_IControls->put_currentPosition((double)position);
}
} }
/* /*
@ -634,7 +551,7 @@ void CPlayerWMP::SetPosition(int position)
*/ */
void CPlayerWMP::SetRating(int rating) void CPlayerWMP::SetRating(int rating)
{ {
if (m_IPlayer && (m_State == PLAYER_PLAYING || m_State == PLAYER_PAUSED)) if (m_State != PLAYER_STOPPED)
{ {
CComPtr<IWMPMedia> spMedia; CComPtr<IWMPMedia> spMedia;
m_IPlayer->get_currentMedia(&spMedia); m_IPlayer->get_currentMedia(&spMedia);
@ -683,10 +600,7 @@ void CPlayerWMP::SetRating(int rating)
*/ */
void CPlayerWMP::SetVolume(int volume) void CPlayerWMP::SetVolume(int volume)
{ {
if (m_IPlayer) m_ISettings->put_volume(volume);
{
m_ISettings->put_volume(volume);
}
} }
/* /*
@ -697,14 +611,11 @@ void CPlayerWMP::SetVolume(int volume)
*/ */
void CPlayerWMP::ClosePlayer() void CPlayerWMP::ClosePlayer()
{ {
if (m_IPlayer) HWND wnd = FindWindow(L"WMPlayerApp", NULL);
{
HWND wmp = FindWindow(L"WMPlayerApp", NULL);
if (wmp) if (wnd)
{ {
SendMessage(wmp, WM_CLOSE, 0, 0); SendMessage(wnd, WM_CLOSE, 0, 0);
}
} }
} }
@ -714,18 +625,7 @@ void CPlayerWMP::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerWMP::OpenPlayer() void CPlayerWMP::OpenPlayer(std::wstring& path)
{ {
ShellExecute(NULL, L"open", m_PlayerPath.empty() ? L"wmplayer.exe" : m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.empty() ? L"wmplayer.exe" : path.c_str(), NULL, NULL, SW_SHOW);
}
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerWMP::TogglePlayer()
{
m_IPlayer ? ClosePlayer() : OpenPlayer();
} }

View File

@ -37,22 +37,18 @@ public:
CPlayerWMP(); CPlayerWMP();
~CPlayerWMP(); ~CPlayerWMP();
virtual void UpdateData();
virtual void Pause(); virtual void Pause();
virtual void Play(); virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
virtual void SetPosition(int position); virtual void SetPosition(int position);
virtual void SetRating(int rating); virtual void SetRating(int rating);
virtual void SetVolume(int volume); virtual void SetVolume(int volume);
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
class CRemoteHost : class CRemoteHost :
@ -133,11 +129,10 @@ private:
void Initialize(); void Initialize();
void Uninitialize(); void Uninitialize();
bool m_Initialized; bool m_TrackChanged;
bool m_HasCoverMeasure;
HWND m_Window; HWND m_Window;
CComModule m_ComModule;
CAxWindow* m_AxWindow; CAxWindow* m_AxWindow;
CComModule m_ComModule;
CComPtr<IWMPPlayer4> m_IPlayer; CComPtr<IWMPPlayer4> m_IPlayer;
CComPtr<IWMPControls> m_IControls; CComPtr<IWMPControls> m_IControls;
CComPtr<IWMPSettings> m_ISettings; CComPtr<IWMPSettings> m_ISettings;

View File

@ -34,10 +34,8 @@ extern CPlayer* g_Winamp;
CPlayerWinamp::CPlayerWinamp(WINAMPTYPE type) : CPlayer(), CPlayerWinamp::CPlayerWinamp(WINAMPTYPE type) : CPlayer(),
m_WinampType(type), m_WinampType(type),
m_UseUnicodeAPI(false), m_UseUnicodeAPI(false),
m_HasCoverMeasure(false),
m_Window() m_Window()
{ {
Initialize();
} }
/* /*
@ -48,102 +46,43 @@ CPlayerWinamp::CPlayerWinamp(WINAMPTYPE type) : CPlayer(),
*/ */
CPlayerWinamp::~CPlayerWinamp() CPlayerWinamp::~CPlayerWinamp()
{ {
g_Winamp = NULL;
if (m_WinampHandle) CloseHandle(m_WinampHandle); if (m_WinampHandle) CloseHandle(m_WinampHandle);
} }
/* /*
** AddInstance ** CheckWindow
** **
** Called during initialization of each measure. ** Try to find Winamp periodically.
** **
*/ */
void CPlayerWinamp::AddInstance(MEASURETYPE type) bool CPlayerWinamp::CheckWindow()
{ {
++m_InstanceCount; static DWORD oldTime = 0;
DWORD time = GetTickCount();
if (type == MEASURE_COVER) // Try to find Winamp window every 5 seconds
if (time - oldTime > 5000)
{ {
m_HasCoverMeasure = true; oldTime = time;
}
}
/* m_Window = FindWindow(L"Winamp v1.x", NULL);
** RemoveInstance if (m_Window)
**
** Called during destruction of each measure.
**
*/
void CPlayerWinamp::RemoveInstance()
{
if (--m_InstanceCount == 0)
{
g_Winamp = NULL;
delete this;
}
}
/*
** Initialize
**
** Get things ready with Winamp.
**
*/
bool CPlayerWinamp::Initialize()
{
m_Window = FindWindow(L"Winamp v1.x", NULL);
if (m_Window)
{
DWORD pID;
GetWindowThreadProcessId(m_Window, &pID);
m_WinampHandle = OpenProcess(PROCESS_VM_READ, false, pID);
if (m_WinampHandle)
{ {
m_WinampAddress = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, 0, IPC_GET_PLAYING_FILENAME); DWORD pID;
m_UseUnicodeAPI = m_WinampAddress ? true : false; GetWindowThreadProcessId(m_Window, &pID);
return true; m_WinampHandle = OpenProcess(PROCESS_VM_READ, false, pID);
if (m_WinampHandle)
{
m_WinampAddress = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, 0, IPC_GET_PLAYING_FILENAME);
m_UseUnicodeAPI = m_WinampAddress ? true : false;
m_Initialized = true;
}
} }
} }
return false; return m_Initialized;
}
/*
** CheckActive
**
** Check if Winamp is active.
**
*/
bool CPlayerWinamp::CheckActive()
{
if (m_Window)
{
if (!IsWindow(m_Window))
{
m_Window = NULL;
CloseHandle(m_WinampHandle);
ClearInfo();
return false;
}
return true;
}
else
{
static DWORD oldTime = 0;
DWORD time = GetTickCount();
// Try to find Winamp window every 5 seconds
if (time - oldTime > 5000)
{
oldTime = time;
return Initialize();
}
return false;
}
} }
/* /*
@ -154,181 +93,175 @@ bool CPlayerWinamp::CheckActive()
*/ */
void CPlayerWinamp::UpdateData() void CPlayerWinamp::UpdateData()
{ {
if (!CheckActive()) return; // Make sure Winamp is running if (m_Initialized || CheckWindow())
if (m_TrackChanged)
{ {
ExecuteTrackChangeAction(); int playing = SendMessage(m_Window, WM_WA_IPC, 0, IPC_ISPLAYING);
m_TrackChanged = false; if (playing == 0)
}
int playing = SendMessage(m_Window, WM_WA_IPC, 0, IPC_ISPLAYING);
if (playing == 0)
{
if (!m_FilePath.empty())
{ {
ClearInfo(); // Make sure Winamp is still active
} if (!IsWindow(m_Window))
return; // Don't continue if stopped {
} m_Initialized = false;
else if (m_WinampHandle) CloseHandle(m_WinampHandle);
{ }
m_State = (playing == 1) ? PLAYER_PLAYING : PLAYER_PAUSED;
m_Position = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000; // Returns ms, make seconds
float volume = SendMessage(m_Window, WM_WA_IPC, -666, IPC_SETVOLUME); if (!m_FilePath.empty())
volume /= 2.55f; {
m_Volume = (UINT)volume; ClearData();
} }
BOOL ret; // Don't continue if Winamp has quit or is stopped
WCHAR wBuffer[MAX_PATH]; return;
char cBuffer[MAX_PATH];
if (m_UseUnicodeAPI)
{
ret = ReadProcessMemory(m_WinampHandle, m_WinampAddress, &wBuffer, MAX_PATH, NULL);
}
else
{
// MediaMonkey doesn't support wide IPC messages
int pos = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETLISTPOS);
LPCVOID address = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, pos, IPC_GETPLAYLISTFILE);
ret = ReadProcessMemory(m_WinampHandle, address, &cBuffer, MAX_PATH, NULL);
mbstowcs(wBuffer, cBuffer, MAX_PATH);
}
if (!ret)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Failed to read Winamp memory (file).");
return;
}
if (wcscmp(wBuffer, m_FilePath.c_str()) != 0)
{
m_TrackChanged = true;
m_FilePath = wBuffer;
m_Rating = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETRATING);
m_Duration = SendMessage(m_Window, WM_WA_IPC, 1, IPC_GETOUTPUTTIME);
TagLib::FileRef fr(wBuffer);
if (!fr.isNull() && fr.tag())
{
TagLib::Tag* tag = fr.tag();
m_Artist = tag->artist().toWString();
m_Album = tag->album().toWString();
m_Title = tag->title().toWString();
} }
else else
{ {
// TagLib couldn't parse the file, try title instead m_State = (playing == 1) ? PLAYER_PLAYING : PLAYER_PAUSED;
if (m_UseUnicodeAPI) m_Position = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000; // ms to secs
{ m_Volume = (SendMessage(m_Window, WM_WA_IPC, -666, IPC_SETVOLUME) * 100) / 255; // 0 - 255 to 0 - 100
LPCVOID address = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE);
ret = ReadProcessMemory(m_WinampHandle, address, &wBuffer, MAX_PATH, NULL);
}
else
{
int pos = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETLISTPOS);
LPCVOID address = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, pos, IPC_GETPLAYLISTTITLE);
ReadProcessMemory(m_WinampHandle, m_WinampAddress, &cBuffer, MAX_PATH, NULL);
ret = mbstowcs(wBuffer, cBuffer, MAX_PATH);
}
if (!ret)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Failed to read Winamp memory (title).");
return;
}
std::wstring title = wBuffer;
std::wstring::size_type pos = title.find(L". ");
if (pos != std::wstring::npos && pos < 5)
{
pos += 2; // Skip ". "
title.erase(0, pos);
}
pos = title.find(L" - ");
if (pos != std::wstring::npos)
{
m_Title = title.substr(0, pos);
pos += 3; // Skip " - "
m_Artist = title.substr(pos);
m_Album.clear();
}
else
{
ClearInfo();
return;
}
} }
if (m_HasCoverMeasure) BOOL ret;
WCHAR wBuffer[MAX_PATH];
char cBuffer[MAX_PATH];
if (m_UseUnicodeAPI)
{ {
if (GetCachedArt() || GetEmbeddedArt(fr)) ret = ReadProcessMemory(m_WinampHandle, m_WinampAddress, &wBuffer, MAX_PATH, NULL);
}
else
{
// MediaMonkey doesn't support wide IPC messages
int pos = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETLISTPOS);
LPCVOID address = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, pos, IPC_GETPLAYLISTFILE);
ret = ReadProcessMemory(m_WinampHandle, address, &cBuffer, MAX_PATH, NULL);
mbstowcs(wBuffer, cBuffer, MAX_PATH);
}
if (!ret)
{
LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Failed to read Winamp memory (file).");
return;
}
if (wcscmp(wBuffer, m_FilePath.c_str()) != 0)
{
++m_TrackCount;
m_FilePath = wBuffer;
m_Rating = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETRATING);
m_Duration = SendMessage(m_Window, WM_WA_IPC, 1, IPC_GETOUTPUTTIME);
TagLib::FileRef fr(wBuffer);
if (!fr.isNull() && fr.tag())
{ {
// Art found in cache or embedded in file TagLib::Tag* tag = fr.tag();
return; m_Artist = tag->artist().toWString();
m_Album = tag->album().toWString();
m_Title = tag->title().toWString();
} }
else
// Get rid of the name and extension from filename
std::wstring trackFolder = m_FilePath;
std::wstring::size_type pos = trackFolder.find_last_of(L'\\');
if (pos == std::wstring::npos) return;
trackFolder.resize(++pos);
if (!m_Album.empty())
{ {
std::wstring file = m_Album; // TagLib couldn't parse the file, try title instead
std::wstring::size_type end = file.length(); if (m_UseUnicodeAPI)
for (pos = 0; pos < end; ++pos)
{ {
// Replace reserved chars according to Winamp specs LPCVOID address = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE);
switch (file[pos]) ret = ReadProcessMemory(m_WinampHandle, address, &wBuffer, MAX_PATH, NULL);
{ }
case L'?': else
case L'*': {
case L'|': int pos = SendMessage(m_Window, WM_WA_IPC, 0, IPC_GETLISTPOS);
file[pos] = L'_'; LPCVOID address = (LPCVOID)SendMessage(m_Window, WM_WA_IPC, pos, IPC_GETPLAYLISTTITLE);
break; ReadProcessMemory(m_WinampHandle, m_WinampAddress, &cBuffer, MAX_PATH, NULL);
ret = mbstowcs(wBuffer, cBuffer, MAX_PATH);
case L'/':
case L'\\':
case L':':
file[pos] = L'-';
break;
case L'\"':
file[pos] = L'\'';
break;
case L'<':
file[pos] = L'(';
break;
case L'>':
file[pos] = L')';
break;
}
} }
if (GetLocalArt(trackFolder, file)) if (!ret)
{ {
// %album% art file found LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Failed to read Winamp memory (title).");
return;
}
std::wstring title = wBuffer;
std::wstring::size_type pos = title.find(L". ");
if (pos != std::wstring::npos && pos < 5)
{
pos += 2; // Skip ". "
title.erase(0, pos);
}
pos = title.find(L" - ");
if (pos != std::wstring::npos)
{
m_Title = title.substr(0, pos);
pos += 3; // Skip " - "
m_Artist = title.substr(pos);
m_Album.clear();
}
else
{
ClearData();
return; return;
} }
} }
if (GetLocalArt(trackFolder, L"cover") || GetLocalArt(trackFolder, L"folder")) // Find cover if needed
if (m_HasCoverMeasure &&
!GetCachedCover(m_Artist, m_Title, m_CoverPath) &&
!GetEmbeddedCover(fr, m_CoverPath))
{ {
// Local art found std::wstring trackFolder = GetFileFolder(m_FilePath);
return;
}
// Nothing found if (!m_Album.empty())
m_CoverPath.clear(); {
// Winamp stores covers usually as %album%.jpg
std::wstring file = m_Album;
std::wstring::size_type end = file.length();
for (std::wstring::size_type pos = 0; pos < end; ++pos)
{
// Replace reserved chars according to Winamp specs
switch (file[pos])
{
case L'?':
case L'*':
case L'|':
file[pos] = L'_';
break;
case L'/':
case L'\\':
case L':':
file[pos] = L'-';
break;
case L'\"':
file[pos] = L'\'';
break;
case L'<':
file[pos] = L'(';
break;
case L'>':
file[pos] = L')';
break;
}
}
if (GetLocalCover(file, trackFolder, m_CoverPath))
{
// %album% art file found
return;
}
}
if (!GetLocalCover(L"cover", trackFolder, m_CoverPath) &&
!GetLocalCover(L"folder", trackFolder, m_CoverPath))
{
// Nothing found
m_CoverPath.clear();
}
}
} }
} }
} }
@ -341,10 +274,7 @@ void CPlayerWinamp::UpdateData()
*/ */
void CPlayerWinamp::Pause() void CPlayerWinamp::Pause()
{ {
if (m_Window) SendMessage(m_Window, WM_COMMAND, WINAMP_PAUSE, 0);
{
SendMessage(m_Window, WM_COMMAND, WINAMP_PAUSE, 0);
}
} }
/* /*
@ -355,21 +285,7 @@ void CPlayerWinamp::Pause()
*/ */
void CPlayerWinamp::Play() void CPlayerWinamp::Play()
{ {
if (m_Window) SendMessage(m_Window, WM_COMMAND, WINAMP_PLAY, 0);
{
SendMessage(m_Window, WM_COMMAND, WINAMP_PLAY, 0);
}
}
/*
** PlayPause
**
** Handles the PlayPause bang.
**
*/
void CPlayerWinamp::PlayPause()
{
(m_State == PLAYER_PLAYING) ? Pause() : Play();
} }
/* /*
@ -380,10 +296,7 @@ void CPlayerWinamp::PlayPause()
*/ */
void CPlayerWinamp::Stop() void CPlayerWinamp::Stop()
{ {
if (m_Window) SendMessage(m_Window, WM_COMMAND, WINAMP_STOP, 0);
{
SendMessage(m_Window, WM_COMMAND, WINAMP_STOP, 0);
}
} }
/* /*
@ -394,10 +307,7 @@ void CPlayerWinamp::Stop()
*/ */
void CPlayerWinamp::Next() void CPlayerWinamp::Next()
{ {
if (m_Window) SendMessage(m_Window, WM_COMMAND, WINAMP_FASTFWD, 0);
{
SendMessage(m_Window, WM_COMMAND, WINAMP_FASTFWD, 0);
}
} }
/* /*
@ -408,10 +318,7 @@ void CPlayerWinamp::Next()
*/ */
void CPlayerWinamp::Previous() void CPlayerWinamp::Previous()
{ {
if (m_Window) SendMessage(m_Window, WM_COMMAND, WINAMP_REWIND, 0);
{
SendMessage(m_Window, WM_COMMAND, WINAMP_REWIND, 0);
}
} }
/* /*
@ -422,11 +329,8 @@ void CPlayerWinamp::Previous()
*/ */
void CPlayerWinamp::SetPosition(int position) void CPlayerWinamp::SetPosition(int position)
{ {
if (m_Window) position *= 1000; // To milliseconds
{ SendMessage(m_Window, WM_WA_IPC, position, IPC_JUMPTOTIME);
position *= 1000; // To milliseconds
SendMessage(m_Window, WM_WA_IPC, position, IPC_JUMPTOTIME);
}
} }
/* /*
@ -437,20 +341,17 @@ void CPlayerWinamp::SetPosition(int position)
*/ */
void CPlayerWinamp::SetRating(int rating) void CPlayerWinamp::SetRating(int rating)
{ {
if (m_Window && (m_State != PLAYER_STOPPED)) if (rating < 0)
{ {
if (rating < 0) rating = 0;
{
rating = 0;
}
else if (rating > 5)
{
rating = 5;
}
SendMessage(m_Window, WM_WA_IPC, rating, IPC_SETRATING);
m_Rating = rating;
} }
else if (rating > 5)
{
rating = 5;
}
SendMessage(m_Window, WM_WA_IPC, rating, IPC_SETRATING);
m_Rating = rating;
} }
/* /*
@ -461,25 +362,20 @@ void CPlayerWinamp::SetRating(int rating)
*/ */
void CPlayerWinamp::SetVolume(int volume) void CPlayerWinamp::SetVolume(int volume)
{ {
if (m_Window) ++volume; // For proper scaling
if (volume < 0)
{ {
++volume; // For proper scaling volume = 0;
if (volume < 0)
{
volume = 0;
}
else if (volume > 100)
{
volume = 100;
}
// Winamp accepts volume in 0 - 255 range
float fVolume = (float)volume;
fVolume *= 2.55f;
volume = (UINT)fVolume;
SendMessage(m_Window, WM_WA_IPC, volume, IPC_SETVOLUME);
} }
else if (volume > 100)
{
volume = 100;
}
// Winamp accepts volume in 0 - 255 range
volume *= 255;
volume /= 100;
SendMessage(m_Window, WM_WA_IPC, volume, IPC_SETVOLUME);
} }
/* /*
@ -490,19 +386,16 @@ void CPlayerWinamp::SetVolume(int volume)
*/ */
void CPlayerWinamp::ClosePlayer() void CPlayerWinamp::ClosePlayer()
{ {
if (m_Window) if (m_WinampType == WA_WINAMP)
{ {
if (m_WinampType == WA_WINAMP) SendMessage(m_Window, WM_CLOSE, 0, 0);
}
else // if (m_WinampType == WA_MEDIAMONKEY)
{
HWND wnd = FindWindow(L"TFMainWindow", L"MediaMonkey");
if (wnd)
{ {
SendMessage(m_Window, WM_CLOSE, 0, 0); SendMessage(wnd, WM_CLOSE, 0, 0);
}
else // if (m_WinampType == WA_MEDIAMONKEY)
{
HWND wnd = FindWindow(L"TFMainWindow", L"MediaMonkey");
if (wnd)
{
SendMessage(wnd, WM_CLOSE, 0, 0);
}
} }
} }
} }
@ -513,15 +406,15 @@ void CPlayerWinamp::ClosePlayer()
** Handles the OpenPlayer bang. ** Handles the OpenPlayer bang.
** **
*/ */
void CPlayerWinamp::OpenPlayer() void CPlayerWinamp::OpenPlayer(std::wstring& path)
{ {
if (m_WinampType == WA_WINAMP) if (m_WinampType == WA_WINAMP)
{ {
ShellExecute(NULL, L"open", m_PlayerPath.empty() ? L"winamp.exe" : m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.empty() ? L"winamp.exe" : path.c_str(), NULL, NULL, SW_SHOW);
} }
else // if (m_WinampType == WA_MEDIAMONKEY) else // if (m_WinampType == WA_MEDIAMONKEY)
{ {
if (m_PlayerPath.empty()) if (path.empty())
{ {
// Gotta figure out where Winamp is located at // Gotta figure out where Winamp is located at
HKEY hKey; HKEY hKey;
@ -545,7 +438,7 @@ void CPlayerWinamp::OpenPlayer()
if (type == REG_SZ) if (type == REG_SZ)
{ {
ShellExecute(NULL, L"open", data, NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", data, NULL, NULL, SW_SHOW);
m_PlayerPath = data; path = data;
} }
} }
@ -554,18 +447,7 @@ void CPlayerWinamp::OpenPlayer()
} }
else else
{ {
ShellExecute(NULL, L"open", m_PlayerPath.c_str(), NULL, NULL, SW_SHOW); ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOW);
} }
} }
} }
/*
** TogglePlayer
**
** Handles the TogglePlayer bang.
**
*/
void CPlayerWinamp::TogglePlayer()
{
m_Window ? ClosePlayer() : OpenPlayer();
}

View File

@ -33,9 +33,10 @@ public:
CPlayerWinamp(WINAMPTYPE type); CPlayerWinamp(WINAMPTYPE type);
~CPlayerWinamp(); ~CPlayerWinamp();
virtual void UpdateData();
virtual void Pause(); virtual void Pause();
virtual void Play(); virtual void Play();
virtual void PlayPause();
virtual void Stop(); virtual void Stop();
virtual void Next(); virtual void Next();
virtual void Previous(); virtual void Previous();
@ -43,18 +44,11 @@ public:
virtual void SetRating(int rating); virtual void SetRating(int rating);
virtual void SetVolume(int volume); virtual void SetVolume(int volume);
virtual void ClosePlayer(); virtual void ClosePlayer();
virtual void OpenPlayer(); virtual void OpenPlayer(std::wstring& path);
virtual void TogglePlayer();
virtual void AddInstance(MEASURETYPE type);
virtual void RemoveInstance();
virtual void UpdateData();
private: private:
bool Initialize(); bool CheckWindow();
bool CheckActive();
bool m_HasCoverMeasure;
bool m_UseUnicodeAPI; bool m_UseUnicodeAPI;
WINAMPTYPE m_WinampType; WINAMPTYPE m_WinampType;
HWND m_Window; // Winamp window HWND m_Window; // Winamp window

View File

@ -12,7 +12,7 @@
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,1,0,0 FILEVERSION 1,1,1,0
PRODUCTVERSION PRODUCTVER PRODUCTVERSION PRODUCTVER
FILEFLAGSMASK 0x17L FILEFLAGSMASK 0x17L
#ifdef _DEBUG #ifdef _DEBUG
@ -29,7 +29,7 @@ BEGIN
BLOCK "040904E4" BLOCK "040904E4"
BEGIN BEGIN
VALUE "FileDescription", "NowPlaying Plugin for Rainmeter" VALUE "FileDescription", "NowPlaying Plugin for Rainmeter"
VALUE "FileVersion", "1.1.0.0" VALUE "FileVersion", "1.1.1.0"
VALUE "InternalName", "NowPlaying" VALUE "InternalName", "NowPlaying"
VALUE "LegalCopyright", "Copyright (C) 2011 - Birunthan Mohanathas" VALUE "LegalCopyright", "Copyright (C) 2011 - Birunthan Mohanathas"
VALUE "OriginalFilename", "NowPlaying.dll" VALUE "OriginalFilename", "NowPlaying.dll"

View File

@ -255,6 +255,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Cover.cpp" />
<ClCompile Include="NowPlaying.cpp" /> <ClCompile Include="NowPlaying.cpp" />
<ClCompile Include="Player.cpp" /> <ClCompile Include="Player.cpp" />
<ClCompile Include="PlayerAIMP.cpp" /> <ClCompile Include="PlayerAIMP.cpp" />
@ -342,6 +343,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Cover.h" />
<ClInclude Include="NowPlaying.h" /> <ClInclude Include="NowPlaying.h" />
<ClInclude Include="Player.h" /> <ClInclude Include="Player.h" />
<ClInclude Include="PlayerAIMP.h" /> <ClInclude Include="PlayerAIMP.h" />

View File

@ -264,6 +264,9 @@
<ClCompile Include="PlayerWLM.cpp"> <ClCompile Include="PlayerWLM.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Cover.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="SDKs\AIMP\aimp2_sdk.h"> <ClInclude Include="SDKs\AIMP\aimp2_sdk.h">
@ -320,6 +323,9 @@
<ClInclude Include="PlayerWLM.h"> <ClInclude Include="PlayerWLM.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Cover.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="PluginNowPlaying.rc"> <ResourceCompile Include="PluginNowPlaying.rc">

View File

@ -109,7 +109,7 @@ enum IPCMESSAGE
// uMsg: WM_USER, wParam: 0 or 1, lParam: 128 // uMsg: WM_USER, wParam: 0 or 1, lParam: 128
IPC_SET_REPEAT, IPC_SET_REPEAT,
// uMsg: WM_USER, wParam: 0 or 1, lParam: 128 [Sent by/to player] // uMsg: WM_USER, wParam: 0 or 1, lParam: 129 [Sent by/to player]
// The player should send this message when it quits. // The player should send this message when it quits.
// CAD will also send this message on exit. Upon receival, the player should // CAD will also send this message on exit. Upon receival, the player should
// disconnect the communication interface and get ready for a IPC_SET_CALLBACK_HWND message. // disconnect the communication interface and get ready for a IPC_SET_CALLBACK_HWND message.