aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp')
-rw-r--r--Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp649
1 files changed, 649 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp
new file mode 100644
index 00000000..92a6db3c
--- /dev/null
+++ b/Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp
@@ -0,0 +1,649 @@
+#include "Main.h"
+#include "WMDRMModule.h"
+#include "AutoWide.h"
+#include "WMInformation.h"
+#include "AutoChar.h"
+#include "FileInfoDialog.h"
+#include "ConfigDialog.h"
+#include "resource.h"
+#include "StatusHook.h"
+#include "../nu/Config.h"
+#include "util.h"
+#include "WMPlaylist.h"
+#include "api.h"
+#include "output/OutPlugin.h"
+#include "output/AudioOut.h"
+#include <strsafe.h>
+
+extern Nullsoft::Utility::Config wmConfig;
+
+#define SAMPLES_PER_BLOCK 576
+AudioOut *out = 0;
+
+unsigned long endTime = 0;
+unsigned long startTime = 0;
+
+void InitOutputs(HWND hMainWindow, HMODULE hDllInstance)
+{}
+
+void WMDRM::AssignOutput()
+{
+ out = &pluginOut;
+}
+
+WMDRM::WMDRM()
+ : paused(false),
+ clock(0), audio(0), video(0), wait(0), info(0), buffer(0), seek(0), reader(NULL), gain(0),
+ killswitch(0), opened(false),
+ drmProtected(false),
+ volume(-666), pan(0),
+ reader2(0), network(0), playing(false),
+ startAtMilliseconds(0),
+ dspBuffer(0),
+ vizBuffer(0),
+ reader1(0),
+ flushed(false)
+{
+ killswitch = CreateEvent(NULL, TRUE, TRUE, NULL);
+}
+
+WMDRM::~WMDRM()
+{
+ DeleteObject(killswitch);
+ delete [] dspBuffer;
+ delete [] vizBuffer;
+}
+
+static int winampVersion=0;
+
+#define WINAMP_VERSION_MINOR1(winampVersion) ((winampVersion & 0x000000F0)>>4) // returns, i.e. 0x01 for 5.12 and 0x02 for 5.2...
+#define WINAMP_VERSION_MINOR2(winampVersion) ((winampVersion & 0x0000000F)) // returns, i.e. 0x02 for 5.12 and 0x00 for 5.2...
+static void MakeVersionString(QWORD *ver)
+{
+ if (!winampVersion)
+ winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
+
+ LARGE_INTEGER temp;
+ temp.HighPart = MAKELONG(WINAMP_VERSION_MINOR1(winampVersion), WINAMP_VERSION_MAJOR(winampVersion));
+ temp.LowPart = MAKELONG(WASABI_API_APP->main_getBuildNumber(), WINAMP_VERSION_MINOR2(winampVersion));
+
+ *ver = temp.QuadPart;
+}
+
+static void MakeUserAgentString(wchar_t str[256])
+{
+ if (!winampVersion)
+ winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
+
+ StringCchPrintfW(str, 256, L"WinampASF/%01x.%02x",
+ WINAMP_VERSION_MAJOR(winampVersion),
+ WINAMP_VERSION_MINOR(winampVersion));
+}
+
+
+void WMDRM::InitWM()
+{
+ static int triedInit = 0;
+ if (!triedInit)
+ {
+ if (FAILED(WMCreateReader(0, WMT_RIGHT_PLAYBACK, &reader)) || !reader)
+ {
+ reader = 0;
+ plugin.FileExtensions = "\0";
+ return ;
+ }
+ if (FAILED(reader->QueryInterface(&reader1)))
+ reader1 = 0;
+
+ if (FAILED(reader->QueryInterface(&reader2)))
+ reader2 = 0;
+
+ if (FAILED(reader->QueryInterface(&network)))
+ network = 0;
+
+ if (reader1)
+ {
+ QWORD verStr;
+ wchar_t userAgent[256] = {0};
+ MakeVersionString(&verStr);
+ MakeUserAgentString(userAgent);
+ WM_READER_CLIENTINFO info;
+ ZeroMemory(&info, sizeof(WM_READER_CLIENTINFO));
+ info.cbSize = sizeof(WM_READER_CLIENTINFO);
+ info.wszHostExe = L"winamp.exe";
+ info.qwHostVersion = verStr;
+ info.wszPlayerUserAgent = userAgent;
+ info.wszBrowserWebPage = L"http://www.winamp.com";
+
+ reader1->SetClientInfo(&info);
+ }
+
+ clock = new ClockLayer(reader);
+ audio = new AudioLayer(reader);
+ video = new VideoLayer(reader);
+ wait = new WaitLayer(reader);
+ info = new WMInformation(reader);
+ buffer = new BufferLayer(reader);
+ seek = new SeekLayer(reader, clock);
+ gain = new GainLayer(audio, info);
+
+ callback >> seek >> buffer >> clock >> video >> audio >> wait >> gain >> this;
+
+ triedInit = 1;
+ }
+}
+
+void WMDRM::Init()
+{
+ winampVersion = (int)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
+ InitOutputs(plugin.hMainWindow, plugin.hDllInstance);
+
+ //Hook(plugin.hMainWindow);
+}
+
+void WMDRM::Config(HWND hwndParent)
+{
+ WASABI_API_DIALOGBOXW(IDD_CONFIG, hwndParent, PreferencesDialogProc);
+}
+
+void WMDRM::Quit()
+{
+ activePlaylist.Clear();
+ delete setFileInfo; setFileInfo = 0;
+ delete clock; clock = 0;
+ delete audio; audio = 0;
+ delete video; video = 0;
+ delete wait; wait = 0;
+ delete info; info = 0;
+ delete buffer; buffer = 0;
+ delete seek; seek = 0;
+
+ if (network) network->Release(); network = 0;
+ if (reader2) reader2->Release(); reader2 = 0;
+ if (reader1) reader1->Release(); reader1 = 0;
+ if (reader) reader->Release(); reader = 0;
+
+// Unhook(plugin.hMainWindow);
+}
+
+static void BuildTitle(WMInformation *info, const wchar_t *file, wchar_t *str, size_t len)
+{
+ if (info)
+ {
+ wchar_t artist[256] = L"", title[256] = L"";
+ info->GetAttribute(g_wszWMAuthor, artist, 256);
+ info->GetAttribute(g_wszWMTitle, title, 256);
+
+ if (!artist[0] && !title[0])
+ {
+ if (file && *file)
+ {
+ StringCchCopy(str, len, file);
+ }
+ }
+ else if (artist[0] && title[0])
+ StringCchPrintf(str, len, L"%s - %s", artist, title);
+ else if (artist[0])
+ StringCchCopy(str, len, artist);
+ else if (title[0])
+ StringCchCopy(str, len, title);
+ }
+ else if (file)
+ StringCchCopy(str, len, file);
+
+}
+
+void WMDRM::GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms)
+{
+ InitWM();
+
+ if (length_in_ms) *length_in_ms = -1000;
+ if (file && file[0])
+ {
+ bool isURL = !!PathIsURL(file);
+ if (config_http_metadata || !isURL)
+ {
+ WMInformation getFileInfo(file, true);
+
+ if (title)
+ {
+ BuildTitle(&getFileInfo, file, title, GETFILEINFO_TITLE_LENGTH);
+ }
+ if (length_in_ms) *length_in_ms = getFileInfo.GetLengthMilliseconds();
+ }
+ else
+ {
+ if (title)
+ StringCchCopy(title, GETFILEINFO_TITLE_LENGTH, file);
+ }
+ winamp.GetStatus(title, 256, file);
+ }
+ else if (activePlaylist.GetFileName())
+ {
+ //isURL = !!wcsstr(activePlaylist.GetFileName(), L"://");
+ if (wait && !wait->IsOpen()) // if it's not open, fill in with some default data... WMDRM::Opened() will refresh the title ...
+ {
+ StringCchCopy(title, GETFILEINFO_TITLE_LENGTH, activePlaylist.GetOriginalFileName());
+ if (length_in_ms) *length_in_ms = -1000;
+ //if (isURL)
+ winamp.GetStatus(title, 256, activePlaylist.GetOriginalFileName());
+ return ;
+ }
+
+ if (title)
+ {
+ BuildTitle(info, activePlaylist.GetOriginalFileName(), title, GETFILEINFO_TITLE_LENGTH);
+ }
+ if (info)
+ if (length_in_ms) *length_in_ms = info->GetLengthMilliseconds();
+ else
+ if (length_in_ms) *length_in_ms = -1000;
+
+ //if (isURL)
+ winamp.GetStatus(title, 256, activePlaylist.GetOriginalFileName());
+ }
+}
+
+int WMDRM::InfoBox(const wchar_t *fn, HWND hwndParent)
+{
+ /* CUT> we're now using the unified file info dialogue
+ FileInfoDialog dialog(WASABI_API_LNG_HINST, hwndParent, fn);
+ if (dialog.WasEdited())
+ return 0;
+ else
+ return 1;
+ */
+ return 0;
+}
+
+int WMDRM::IsOurFile(const in_char *fn)
+{
+ // if (!reader)
+ // return 0;
+ if (wcsstr(fn, L".asx")) // TODO: need something WAY better than this
+ return 1;
+ return fileTypes.IsSupportedURL(fn);
+}
+
+int WMDRM::Play(const wchar_t * fn)
+{
+ InitWM();
+
+ if (!reader)
+ return -1;
+ if (network)
+ network->SetBufferingTime((QWORD)config_buffer_time*10000LL);
+
+ ResetEvent(killswitch);
+ wait->ResetForOpen();
+
+ activePlaylist.Clear();
+ activePlaylist.playlistFilename = _wcsdup(fn);
+ if (playlistManager->Load(fn, &activePlaylist) != PLAYLISTMANAGER_SUCCESS)
+ activePlaylist.OnFile(fn, 0, -1, 0); // add it manually (TODO: need a better way to do this)
+
+ winamp.GetVideoOutput();
+ playing = true;
+ startTime = winamp.GetStart() * 1000;
+ if (!startAtMilliseconds)
+ startAtMilliseconds = startTime;
+
+ endTime = winamp.GetEnd() * 1000;
+ clock->SetLastOutputTime(startAtMilliseconds); // normally 0, but set when metadata editor needs to stop / restart a file
+ AssignOutput();
+ clock->SetStartTimeMilliseconds(startAtMilliseconds); // normally 0, but set when metadata editor needs to stop / restart a file
+ startAtMilliseconds = 0;
+ return seek->Open(activePlaylist.GetFileName(), &callback);
+}
+
+void WMDRM::ReOpen()
+{
+ if (opened)
+ seek->Stop();
+ seek->Open(activePlaylist.GetFileName(), &callback);
+}
+
+void WMDRM::Pause()
+{
+ paused = true;
+ if (seek)
+ seek->Pause();
+}
+
+void WMDRM::UnPause()
+{
+ paused = false;
+ if (seek)
+ seek->Unpause();
+}
+
+int WMDRM::IsPaused()
+{
+ return (int)paused;
+}
+
+void WMDRM::Stop()
+{
+ if (!playing)
+ return ;
+
+ playing = false;
+ SetEvent(killswitch);
+ if (paused)
+ UnPause();
+ if (seek)
+ seek->Stop();
+}
+
+
+void WMDRM::Closed()
+{
+ opened = false;
+ WMHandler::Closed();
+}
+
+int WMDRM::GetLength()
+{
+ if (info)
+ return info->GetLengthMilliseconds();
+ else
+ return 0;
+}
+
+int WMDRM::GetOutputTime()
+{
+ if (!opened)
+ {
+ //if (winamp.bufferCount)
+ return winamp.bufferCount;
+ //return 0;
+ }
+ return clock->GetOutputTime();
+}
+
+void WMDRM::SetOutputTime(int time_in_ms)
+{
+ if (startTime || endTime)
+ {
+ unsigned int seektime = time_in_ms;
+ if (endTime && seektime > endTime)
+ seektime = endTime;
+ if (startTime && seektime < startTime)
+ seektime = startTime;
+ seek->SeekTo(seektime);
+ return ;
+ }
+ seek->SeekTo(time_in_ms);
+}
+
+void WMDRM::SetVolume(int volume)
+{
+ this->volume = volume;
+ if (out)
+ out->SetVolume(volume);
+}
+
+void WMDRM::SetPan(int pan)
+{
+ this->pan = pan;
+ if (out)
+ out->SetPan(pan);
+}
+
+void WMDRM::EQSet(int on, char data[10], int preamp)
+{}
+
+void WMDRM::BuildBuffers()
+{
+ remaining.Allocate(audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK));
+
+ // TODO: check against old size
+ delete [] dspBuffer;
+ delete [] vizBuffer;
+ dspBuffer = new unsigned char[audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK) * 2];
+ vizBuffer = new unsigned char[audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK) * 2];
+}
+
+void WMDRM::AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp)
+{
+ // TODO: apply replaygain first
+ // but if we change bitdepth, we'll have to be careful about calling audio->AudioSamplesToBytes() and similiar functions
+
+ unsigned char *data = (unsigned char *)_data;
+ if (!remaining.Empty())
+ {
+ if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
+ {
+ remaining.Flush();
+ return ;
+ }
+ remaining.UpdatingWrite(data, sizeBytes);
+ if (remaining.Full())
+ {
+ OutputAudioSamples(remaining.GetData(), SAMPLES_PER_BLOCK, timestamp);
+ remaining.Flush();
+ }
+ }
+
+ long samplesLeft = audio->AudioBytesToSamples(sizeBytes);
+
+ while (samplesLeft)
+ {
+ if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
+ {
+ remaining.Flush();
+ return ;
+ }
+
+ if (samplesLeft >= SAMPLES_PER_BLOCK)
+ {
+ OutputAudioSamples(data, SAMPLES_PER_BLOCK, timestamp);
+ data += audio->AudioSamplesToBytes(SAMPLES_PER_BLOCK);
+ samplesLeft -= SAMPLES_PER_BLOCK;
+ }
+ else
+ {
+ unsigned long bytesLeft = audio->AudioSamplesToBytes(samplesLeft);
+ remaining.UpdatingWrite(data, bytesLeft);
+ samplesLeft = audio->AudioBytesToSamples(bytesLeft); // should always be 0
+ assert(samplesLeft == 0);
+ }
+ }
+}
+
+
+void WMDRM::QuantizedViz(void *data, long sizeBytes, DWORD timestamp)
+{
+ if (drmProtected)
+ {
+ assert(sizeBytes == audio->Channels() *(audio->BitSize() / 8) * SAMPLES_PER_BLOCK);
+ memset(vizBuffer, 0, sizeBytes);
+ ptrdiff_t stride = audio->BitSize() / 8;
+ size_t position = stride - 1;
+ unsigned char *origData = (unsigned char *)data;
+ for (int i = 0;i < SAMPLES_PER_BLOCK*audio->Channels();i++) // winamp hardcodes this ...
+ {
+ vizBuffer[position] = (origData[position] & 0xFC); // 6 bits of precision, enough for viz.
+ position += stride;
+ }
+
+ plugin.SAAddPCMData((char *) vizBuffer, audio->Channels(), audio->ValidBits(), timestamp);
+ plugin.VSAAddPCMData((char *) vizBuffer, audio->Channels(), audio->ValidBits(), timestamp);
+ }
+ else
+ {
+ plugin.SAAddPCMData((char *) data, audio->Channels(), audio->ValidBits(), timestamp);
+ plugin.VSAAddPCMData((char *) data, audio->Channels(), audio->ValidBits(), timestamp);
+ }
+}
+
+long WMDRM::GetPosition()
+{
+ if (!opened)
+ return 0;
+ return out->GetWrittenTime();
+}
+
+void WMDRM::OutputAudioSamples(void *data, long samples)
+{
+ DWORD timestamp = out->GetWrittenTime();
+ OutputAudioSamples(data, samples, timestamp);
+}
+
+void WMDRM::OutputAudioSamples(void *data, long samples, DWORD &timestamp)
+{
+ clock->SetLastOutputTime(timestamp);
+ timestamp += audio->AudioSamplesToMilliseconds(samples);
+
+
+ //clock->SetLastOutputTime(winamp.GetWrittenTime());
+
+ //in theory, we could check mod->dsp_isactive(), but that opens up a potential race condition ...
+ memcpy(dspBuffer, data, audio->AudioSamplesToBytes(samples));
+ int dspSize = samples;
+ if (!drmProtected)
+ dspSize = plugin.dsp_dosamples((short *)dspBuffer, samples, audio->BitSize(), audio->Channels(), audio->SampleRate());
+ dspSize = audio->AudioSamplesToBytes(dspSize);
+ if (samples == SAMPLES_PER_BLOCK)
+ QuantizedViz(dspBuffer, dspSize, timestamp);
+ while (out->CanWrite() <= dspSize)
+ {
+ if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
+ {
+ remaining.Flush();
+ return ;
+ }
+ }
+ out->Write((char *)dspBuffer, dspSize);
+ /*long bytesAvail = */out->CanWrite();
+}
+
+void WMDRM::Opened()
+{
+ //winamp.ResetBuffering();
+ drmProtected = info->IsAttribute(g_wszWMProtected);
+ ResetEvent(killswitch);
+ if (!audio->IsOpen())
+ {
+ if (video->IsOpen())
+ {
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_REALTIME));
+ clock->GoRealTime();
+ plugin.is_seekable = info->IsSeekable() ? 1 : 0;
+ winamp.SetAudioInfo(info->GetBitrate() / 1000, 0, 0);
+ //out->SetVolume( -666); // set default volume
+ }
+ else
+ {
+ // no audio or video!!
+ seek->Stop();
+ First().OpenFailed();
+ return ;
+ }
+ }
+ else
+ {
+ BuildBuffers();
+ plugin.is_seekable = info->IsSeekable() ? 1 : 0;
+ winamp.SetAudioInfo(info->GetBitrate() / 1000, audio->SampleRate() / 1000, audio->Channels());
+ out->SetVolume(volume); // set default volume
+ out->SetPan(pan);
+ winamp.SetVizInfo(audio->SampleRate(), audio->Channels());
+ }
+ opened = true;
+ winamp.ClearStatus();
+ reader->Start(clock->GetStartTime(), 0, 1.0f, NULL);
+ WMHandler::Opened();
+
+}
+
+void WMDRM::Started()
+{
+ ResetEvent(killswitch);
+ winamp.ResetBuffering();
+ winamp.ClearStatus();
+ WMHandler::Started();
+}
+void WMDRM::EndOfFile()
+{
+ if (audio->IsOpen())
+ {
+ if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
+ {
+ if (remaining.used)
+ {
+ OutputAudioSamples(remaining.GetData(), audio->AudioBytesToSamples(remaining.used));
+ remaining.Flush();
+ }
+ out->Write(0, 0);
+ while (out->IsPlaying())
+ {
+ if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ // TODO: if we have a playlist, start the next track instead of telling winamp to go to the next track
+ if (playing)
+ winamp.EndOfFile();
+ WMHandler::EndOfFile();
+}
+
+void WMDRM::NewMetadata()
+{
+ winamp.RefreshTitle();
+ WMHandler::NewMetadata();
+}
+
+void WMDRM::Error()
+{
+ // wait 200 ms for the killswitch (aka hitting stop)
+ // this allows the user to hit "stop" and not have to continue cycling through songs if there are a whole bunch of bad/missing WMAs in the playlist
+ if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
+ winamp.EndOfFile();
+}
+
+void WMDRM::OpenFailed()
+{
+ if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0) // wait 200 ms for the killswitch (see above notes)
+ winamp.EndOfFile();
+}
+
+void WMDRM::Stopped()
+{
+ remaining.Flush();
+ WMHandler::Stopped();
+}
+
+void WMDRM::Kill()
+{
+ SetEvent(killswitch);
+ WMHandler::Kill();
+}
+
+void WMDRM::NewSourceFlags()
+{
+ plugin.is_seekable = info->IsSeekable() ? 1 : 0;
+}
+
+void WMDRM::Connecting()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_CONNECTING));
+ WMHandler::Connecting();
+}
+
+void WMDRM::Locating()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_LOCATING));
+ WMHandler::Locating();
+}
+
+void WMDRM::AccessDenied()
+{
+ winamp.SetStatus(WASABI_API_LNGSTRINGW(IDS_ACCESS_DENIED));
+ if (playing && WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0) // wait 200 ms for the killswitch (see above notes)
+ winamp.PressStop();
+} \ No newline at end of file