diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-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.cpp | 649 |
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 ×tamp) +{ + 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 |