123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649 |
- #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();
- }
|