Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
54
Src/Plugins/Input/in_flac/About.cpp
Normal file
54
Src/Plugins/Input/in_flac/About.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
** Copyright © 2007-2014 Winamp SA
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "../Agave/Language/api_language.h"
|
||||
#include "resource.h"
|
||||
#include <FLAC/all.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
static HMODULE libflac=0;
|
||||
static char defaultVersionString[64];
|
||||
static const char **versionString=0;
|
||||
static const char *GetFLACVersion()
|
||||
{
|
||||
return "1.4.2";
|
||||
}
|
||||
|
||||
int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
|
||||
{
|
||||
MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
|
||||
msgbx.lpszText = message;
|
||||
msgbx.lpszCaption = title;
|
||||
msgbx.lpszIcon = MAKEINTRESOURCEW(102);
|
||||
msgbx.hInstance = GetModuleHandle(0);
|
||||
msgbx.dwStyle = MB_USERICON;
|
||||
msgbx.hwndOwner = parent;
|
||||
return MessageBoxIndirectW(&msgbx);
|
||||
}
|
||||
|
||||
void About(HWND hwndParent)
|
||||
{
|
||||
wchar_t message[1024] = {0};
|
||||
StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
|
||||
plugin.description, __DATE__, GetFLACVersion());
|
||||
DoAboutMessageBox(hwndParent,(wchar_t*)plugin.description,message);
|
||||
}
|
270
Src/Plugins/Input/in_flac/AlbumArt.cpp
Normal file
270
Src/Plugins/Input/in_flac/AlbumArt.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
** Copyright © 2007-2014 Winamp SA
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: July 25, 2007
|
||||
**
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "api__in_flv.h"
|
||||
#include "Metadata.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "AlbumArt.h"
|
||||
#include <shlwapi.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
bool FLAC_AlbumArtProvider::IsMine(const wchar_t *filename)
|
||||
{
|
||||
const wchar_t *extension = PathFindExtensionW(filename);
|
||||
if (extension && *extension)
|
||||
{
|
||||
wchar_t exts[128] = {0};
|
||||
GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, exts, 128, winampINI);
|
||||
|
||||
extension++;
|
||||
wchar_t *b = exts;
|
||||
wchar_t *c;
|
||||
do
|
||||
{
|
||||
wchar_t d[20] = {0};
|
||||
StringCchCopyW(d, 15, b);
|
||||
if ((c = wcschr(b, L';')))
|
||||
{
|
||||
if ((c-b)<15)
|
||||
d[c - b] = 0;
|
||||
}
|
||||
|
||||
if (!lstrcmpiW(extension, d))
|
||||
return true;
|
||||
|
||||
b = c + 1;
|
||||
}
|
||||
while (c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int FLAC_AlbumArtProvider::ProviderType()
|
||||
{
|
||||
return ALBUMARTPROVIDER_TYPE_EMBEDDED;
|
||||
}
|
||||
|
||||
bool NameToAPICType(const wchar_t *name, FLAC__StreamMetadata_Picture_Type &num)
|
||||
{
|
||||
if (!name || !*name) // default to cover
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
|
||||
else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD;
|
||||
else if (!_wcsicmp(name, L"icon")) // Other file icon
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON;
|
||||
else if (!_wcsicmp(name, L"cover")) // Cover (front)
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
|
||||
else if (!_wcsicmp(name, L"back")) // Cover (back)
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER;
|
||||
else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE;
|
||||
else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA;
|
||||
else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST;
|
||||
else if (!_wcsicmp(name, L"artist")) // Artist/performer
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST;
|
||||
else if (!_wcsicmp(name, L"conductor")) // Conductor
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR;
|
||||
else if (!_wcsicmp(name, L"band")) // Band/Orchestra
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_BAND;
|
||||
else if (!_wcsicmp(name, L"composer")) // Composer
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER;
|
||||
else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST;
|
||||
else if (!_wcsicmp(name, L"location")) // Recording Location
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION;
|
||||
else if (!_wcsicmp(name, L"recording")) // During recording
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING;
|
||||
else if (!_wcsicmp(name, L"performance")) // During performance
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE;
|
||||
else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE;
|
||||
else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_FISH;
|
||||
else if (!_wcsicmp(name, L"illustration")) // Illustration
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION;
|
||||
else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE;
|
||||
else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
|
||||
num=FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int FLAC_AlbumArtProvider::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
|
||||
{
|
||||
FLACMetadata metadata;
|
||||
|
||||
if (metadata.Open(filename))
|
||||
{
|
||||
FLAC__StreamMetadata_Picture_Type pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
if (metadata.GetPicture(pictype, bits, len, mimeType))
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int FLAC_AlbumArtProvider::SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
|
||||
{
|
||||
if (!info || info && _wcsicmp(filename, info->filename))
|
||||
{
|
||||
FLACMetadata metadata;
|
||||
if (metadata.Open(filename))
|
||||
{
|
||||
FLAC__StreamMetadata_Picture_Type pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
if (metadata.SetPicture(pictype, bits, len, mimeType, 0/*TODO*/, 0/*TODO*/))
|
||||
{
|
||||
if (metadata.Save(filename))
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
else
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FLAC__StreamMetadata_Picture_Type pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
if (info->metadata.SetPicture(pictype, bits, len, mimeType, 0/*TODO*/, 0/*TODO*/))
|
||||
{
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int FLAC_AlbumArtProvider::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
|
||||
{
|
||||
if (!info || info && _wcsicmp(filename, info->filename))
|
||||
{
|
||||
FLACMetadata metadata;
|
||||
if (metadata.Open(filename))
|
||||
{
|
||||
FLAC__StreamMetadata_Picture_Type pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
if (info->metadata.RemovePicture(pictype))
|
||||
{
|
||||
if (info->metadata.Save(filename))
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
else
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FLAC__StreamMetadata_Picture_Type pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
if (info->metadata.RemovePicture(pictype))
|
||||
{
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
#define CBCLASS FLAC_AlbumArtProvider
|
||||
START_DISPATCH;
|
||||
CB(SVC_ALBUMARTPROVIDER_PROVIDERTYPE, ProviderType);
|
||||
CB(SVC_ALBUMARTPROVIDER_GETALBUMARTDATA, GetAlbumArtData);
|
||||
CB(SVC_ALBUMARTPROVIDER_ISMINE, IsMine);
|
||||
CB(SVC_ALBUMARTPROVIDER_SETALBUMARTDATA, SetAlbumArtData);
|
||||
CB(SVC_ALBUMARTPROVIDER_DELETEALBUMART, DeleteAlbumArt);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
static FLAC_AlbumArtProvider albumArtProvider;
|
||||
|
||||
// {622C3B42-866E-4935-AA52-3B456AE8B036}
|
||||
static const GUID flac_albumartproviderGUID =
|
||||
{ 0x622c3b42, 0x866e, 0x4935, { 0xaa, 0x52, 0x3b, 0x45, 0x6a, 0xe8, 0xb0, 0x36 } };
|
||||
|
||||
FOURCC AlbumArtFactory::GetServiceType()
|
||||
{
|
||||
return svc_albumArtProvider::SERVICETYPE;
|
||||
}
|
||||
|
||||
const char *AlbumArtFactory::GetServiceName()
|
||||
{
|
||||
return "FLAC Album Art Provider";
|
||||
}
|
||||
|
||||
GUID AlbumArtFactory::GetGUID()
|
||||
{
|
||||
return flac_albumartproviderGUID;
|
||||
}
|
||||
|
||||
void *AlbumArtFactory::GetInterface(int global_lock)
|
||||
{
|
||||
return &albumArtProvider;
|
||||
}
|
||||
|
||||
int AlbumArtFactory::SupportNonLockingInterface()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int AlbumArtFactory::ReleaseInterface(void *ifc)
|
||||
{
|
||||
//plugin.service->service_unlock(ifc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *AlbumArtFactory::GetTestString()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AlbumArtFactory::ServiceNotify(int msg, int param1, int param2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define CBCLASS AlbumArtFactory
|
||||
START_DISPATCH;
|
||||
CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
|
||||
CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
|
||||
CB(WASERVICEFACTORY_GETGUID, GetGUID)
|
||||
CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
|
||||
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
|
||||
CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
|
||||
CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
|
||||
CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
38
Src/Plugins/Input/in_flac/AlbumArt.h
Normal file
38
Src/Plugins/Input/in_flac/AlbumArt.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef NULLSOFT_IN_FLAC_ALBUMART_H
|
||||
#define NULLSOFT_IN_FLAC_ALBUMART_H
|
||||
|
||||
#include "../Agave/AlbumArt/svc_albumArtProvider.h"
|
||||
|
||||
class FLAC_AlbumArtProvider : public svc_albumArtProvider
|
||||
{
|
||||
public:
|
||||
bool IsMine(const wchar_t *filename);
|
||||
int ProviderType();
|
||||
// implementation note: use WASABI_API_MEMMGR to alloc bits and mimetype, so that the recipient can free through that
|
||||
int GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType);
|
||||
int SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType);
|
||||
int DeleteAlbumArt(const wchar_t *filename, const wchar_t *type);
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
#include <api/service/waservicefactory.h>
|
||||
|
||||
class AlbumArtFactory : public waServiceFactory
|
||||
{
|
||||
public:
|
||||
FOURCC GetServiceType();
|
||||
const char *GetServiceName();
|
||||
GUID GetGUID();
|
||||
void *GetInterface(int global_lock);
|
||||
int SupportNonLockingInterface();
|
||||
int ReleaseInterface(void *ifc);
|
||||
const char *GetTestString();
|
||||
int ServiceNotify(int msg, int param1, int param2);
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
719
Src/Plugins/Input/in_flac/DecodeThread.cpp
Normal file
719
Src/Plugins/Input/in_flac/DecodeThread.cpp
Normal file
|
@ -0,0 +1,719 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2011 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include "main.h"
|
||||
#include <windows.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <locale.h>
|
||||
#include <FLAC/all.h>
|
||||
#include "StreamFileWin32.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "QuickBuf.h"
|
||||
#include "api__in_flv.h"
|
||||
#include "../nu/AudioOutput.h"
|
||||
#include "../Agave/Language/api_language.h"
|
||||
#include "FLACFileCallbacks.h"
|
||||
#include "nx/nxpath.h"
|
||||
|
||||
int m_force_seek = -1; // el hacko grande
|
||||
const DWORD PAUSE_TIMEOUT = 100; // number of milliseconds to sleep for when paused
|
||||
|
||||
static bool paused = false;
|
||||
|
||||
// could probably move this inside client_data
|
||||
int realbits;
|
||||
int bps;
|
||||
int samplerate;
|
||||
int channels;
|
||||
volatile int currentSongLength=-1000;
|
||||
int averageBitrate;
|
||||
|
||||
// if input plugins weren't single-instance only, we could put this in thread-local-storage
|
||||
static FLAC__StreamDecoder *decoder = 0;
|
||||
static uint64_t fileSize = 0;
|
||||
static FLAC__uint64 decodePosition = 0;
|
||||
|
||||
double gain = 1.0;
|
||||
static double buffer_scale=1.0;
|
||||
|
||||
class FLACWait
|
||||
{
|
||||
public:
|
||||
int WaitOrAbort(int time_in_ms)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, time_in_ms) == WAIT_OBJECT_0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
volatile int bufferCount=0;
|
||||
static void Buffering(int bufStatus, const wchar_t *displayString)
|
||||
{
|
||||
if (bufStatus < 0 || bufStatus > 100)
|
||||
return;
|
||||
|
||||
char tempdata[75*2] = {0, };
|
||||
|
||||
int csa = plugin.SAGetMode();
|
||||
if (csa & 1)
|
||||
{
|
||||
for (int x = 0; x < bufStatus*75 / 100; x ++)
|
||||
tempdata[x] = x * 16 / 75;
|
||||
}
|
||||
else if (csa&2)
|
||||
{
|
||||
int offs = (csa & 1) ? 75 : 0;
|
||||
int x = 0;
|
||||
while (x < bufStatus*75 / 100)
|
||||
{
|
||||
tempdata[offs + x++] = -6 + x * 14 / 75;
|
||||
}
|
||||
while (x < 75)
|
||||
{
|
||||
tempdata[offs + x++] = 0;
|
||||
}
|
||||
}
|
||||
else if (csa == 4)
|
||||
{
|
||||
tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
|
||||
}
|
||||
if (csa) plugin.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
|
||||
|
||||
/*
|
||||
TODO
|
||||
wchar_t temp[64] = {0};
|
||||
StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
|
||||
SetStatus(temp);
|
||||
*/
|
||||
//SetVideoStatusText(temp); // TODO: find a way to set the old status back
|
||||
// videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f));
|
||||
}
|
||||
|
||||
static nu::AudioOutput<FLACWait> audio_output(&plugin);
|
||||
|
||||
#pragma region Truncaters
|
||||
inline static void clip(double &x, double a, double b)
|
||||
{
|
||||
double x1 = fabs(x - a);
|
||||
double x2 = fabs(x - b);
|
||||
x = x1 + (a + b);
|
||||
x -= x2;
|
||||
x *= 0.5;
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate32(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
|
||||
{
|
||||
// TODO: this can be sped up significantly
|
||||
FLAC__int32 *output = (FLAC__int32 *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
double appliedGain = gain * (double)buffer[c][b];
|
||||
// TODO: add dither
|
||||
clip(appliedGain, -2147483648., 2147483647.);
|
||||
*output = (FLAC__int32)appliedGain;
|
||||
output++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate24(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
|
||||
{
|
||||
char *output = (char *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
double appliedGain = gain * (double)buffer[c][b];
|
||||
// TODO: add dither
|
||||
clip(appliedGain, -8388608., 8388607.);
|
||||
FLAC__int32 sample = (FLAC__int32)appliedGain;
|
||||
|
||||
// little endian specific code
|
||||
output[0] = (unsigned char)(sample);
|
||||
output[1] = (unsigned char)(sample >> 8);
|
||||
output[2] = (unsigned char)(sample >> 16);
|
||||
output += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate16(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
|
||||
{
|
||||
short *output = (short *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
double appliedGain = gain * (double)buffer[c][b];
|
||||
// TODO: add dither
|
||||
clip(appliedGain, -32768., 32767.);
|
||||
FLAC__int32 sample = (FLAC__int32)appliedGain;
|
||||
|
||||
*output = (short) sample ;
|
||||
output ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate8(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
|
||||
{
|
||||
unsigned char *output = (unsigned char *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
double appliedGain = gain * (double)buffer[c][b];
|
||||
// TODO: add dither
|
||||
clip(appliedGain, -128., 127.);
|
||||
FLAC__int32 sample = (FLAC__int32)appliedGain;
|
||||
|
||||
*output = (unsigned char)(sample + 128);
|
||||
output++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Versions without gain adjustment --- */
|
||||
|
||||
static void InterleaveAndTruncate32(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
|
||||
{
|
||||
FLAC__int32 *output = (FLAC__int32 *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
*output = buffer[c][b];
|
||||
output++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate24(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
|
||||
{
|
||||
char *output = (char *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
FLAC__int32 sample = buffer[c][b];
|
||||
|
||||
// little endian specific code
|
||||
output[0] = (unsigned char)(sample);
|
||||
output[1] = (unsigned char)(sample >> 8);
|
||||
output[2] = (unsigned char)(sample >> 16);
|
||||
output += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate16(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
|
||||
{
|
||||
short *output = (short *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
*output = (short) buffer[c][b];
|
||||
output ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InterleaveAndTruncate8(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
|
||||
{
|
||||
unsigned char *output = (unsigned char *)_output;
|
||||
|
||||
for (int b = 0;b < blocksize;b++)
|
||||
{
|
||||
for (int c = 0;c < channels;c++)
|
||||
{
|
||||
*output = (unsigned char)(buffer[c][b] + 128);
|
||||
output++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InterleaveAndTruncate(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
|
||||
{
|
||||
if (gain == 1.0) // if it's EXACTLY 1.0, i.e. from a hardcoded value. not meant to be a "RG happens to be 1.0" check
|
||||
{
|
||||
switch(bps)
|
||||
{
|
||||
case 8:
|
||||
InterleaveAndTruncate8(buffer, _output, bps, channels, blocksize);
|
||||
break;
|
||||
case 16:
|
||||
InterleaveAndTruncate16(buffer, _output, bps, channels, blocksize);
|
||||
break;
|
||||
case 24:
|
||||
InterleaveAndTruncate24(buffer, _output, bps, channels, blocksize);
|
||||
break;
|
||||
case 32:
|
||||
InterleaveAndTruncate32(buffer, _output, bps, channels, blocksize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // apply replay gain
|
||||
{
|
||||
switch(bps)
|
||||
{
|
||||
case 8:
|
||||
InterleaveAndTruncate8(buffer, _output, bps, channels, blocksize, gain);
|
||||
break;
|
||||
case 16:
|
||||
InterleaveAndTruncate16(buffer, _output, bps, channels, blocksize, gain);
|
||||
break;
|
||||
case 24:
|
||||
InterleaveAndTruncate24(buffer, _output, bps, channels, blocksize, gain);
|
||||
break;
|
||||
case 32:
|
||||
InterleaveAndTruncate32(buffer, _output, bps, channels, blocksize, gain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
QuickBuf output;
|
||||
FLAC__uint64 lastoutputtime;
|
||||
static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
|
||||
{
|
||||
// TODO: if frame bps/samplerate/channels doesn't equal our own, we'll probably have to close & re-open the audio output
|
||||
// mux buffer into interleaved samples
|
||||
FLAC__uint64 newPosition;
|
||||
FLAC__stream_decoder_get_decode_position(decoder, &newPosition);
|
||||
FLAC__uint64 delta = newPosition - decodePosition;
|
||||
decodePosition = newPosition;
|
||||
if (!config_average_bitrate)
|
||||
plugin.SetInfo((int)(delta / (125.*frame->header.blocksize / samplerate)), -1, -1, -1);
|
||||
else if (fixBitrate)
|
||||
{
|
||||
fixBitrate = false;
|
||||
plugin.SetInfo(averageBitrate, -1, -1, -1);
|
||||
}
|
||||
|
||||
if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER)
|
||||
lastoutputtime = 1000ULL*frame->header.number.sample_number / (FLAC__uint64)samplerate;
|
||||
else
|
||||
lastoutputtime = 0;
|
||||
|
||||
int byteLength = (bps / 8) * channels * frame->header.blocksize;
|
||||
output.Reserve(byteLength*2);
|
||||
InterleaveAndTruncate(buffer, output, bps, channels, frame->header.blocksize, gain);
|
||||
|
||||
audio_output.Write(output, byteLength);
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
static int GetBits(int incoming)
|
||||
{
|
||||
int bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
|
||||
if (bits > 16 && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
|
||||
return bits;
|
||||
|
||||
return min(bits, incoming);
|
||||
}
|
||||
|
||||
static double GetGain(const FLAC__StreamMetadata *metadata)
|
||||
{
|
||||
if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
|
||||
{
|
||||
if (metadata)
|
||||
{
|
||||
int gainPos = -1, peakPos = -1;
|
||||
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
|
||||
{
|
||||
case 0: // track
|
||||
gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_GAIN");
|
||||
if (gainPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_GAIN");
|
||||
|
||||
peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_PEAK");
|
||||
if (peakPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_PEAK");
|
||||
|
||||
break;
|
||||
case 1:
|
||||
gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_GAIN");
|
||||
if (gainPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_GAIN");
|
||||
|
||||
peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_PEAK");
|
||||
if (peakPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
|
||||
peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_PEAK");
|
||||
break;
|
||||
}
|
||||
|
||||
double dB = 0, peak = 1.0;
|
||||
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
|
||||
if (gainPos >= 0)
|
||||
{
|
||||
const char *entry = (const char *)metadata->data.vorbis_comment.comments[gainPos].entry;
|
||||
const char *value = strchr(entry, '='); // find the first equal
|
||||
if (value++)
|
||||
{
|
||||
if (value[0] == '+')
|
||||
dB = _atof_l(&value[1], C_locale);
|
||||
else
|
||||
dB = _atof_l(value, C_locale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
|
||||
return pow(10.0, dB / 20.0);
|
||||
}
|
||||
|
||||
if (peakPos >= 0)
|
||||
{
|
||||
const char *entry = (const char *)metadata->data.vorbis_comment.comments[peakPos].entry;
|
||||
const char *value = strchr(entry, '='); // find the first equal
|
||||
if (value++)
|
||||
{
|
||||
peak = _atof_l(value, C_locale);
|
||||
}
|
||||
}
|
||||
|
||||
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
|
||||
{
|
||||
case 0: // apply gain
|
||||
return pow(10.0, dB / 20.0);
|
||||
break;
|
||||
case 1: // apply gain, but don't clip
|
||||
return min(pow(10.0, dB / 20.0), 1.0 / peak);
|
||||
break;
|
||||
case 2: // normalize
|
||||
return 1.0 / peak;
|
||||
break;
|
||||
case 3: // prevent clipping
|
||||
if (peak > 1.0)
|
||||
return 1.0 / peak;
|
||||
else
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
|
||||
return pow(10.0, dB / 20.0);
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||
{
|
||||
switch (metadata->type)
|
||||
{
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
{
|
||||
realbits = metadata->data.stream_info.bits_per_sample;
|
||||
channels = metadata->data.stream_info.channels;
|
||||
samplerate = metadata->data.stream_info.sample_rate;
|
||||
bps = GetBits(metadata->data.stream_info.bits_per_sample);
|
||||
gain = GetGain(0) * pow(2., (double)(bps - realbits));
|
||||
if (metadata->data.stream_info.total_samples)
|
||||
{
|
||||
currentSongLength = (int)((metadata->data.stream_info.total_samples*1000ULL)/(uint64_t)samplerate);
|
||||
averageBitrate = (int)(fileSize / (125 * metadata->data.stream_info.total_samples / (__int64)samplerate));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSongLength=-1000;
|
||||
averageBitrate=0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
gain = GetGain(metadata) * pow(2., (double)(bps - realbits));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||
{
|
||||
//client_data = client_data; // dummy line so i can set a breakpoint
|
||||
}
|
||||
|
||||
void CALLBACK APCPause(ULONG_PTR data)
|
||||
{
|
||||
paused = !!data;
|
||||
plugin.outMod->Pause(!!paused);
|
||||
}
|
||||
|
||||
void CALLBACK APCSeek(ULONG_PTR data)
|
||||
{
|
||||
// TODO: break out of end-of-file handling if necessary
|
||||
buffer_scale=1.0;
|
||||
int time_in_ms = (int)data;
|
||||
lastoutputtime=time_in_ms; // cheating a bit here :)
|
||||
audio_output.Flush(time_in_ms);
|
||||
__int64 frames = Int32x32To64(time_in_ms, samplerate) / 1000;
|
||||
FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder);
|
||||
plugin.outMod->Pause(0);
|
||||
FLAC__stream_decoder_seek_absolute(decoder, frames);
|
||||
plugin.outMod->Pause(paused);
|
||||
if (FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_SEEK_ERROR)
|
||||
{
|
||||
FLAC__stream_decoder_flush(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
static const double prebuffer_seconds = 0.5;
|
||||
static bool DoBuffering(nx_file_t file)
|
||||
{
|
||||
// TODO: check for disconnect, etc.
|
||||
|
||||
// anything less than half-a-second and we'll rebuffer. but when we rebuffer, we'll get 2 seconds worth of audio
|
||||
// also, every time we're forced to re-buffer, we'll double the buffer amount
|
||||
|
||||
uint64_t required;
|
||||
if (averageBitrate > 0)
|
||||
{
|
||||
required = (uint64_t)((double)averageBitrate * 1000.0 * buffer_scale * prebuffer_seconds / 8.0);
|
||||
}
|
||||
else /* no bitrate specified, let's assume 1000kbps */
|
||||
{
|
||||
required = (uint64_t)(1000.0 * 1000.0 * buffer_scale * prebuffer_seconds / 8.0);
|
||||
}
|
||||
|
||||
uint64_t available;
|
||||
if (NXFileProgressiveDownloaderAvailable(file, required, &available) == NErr_True)
|
||||
return true;
|
||||
|
||||
Buffering(0, 0);
|
||||
bufferCount=lastoutputtime;
|
||||
required *= 4;
|
||||
buffer_scale *= 2.0;
|
||||
|
||||
while (NXFileProgressiveDownloaderAvailable(file, required, &available) == NErr_False)
|
||||
{
|
||||
int percent = (int)((double)available * 100.0 / (double)required);
|
||||
if (percent <= 0)
|
||||
percent=1;
|
||||
if (percent > 99)
|
||||
percent=99;
|
||||
|
||||
Buffering(percent, 0);
|
||||
if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Buffering(100, 0);
|
||||
bufferCount=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern HANDLE threadStarted;
|
||||
DWORD CALLBACK FLACThread(LPVOID param)
|
||||
{
|
||||
buffer_scale=1.0;
|
||||
bool streaming=false;
|
||||
nx_file_t file;
|
||||
FLACClientData state;
|
||||
audio_output.Init(plugin.outMod);
|
||||
paused=false;
|
||||
SetEvent(threadStarted);
|
||||
nx_uri_t filename = (nx_uri_t)param;
|
||||
decodePosition = 0;
|
||||
gain = 1.0;
|
||||
bufferCount = 0;
|
||||
decoder = FLAC__stream_decoder_new();
|
||||
if (decoder == 0)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
|
||||
free(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FLAC__stream_decoder_set_md5_checking(decoder, false);
|
||||
FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
|
||||
plugin.is_seekable = 1;
|
||||
|
||||
|
||||
int ret;
|
||||
|
||||
if (NXPathIsURL(filename) == NErr_True)
|
||||
{
|
||||
// Display a window to request to download
|
||||
//
|
||||
MessageBox(NULL, L"Cannot stream flac file, please download it", NULL, 0);
|
||||
/*
|
||||
char agent[256] = { 0 };
|
||||
snprintf(agent, 256, "User-Agent: %S/%S", "Winamp", "5.9.1");
|
||||
ret = NXFileOpenProgressiveDownloader(&file, filename, nx_file_FILE_read_binary, 0, agent); // TODO: calculate real user agent
|
||||
*/
|
||||
return NErr_Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = NXFileOpenFile(&file, filename, nx_file_FILE_read_binary);
|
||||
}
|
||||
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
|
||||
NXURIRelease(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
state.SetFile(file);
|
||||
|
||||
NXFileLength(file, &fileSize);
|
||||
|
||||
if (FLAC__stream_decoder_init_stream(
|
||||
decoder,
|
||||
FLAC_NXFile_Read,
|
||||
FLAC_NXFile_Seek,
|
||||
FLAC_NXFile_Tell,
|
||||
FLAC_NXFile_Length,
|
||||
FLAC_NXFile_EOF,
|
||||
OnAudio,
|
||||
OnMetadata, // or NULL
|
||||
OnError,
|
||||
&state
|
||||
) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
|
||||
NXURIRelease(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
||||
|
||||
|
||||
|
||||
if (!audio_output.Open(0, channels, samplerate, bps))
|
||||
{
|
||||
FLAC__stream_decoder_finish(decoder);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
|
||||
NXURIRelease(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
plugin.SetInfo(averageBitrate, -1, -1, -1);
|
||||
plugin.outMod->SetVolume(volume);
|
||||
plugin.outMod->SetPan(pan);
|
||||
|
||||
if (streaming && !DoBuffering(file))
|
||||
{
|
||||
FLAC__stream_decoder_finish(decoder);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
NXURIRelease(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_force_seek != -1)
|
||||
APCSeek((ULONG_PTR)m_force_seek);
|
||||
m_force_seek = -1;
|
||||
|
||||
HANDLE events[] = {killswitch};
|
||||
DWORD timeout = 0;
|
||||
while (true)
|
||||
{
|
||||
DWORD result = WaitForMultipleObjectsEx(sizeof(events) / sizeof(*events), events, FALSE, timeout, TRUE);
|
||||
switch (result)
|
||||
{
|
||||
case WAIT_OBJECT_0:// kill thread
|
||||
FLAC__stream_decoder_finish(decoder);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
NXURIRelease(filename);
|
||||
return 0;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
timeout = paused ? PAUSE_TIMEOUT : 0;
|
||||
if (streaming && !DoBuffering(file))
|
||||
{
|
||||
FLAC__stream_decoder_finish(decoder);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
NXURIRelease(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!paused)
|
||||
{
|
||||
FLAC__bool decode_successful = FLAC__stream_decoder_process_single(decoder);
|
||||
|
||||
FLAC__StreamDecoderState FLACstate = FLAC__stream_decoder_get_state(decoder);
|
||||
|
||||
if (FLACstate == FLAC__STREAM_DECODER_END_OF_STREAM)
|
||||
{
|
||||
audio_output.Write(0, 0);
|
||||
if (audio_output.WaitWhilePlaying())
|
||||
{
|
||||
FLAC__stream_decoder_finish(decoder);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
NXURIRelease(filename);
|
||||
return 0;
|
||||
}
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
timeout = INFINITE; // sit and wait for killswitch
|
||||
}
|
||||
else if (!decode_successful)
|
||||
{
|
||||
// some other error - abort playback
|
||||
// if we can find FLAC files with errors, we might be able to gracefully handle some errors
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
timeout = INFINITE; // sit and wait for killswitch
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
424
Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp
Normal file
424
Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp
Normal file
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2011 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "Metadata.h"
|
||||
#include "../nu/ns_wc.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include <shlwapi.h>
|
||||
#include "resource.h"
|
||||
#include "../Agave/Language/api_language.h"
|
||||
#include "Stopper.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
static int FillFileInfo(wchar_t *infoStr, size_t len, FLACMetadata &metadata)
|
||||
{
|
||||
const FLAC__StreamMetadata_StreamInfo *info = metadata.GetStreamInfo();
|
||||
if (info)
|
||||
{
|
||||
unsigned __int64 length = info->total_samples / info->sample_rate;
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_LENGTH_IN_SECONDS), length);
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_CHANNELS), info->channels);
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_BITS_PER_SAMPLE), info->bits_per_sample);
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_SAMPLE_RATE), info->sample_rate);
|
||||
__int64 filesize = metadata.GetFileSize();
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_FILE_SIZE_IN_BYTES), filesize);
|
||||
if (info->total_samples)
|
||||
{
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE), filesize / (125*info->total_samples / (__int64)info->sample_rate)); // (125 is 1000/8)
|
||||
int percent = (int)((100*filesize) / (info->total_samples * (info->bits_per_sample/8) * info->channels));
|
||||
StringCchPrintfExW(infoStr, len, &infoStr, &len, 0, WASABI_API_LNGSTRINGW(IDS_COMPRESSION_RATIO), percent);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool KeywordMatch(const char *mainString, const char *keyword)
|
||||
{
|
||||
return !_stricmp(mainString, keyword);
|
||||
}
|
||||
|
||||
Info *info = 0;
|
||||
FLACMetadata *getMetadata = 0;
|
||||
wchar_t *getFileInfoFn = 0;
|
||||
static FILETIME ftLastWriteTime;
|
||||
|
||||
// is used to determine if the last write time of the file has changed when
|
||||
// asked to get the metadata for the same cached file so we can update things
|
||||
BOOL HasFileTimeChanged(const wchar_t *fn)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA fileData = {0};
|
||||
if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
|
||||
{
|
||||
if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime))
|
||||
{
|
||||
ftLastWriteTime = fileData.ftLastWriteTime;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void UpdateFileTimeChanged(const wchar_t *fn)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA fileData;
|
||||
if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
|
||||
{
|
||||
ftLastWriteTime = fileData.ftLastWriteTime;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetMetadataCache()
|
||||
{
|
||||
// cheap way to trigger a metadata reset in a thread-safe manner
|
||||
wchar_t d[10] = {0};
|
||||
extendedFileInfoStructW reset_info = {0};
|
||||
reset_info.filename=L".flac";
|
||||
reset_info.metadata=L"artist";
|
||||
reset_info.ret = d;
|
||||
reset_info.retlen=10;
|
||||
SendMessage(plugin.hMainWindow, WM_WA_IPC, (WPARAM)&reset_info, IPC_GET_EXTENDED_FILE_INFOW);
|
||||
}
|
||||
|
||||
#define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
|
||||
#define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
|
||||
extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
|
||||
{
|
||||
if (KeywordMatch(data, "type"))
|
||||
{
|
||||
dest[0] = '0';
|
||||
dest[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
if (KeywordMatch(data, "rateable"))
|
||||
{
|
||||
dest[0] = '1';
|
||||
dest[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "lossless"))
|
||||
{
|
||||
dest[0] = '1';
|
||||
dest[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fn || (fn && !fn[0]))
|
||||
return 0;
|
||||
|
||||
if (KeywordMatch(data, "family"))
|
||||
{
|
||||
LPCWSTR e;
|
||||
int pID = -1;
|
||||
DWORD lcid;
|
||||
e = PathFindExtensionW(fn);
|
||||
if (L'.' != *e) return 0;
|
||||
e++;
|
||||
lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
|
||||
if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"FLAC", -1) ||
|
||||
CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"FLA", -1)) pID = IDS_FAMILY_STRING;
|
||||
|
||||
if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (KeywordMatch(data, "mime"))
|
||||
{
|
||||
StringCchCopyW(dest, destlen, L"audio/flac");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!getMetadata || !getFileInfoFn || _wcsicmp(fn, getFileInfoFn) || HasFileTimeChanged(fn))
|
||||
{
|
||||
if (getMetadata)
|
||||
getMetadata->Reset();
|
||||
else
|
||||
getMetadata = new FLACMetadata;
|
||||
|
||||
if (!getMetadata->Open(fn))
|
||||
{
|
||||
delete getMetadata;
|
||||
getMetadata = 0;
|
||||
dest[0]=0;
|
||||
return 0;
|
||||
}
|
||||
free(getFileInfoFn);
|
||||
getFileInfoFn = _wcsdup(fn);
|
||||
}
|
||||
|
||||
FLACMetadata &metadata = *getMetadata;
|
||||
|
||||
if(KeywordMatch(data, "formatinformation"))
|
||||
return FillFileInfo(dest,destlen,metadata);
|
||||
|
||||
const char *lookup=0;
|
||||
if (KeywordMatch(data, "length"))
|
||||
{
|
||||
unsigned __int64 length_in_msec;
|
||||
if (metadata.GetLengthMilliseconds(&length_in_msec))
|
||||
StringCchPrintfW(dest, destlen, L"%d", length_in_msec);
|
||||
else
|
||||
dest[0]=0;
|
||||
return 1;
|
||||
}
|
||||
else if (KeywordMatch(data, "bitrate"))
|
||||
{
|
||||
// TODO: move this into FLACMetadata
|
||||
const FLAC__StreamMetadata_StreamInfo *streaminfo = metadata.GetStreamInfo();
|
||||
if (streaminfo)
|
||||
{
|
||||
if (streaminfo->total_samples == 0 || streaminfo->sample_rate == 0) // prevent divide-by-zero
|
||||
dest[0]=0;
|
||||
else
|
||||
StringCchPrintfW(dest, destlen, L"%I64d", metadata.GetFileSize() / (125*streaminfo->total_samples / (__int64)streaminfo->sample_rate)); // (125 is 1000/8)
|
||||
}
|
||||
else
|
||||
dest[0]=0;
|
||||
return 1;
|
||||
}
|
||||
TAG_ALIAS("title", "TITLE");
|
||||
TAG_ALIAS("artist", "ARTIST");
|
||||
TAG_ALIAS("album", "ALBUM");
|
||||
TAG_ALIAS("genre", "GENRE");
|
||||
TAG_ALIAS("comment", "COMMENT");
|
||||
TAG_ALIAS("year", "DATE");
|
||||
TAG_ALIAS("track", "TRACKNUMBER");
|
||||
TAG_ALIAS("albumartist", "ALBUM ARTIST");
|
||||
TAG_ALIAS("composer", "COMPOSER");
|
||||
TAG_ALIAS("disc", "DISCNUMBER");
|
||||
TAG_ALIAS("publisher", "PUBLISHER");
|
||||
TAG_ALIAS("conductor", "CONDUCTOR");
|
||||
TAG_ALIAS("tool", "ENCODED-BY");
|
||||
TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN");
|
||||
TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK");
|
||||
TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN");
|
||||
TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK");
|
||||
TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID");
|
||||
TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA");
|
||||
TAG_ALIAS("bpm", "BPM");
|
||||
TAG_ALIAS("remixing", "REMIXING");
|
||||
TAG_ALIAS("subtitle", "VERSION");
|
||||
TAG_ALIAS("isrc", "ISRC");
|
||||
TAG_ALIAS("category", "CATEGORY");
|
||||
TAG_ALIAS("rating", "RATING");
|
||||
TAG_ALIAS("producer", "PRODUCER");
|
||||
|
||||
if (!lookup)
|
||||
return 0;
|
||||
|
||||
const char *value = metadata.GetMetadata(lookup);
|
||||
|
||||
if(KeywordMatch("comment",data)) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("DESCRIPTION");
|
||||
}
|
||||
|
||||
if(KeywordMatch("year",data)) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("YEAR");
|
||||
}
|
||||
|
||||
if(KeywordMatch("track",data)) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("TRACK");
|
||||
}
|
||||
|
||||
if(KeywordMatch("albumartist",data)) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("ALBUMARTIST");
|
||||
if(!value || !*value) value = metadata.GetMetadata("ENSEMBLE");
|
||||
}
|
||||
|
||||
if(KeywordMatch("publisher",data)) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("ORGANIZATION");
|
||||
}
|
||||
|
||||
if(KeywordMatch("category",data)) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("CONTENTGROUP");
|
||||
if(!value || !*value) value = metadata.GetMetadata("GROUPING");
|
||||
}
|
||||
|
||||
if(KeywordMatch(data, "rating")) {
|
||||
if(!value || !*value) value = metadata.GetMetadata("RATING");
|
||||
if(value && *value) {
|
||||
int rating = atoi(value);
|
||||
|
||||
// keeps things limited to our range of 0-100
|
||||
if (rating >= 100) {
|
||||
rating = 5;
|
||||
}
|
||||
// 1-100 case
|
||||
else if (rating > 5 && rating < 100) {
|
||||
rating = (rating /= 20);
|
||||
// shift up by one rating when in next band
|
||||
// 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5
|
||||
rating += ((atoi(value) - (rating * 20)) > 0);
|
||||
}
|
||||
else if (rating > 0 && rating <= 5) {
|
||||
}
|
||||
// otherwise just make sure and set zero
|
||||
else {
|
||||
rating = 0;
|
||||
}
|
||||
|
||||
StringCchPrintfW(dest, destlen, L"%u", rating);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
MultiByteToWideCharSZ(CP_UTF8, 0, value, -1, dest, destlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FLACMetadata *setMetadata=0;
|
||||
wchar_t *setFn=0;
|
||||
extern "C" __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
|
||||
{
|
||||
if (!setMetadata || !setFn || lstrcmpiW(fn, setFn))
|
||||
{
|
||||
free(setFn);
|
||||
setFn=_wcsdup(fn);
|
||||
if (!setMetadata)
|
||||
setMetadata = new FLACMetadata;
|
||||
if (setMetadata->Open(setFn, true) == false)
|
||||
{
|
||||
delete setMetadata;
|
||||
setMetadata=0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *lookup=0;
|
||||
START_TAG_ALIAS("artist", "ARTIST");
|
||||
TAG_ALIAS("title", "TITLE");
|
||||
TAG_ALIAS("album", "ALBUM");
|
||||
TAG_ALIAS("genre", "GENRE");
|
||||
TAG_ALIAS("comment", "COMMENT");
|
||||
TAG_ALIAS("year", "DATE");
|
||||
TAG_ALIAS("track", "TRACKNUMBER");
|
||||
TAG_ALIAS("albumartist", "ALBUM ARTIST");
|
||||
TAG_ALIAS("composer", "COMPOSER");
|
||||
TAG_ALIAS("disc", "DISCNUMBER");
|
||||
TAG_ALIAS("publisher", "PUBLISHER");
|
||||
TAG_ALIAS("conductor", "CONDUCTOR");
|
||||
TAG_ALIAS("tool", "ENCODED-BY");
|
||||
TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN");
|
||||
TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK");
|
||||
TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN");
|
||||
TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK");
|
||||
TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID");
|
||||
TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA");
|
||||
TAG_ALIAS("bpm", "BPM");
|
||||
TAG_ALIAS("remixing", "REMIXING");
|
||||
TAG_ALIAS("subtitle", "VERSION");
|
||||
TAG_ALIAS("isrc", "ISRC");
|
||||
TAG_ALIAS("category", "CATEGORY");
|
||||
TAG_ALIAS("rating", "RATING");
|
||||
TAG_ALIAS("producer", "PRODUCER");
|
||||
|
||||
if (!lookup)
|
||||
return 0;
|
||||
|
||||
if (val && *val)
|
||||
{
|
||||
if(KeywordMatch("rating",data))
|
||||
{
|
||||
char temp[128] = {0};
|
||||
StringCchPrintfA(temp, 128, "%u", _wtoi(val)*20);
|
||||
setMetadata->SetMetadata(lookup, temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
setMetadata->SetMetadata(lookup, AutoChar(val, CP_UTF8));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setMetadata->RemoveMetadata(lookup);
|
||||
if(KeywordMatch("comment",data))
|
||||
{
|
||||
// need to remove this one also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
setMetadata->RemoveMetadata("DESCRIPTION");
|
||||
}
|
||||
else if(KeywordMatch("year",data))
|
||||
{
|
||||
// need to remove this one also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
setMetadata->RemoveMetadata("YEAR");
|
||||
}
|
||||
else if(KeywordMatch("track",data))
|
||||
{
|
||||
// need to remove this one also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
setMetadata->RemoveMetadata("TRACK");
|
||||
}
|
||||
else if(KeywordMatch("albumartist",data))
|
||||
{
|
||||
// need to remove these two, also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using these alternate fields
|
||||
setMetadata->RemoveMetadata("ALBUMARTIST");
|
||||
setMetadata->RemoveMetadata("ENSEMBLE");
|
||||
}
|
||||
else if(KeywordMatch("publisher",data))
|
||||
{
|
||||
// need to remove this one also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
setMetadata->RemoveMetadata("ORGANIZATION");
|
||||
}
|
||||
else if(KeywordMatch("category",data))
|
||||
{
|
||||
// need to remove these two also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using these alternate fields
|
||||
setMetadata->RemoveMetadata("CONTENTGROUP");
|
||||
setMetadata->RemoveMetadata("GROUPING");
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int winampWriteExtendedFileInfo()
|
||||
{
|
||||
if (setFn && setMetadata)
|
||||
{
|
||||
Stopper stopper;
|
||||
if (lastfn && !_wcsicmp(lastfn, setFn))
|
||||
stopper.Stop();
|
||||
bool success = setMetadata->Save(setFn);
|
||||
stopper.Play();
|
||||
setMetadata->Reset();
|
||||
free(setFn);
|
||||
setFn=0;
|
||||
|
||||
delete getMetadata;
|
||||
getMetadata=0;
|
||||
|
||||
// update last modified so we're not re-queried on our own updates
|
||||
UpdateFileTimeChanged(setFn);
|
||||
|
||||
return !!success;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) const wchar_t *winampWriteExtendedGetLastError()
|
||||
{
|
||||
return 0;
|
||||
}
|
166
Src/Plugins/Input/in_flac/ExtendedRead.cpp
Normal file
166
Src/Plugins/Input/in_flac/ExtendedRead.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2011 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <FLAC/all.h>
|
||||
#include "StreamFileWin32.h"
|
||||
#include "QuickBuf.h"
|
||||
#include <bfc/platform/types.h>
|
||||
#include <assert.h>
|
||||
#include "FLACFileCallbacks.h"
|
||||
#include "nswasabi/ReferenceCounted.h"
|
||||
|
||||
struct ExtendedRead
|
||||
{
|
||||
int bps, channels, samplerate, truebps;
|
||||
uint64_t samples;
|
||||
QuickBuf output;
|
||||
FLAC__StreamDecoder *decoder;
|
||||
size_t used;
|
||||
FLACClientData client_data;
|
||||
};
|
||||
|
||||
static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||
{
|
||||
//client_data=client_data; // dummy line so i can set a breakpoint
|
||||
}
|
||||
|
||||
static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||
{
|
||||
ExtendedRead *ext = FLAC_GetObject<ExtendedRead>(client_data);
|
||||
|
||||
switch(metadata->type)
|
||||
{
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
{
|
||||
ext->truebps=metadata->data.stream_info.bits_per_sample;
|
||||
ext->bps = (ext->truebps +7) & (~7);
|
||||
ext->channels=metadata->data.stream_info.channels;
|
||||
ext->samplerate=metadata->data.stream_info.sample_rate;
|
||||
ext->samples=metadata->data.stream_info.total_samples;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
|
||||
{
|
||||
ExtendedRead *ext = FLAC_GetObject<ExtendedRead>(client_data);
|
||||
|
||||
int byteLength = (ext->bps/8) * ext->channels * frame->header.blocksize;
|
||||
ext->output.Reserve(byteLength);
|
||||
InterleaveAndTruncate(buffer, ext->output, ext->bps, ext->channels, frame->header.blocksize);
|
||||
ext->used = byteLength;
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec( dllexport ) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
|
||||
{
|
||||
nx_file_t file;
|
||||
ReferenceCountedNXString filename_nx;
|
||||
ReferenceCountedNXURI filename_uri;
|
||||
NXStringCreateWithUTF16(&filename_nx, fn);
|
||||
NXURICreateWithNXString(&filename_uri, filename_nx);
|
||||
|
||||
int ret = NXFileOpenFile(&file, filename_uri, nx_file_FILE_read_binary);
|
||||
if (ret != NErr_Success)
|
||||
return 0;
|
||||
|
||||
ExtendedRead * e = (ExtendedRead *)calloc(sizeof(ExtendedRead), 1);
|
||||
e->decoder = FLAC__stream_decoder_new();
|
||||
if (e->decoder == 0)
|
||||
{
|
||||
NXFileRelease(file);
|
||||
free(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
e->client_data.SetFile(file);
|
||||
e->client_data.SetObject(e);
|
||||
|
||||
FLAC__stream_decoder_set_md5_checking(e->decoder, true);
|
||||
if(FLAC__stream_decoder_init_stream(
|
||||
e->decoder,
|
||||
FLAC_NXFile_Read,
|
||||
FLAC_NXFile_Seek,
|
||||
FLAC_NXFile_Tell,
|
||||
FLAC_NXFile_Length,
|
||||
FLAC_NXFile_EOF,
|
||||
OnAudio,
|
||||
OnMetadata,
|
||||
OnError,
|
||||
&e->client_data
|
||||
) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
FLAC__stream_decoder_delete(e->decoder);
|
||||
NXFileRelease(file);
|
||||
free(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FLAC__stream_decoder_process_until_end_of_metadata(e->decoder);
|
||||
*bps = e->truebps;
|
||||
*nch = e->channels;
|
||||
*srate = e->samplerate;
|
||||
*size = (int)(e->samples * (e->bps/8) * e->channels);
|
||||
return (intptr_t) e;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, size_t len, int *killswitch)
|
||||
{
|
||||
ExtendedRead *ext = (ExtendedRead *)handle;
|
||||
|
||||
while(!ext->used) // loop until we get some data
|
||||
{
|
||||
if (FLAC__stream_decoder_process_single(ext->decoder) == 0)
|
||||
break; // break out if there's an error
|
||||
|
||||
FLAC__StreamDecoderState FLACstate = FLAC__stream_decoder_get_state(ext->decoder);
|
||||
if (FLACstate == FLAC__STREAM_DECODER_END_OF_STREAM) // break out if we hit EOF
|
||||
break;
|
||||
}
|
||||
|
||||
if (ext->used)
|
||||
{
|
||||
size_t toCopy = min(len, ext->used);
|
||||
memcpy(dest, ext->output, toCopy);
|
||||
if (toCopy < ext->used)
|
||||
ext->output.Move(toCopy);
|
||||
ext->used-=toCopy;
|
||||
return toCopy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) void winampGetExtendedRead_close(intptr_t handle)
|
||||
{
|
||||
ExtendedRead *ext = (ExtendedRead *)handle;
|
||||
|
||||
ext->output.Free();
|
||||
FLAC__stream_decoder_finish(ext->decoder);
|
||||
FLAC__stream_decoder_delete(ext->decoder);
|
||||
NXFileRelease(ext->client_data.GetFile());
|
||||
free(ext);
|
||||
}
|
||||
}
|
57
Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp
Normal file
57
Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "FLACFileCallbacks.h"
|
||||
|
||||
|
||||
FLAC__StreamDecoderReadStatus FLAC_NXFile_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
|
||||
{
|
||||
nx_file_t file = FLAC_GetFile(client_data);
|
||||
size_t bytes_to_read = *bytes;
|
||||
size_t bytes_read=0;
|
||||
ns_error_t ret = NXFileRead(file, buffer, bytes_to_read, &bytes_read);
|
||||
*bytes = bytes_read;
|
||||
|
||||
if (ret == NErr_EndOfFile)
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
||||
|
||||
if (ret != NErr_Success)
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus FLAC_NXFile_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
|
||||
{
|
||||
nx_file_t file = FLAC_GetFile(client_data);
|
||||
if (NXFileSeek(file, absolute_byte_offset) == NErr_Success)
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
||||
else
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus FLAC_NXFile_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||||
{
|
||||
nx_file_t file = FLAC_GetFile(client_data);
|
||||
|
||||
if (NXFileTell(file, absolute_byte_offset) == NErr_Success)
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
else
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus FLAC_NXFile_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
|
||||
{
|
||||
nx_file_t file = FLAC_GetFile(client_data);
|
||||
|
||||
if (NXFileLength(file, stream_length) == NErr_Success)
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
else
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
|
||||
}
|
||||
|
||||
FLAC__bool FLAC_NXFile_EOF(const FLAC__StreamDecoder *decoder, void *client_data)
|
||||
{
|
||||
nx_file_t file = FLAC_GetFile(client_data);
|
||||
if (NXFileEndOfFile(file) == NErr_False)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
35
Src/Plugins/Input/in_flac/FLACFileCallbacks.h
Normal file
35
Src/Plugins/Input/in_flac/FLACFileCallbacks.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <FLAC/all.h>
|
||||
#include "nx/nxfile.h"
|
||||
|
||||
|
||||
FLAC__StreamDecoderReadStatus FLAC_NXFile_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
|
||||
FLAC__StreamDecoderSeekStatus FLAC_NXFile_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
|
||||
FLAC__StreamDecoderTellStatus FLAC_NXFile_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
|
||||
FLAC__StreamDecoderLengthStatus FLAC_NXFile_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
|
||||
FLAC__bool FLAC_NXFile_EOF(const FLAC__StreamDecoder *decoder, void *client_data);
|
||||
|
||||
|
||||
class FLACClientData
|
||||
{
|
||||
public:
|
||||
FLACClientData() : object(0) {}
|
||||
void SetFile(nx_file_t file) { this->file = file; }
|
||||
void SetObject(void *object) { this->object = object; }
|
||||
nx_file_t GetFile() { return file; }
|
||||
void *GetObject() { return object; }
|
||||
private:
|
||||
nx_file_t file;
|
||||
void *object;
|
||||
};
|
||||
|
||||
template <typename _t>
|
||||
static _t *FLAC_GetObject(void *client_data)
|
||||
{
|
||||
return (_t *)((FLACClientData *)client_data)->GetObject();
|
||||
}
|
||||
|
||||
static nx_file_t FLAC_GetFile(void *client_data)
|
||||
{
|
||||
return ((FLACClientData *)client_data)->GetFile();
|
||||
}
|
400
Src/Plugins/Input/in_flac/FileInfo.cpp
Normal file
400
Src/Plugins/Input/in_flac/FileInfo.cpp
Normal file
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2011 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
|
||||
#include <FLAC/all.h>
|
||||
#include "main.h"
|
||||
#include "../nu/ns_wc.h"
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "Metadata.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include "Stopper.h"
|
||||
#include <strsafe.h>
|
||||
#include <commctrl.h>
|
||||
#include "../Agave/Language/api_language.h"
|
||||
|
||||
bool FlacTagToWinampTag(wchar_t * tag, int len)
|
||||
{
|
||||
#define TAG_ALIAS(b,a) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
|
||||
TAG_ALIAS("title", "TITLE");
|
||||
TAG_ALIAS("artist", "ARTIST");
|
||||
TAG_ALIAS("album", "ALBUM");
|
||||
TAG_ALIAS("genre", "GENRE");
|
||||
TAG_ALIAS("comment", "COMMENT");
|
||||
TAG_ALIAS("year", "DATE");
|
||||
TAG_ALIAS("track", "TRACKNUMBER");
|
||||
TAG_ALIAS("albumartist", "ALBUM ARTIST");
|
||||
TAG_ALIAS("composer", "COMPOSER");
|
||||
TAG_ALIAS("disc", "DISCNUMBER");
|
||||
TAG_ALIAS("publisher", "PUBLISHER");
|
||||
TAG_ALIAS("conductor", "CONDUCTOR");
|
||||
TAG_ALIAS("bpm", "BPM");
|
||||
return false;
|
||||
#undef TAG_ALIAS
|
||||
}
|
||||
|
||||
bool WinampTagToFlacTag(wchar_t * tag, int len)
|
||||
{
|
||||
#define TAG_ALIAS(a,b) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
|
||||
TAG_ALIAS("title", "TITLE");
|
||||
TAG_ALIAS("artist", "ARTIST");
|
||||
TAG_ALIAS("album", "ALBUM");
|
||||
TAG_ALIAS("genre", "GENRE");
|
||||
TAG_ALIAS("comment", "COMMENT");
|
||||
TAG_ALIAS("year", "DATE");
|
||||
TAG_ALIAS("track", "TRACKNUMBER");
|
||||
TAG_ALIAS("albumartist", "ALBUM ARTIST");
|
||||
TAG_ALIAS("composer", "COMPOSER");
|
||||
TAG_ALIAS("disc", "DISCNUMBER");
|
||||
TAG_ALIAS("publisher", "PUBLISHER");
|
||||
TAG_ALIAS("conductor", "CONDUCTOR");
|
||||
TAG_ALIAS("bpm", "BPM");
|
||||
return false;
|
||||
#undef TAG_ALIAS
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
static int sel=-1;
|
||||
static int ismychange=0;
|
||||
wchar_t key[512]={0};
|
||||
wchar_t value[32768]={0};
|
||||
|
||||
switch(msg)
|
||||
{
|
||||
case WM_NOTIFYFORMAT:
|
||||
return NFR_UNICODE;
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
#define ListView_InsertColumnW(hwnd, iCol, pcol) \
|
||||
(int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol))
|
||||
SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
|
||||
sel=-1;
|
||||
HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
|
||||
LVCOLUMNW lvc = {0, };
|
||||
lvc.mask = LVCF_TEXT|LVCF_WIDTH;
|
||||
lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME);
|
||||
lvc.cx = 82;
|
||||
ListView_InsertColumnW(hwndlist, 0, &lvc);
|
||||
lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE);
|
||||
lvc.cx = 160;
|
||||
ListView_InsertColumnW(hwndlist, 1, &lvc);
|
||||
|
||||
Info *info = (Info *)lParam;
|
||||
int n = info->metadata.GetNumMetadataItems();
|
||||
for(int i=0; i<n; i++) {
|
||||
char key_[512] = {0};
|
||||
const char* value_ = info->metadata.EnumMetadata(i,key_,512);
|
||||
if(value_ && key_[0]) {
|
||||
AutoWide k(key_, CP_UTF8);
|
||||
AutoWide v(value_, CP_UTF8);
|
||||
LVITEMW lvi={LVIF_TEXT,i,0};
|
||||
lvi.pszText = k;
|
||||
SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
|
||||
lvi.iSubItem=1;
|
||||
lvi.pszText = v;
|
||||
SendMessage(hwndlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
}
|
||||
}
|
||||
ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE);
|
||||
ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE);
|
||||
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
}
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
{
|
||||
HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
ListView_DeleteAllItems(hwndlist);
|
||||
while(ListView_DeleteColumn(hwndlist,0));
|
||||
Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
|
||||
delete info;
|
||||
info = 0;
|
||||
}
|
||||
break;
|
||||
case WM_USER:
|
||||
if(wParam && lParam && !ismychange)
|
||||
{
|
||||
wchar_t * value = (wchar_t*)lParam;
|
||||
wchar_t tag[100] = {0};
|
||||
lstrcpynW(tag,(wchar_t*)wParam,100);
|
||||
WinampTagToFlacTag(tag,100);
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
if(!*value)
|
||||
{
|
||||
info->metadata.RemoveMetadata(AutoChar(tag,CP_UTF8));
|
||||
if(!_wcsicmp(L"ALBUM ARTIST",tag))
|
||||
{
|
||||
// need to remove these two, also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using these alternate fields
|
||||
info->metadata.RemoveMetadata("ALBUMARTIST");
|
||||
info->metadata.RemoveMetadata("ENSEMBLE");
|
||||
}
|
||||
if(!_wcsicmp(L"PUBLISHER",tag))
|
||||
{
|
||||
// need to remove this also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
info->metadata.RemoveMetadata("ORGANIZATION");
|
||||
}
|
||||
if(!_wcsicmp(L"DATE",tag))
|
||||
{
|
||||
// need to remove this also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
info->metadata.RemoveMetadata("YEAR");
|
||||
}
|
||||
if(!_wcsicmp(L"TRACKNUMBER",tag))
|
||||
{
|
||||
// need to remove this also, or else it's gonna look like delete doesn't work
|
||||
// if the file was tagged using this alternate field
|
||||
info->metadata.RemoveMetadata("TRACK");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info->metadata.SetMetadata(AutoChar(tag,CP_UTF8),AutoChar(value,CP_UTF8));
|
||||
}
|
||||
HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
int n = ListView_GetItemCount(hlist);
|
||||
for(int i=0; i<n; i++)
|
||||
{
|
||||
key[0]=0;
|
||||
LVITEMW lvi={LVIF_TEXT,i,0};
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=sizeof(key)/sizeof(*key);
|
||||
SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
if(!_wcsicmp(key,tag))
|
||||
{
|
||||
lvi.iSubItem = 1;
|
||||
lvi.pszText = value;
|
||||
SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
if(!*value)
|
||||
ListView_DeleteItem(hlist,i);
|
||||
else if(ListView_GetItemState(hlist,i,LVIS_SELECTED))
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// bew hew, not found
|
||||
LVITEMW lvi={0,0x7FFFFFF0,0};
|
||||
n = SendMessage(hlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
|
||||
lvi.mask = LVIF_TEXT;
|
||||
lvi.iItem = n;
|
||||
lvi.iSubItem = 0;
|
||||
lvi.pszText = tag;
|
||||
SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.iSubItem = 1;
|
||||
lvi.pszText = value;
|
||||
SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
}
|
||||
break;
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR l=(LPNMHDR)lParam;
|
||||
if(l->idFrom==IDC_LIST && l->code == LVN_KEYDOWN) {
|
||||
if((((LPNMLVKEYDOWN)l)->wVKey) == VK_DELETE){
|
||||
int selitem = ListView_GetNextItem(l->hwndFrom,-1,LVNI_SELECTED|LVNI_FOCUSED);
|
||||
if(selitem != -1)
|
||||
SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_DEL,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDC_BUTTON_DEL));
|
||||
}
|
||||
}
|
||||
else if(l->idFrom==IDC_LIST && l->code == LVN_ITEMCHANGED) {
|
||||
LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
|
||||
if(lv->uNewState & LVIS_SELECTED) {
|
||||
int n = lv->iItem;
|
||||
LVITEMW lvi={LVIF_TEXT,lv->iItem,0};
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=sizeof(key)/sizeof(*key);
|
||||
SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.pszText=value;
|
||||
lvi.cchTextMax=sizeof(value)/sizeof(*value);
|
||||
lvi.iSubItem=1;
|
||||
SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,key);
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
|
||||
sel = n;
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),TRUE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),TRUE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),TRUE);
|
||||
}
|
||||
if(lv->uOldState & LVIS_SELECTED) {
|
||||
sel = -1;
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch(LOWORD(wParam)) {
|
||||
case IDOK:
|
||||
{
|
||||
Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
|
||||
Stopper stopper;
|
||||
if (lastfn && !_wcsicmp(lastfn, info->filename))
|
||||
stopper.Stop();
|
||||
bool success = info->metadata.Save(info->filename);
|
||||
stopper.Play();
|
||||
if (success)
|
||||
{
|
||||
ResetMetadataCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t title[128] = {0};
|
||||
MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_CANNOT_SAVE_METADATA),
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA,title,128),
|
||||
MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IDC_NAME:
|
||||
case IDC_VALUE:
|
||||
if(HIWORD(wParam) == EN_CHANGE && sel>=0) {
|
||||
LVITEMW lvi={LVIF_TEXT,sel,0};
|
||||
GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
|
||||
GetDlgItemTextW(hwndDlg,IDC_VALUE,value,sizeof(value)/sizeof(*value));
|
||||
lvi.pszText=key;
|
||||
lvi.cchTextMax=sizeof(key)/sizeof(*key);
|
||||
SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
lvi.pszText=value;
|
||||
lvi.cchTextMax=sizeof(value)/sizeof(*value);
|
||||
lvi.iSubItem=1;
|
||||
SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
|
||||
FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
|
||||
ismychange=0;
|
||||
}
|
||||
else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) {
|
||||
GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
|
||||
GetDlgItemTextW(hwndDlg,IDC_VALUE,value,sizeof(value)/sizeof(*value));
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
char oldkeyA[100]="";
|
||||
bool newitem=true;
|
||||
if(sel < info->metadata.GetNumMetadataItems()) {
|
||||
info->metadata.EnumMetadata(sel,oldkeyA,100);
|
||||
newitem=false;
|
||||
}
|
||||
AutoWide oldkey(oldkeyA);
|
||||
if(!newitem && _wcsicmp(oldkey,key)) { // key changed
|
||||
info->metadata.SetTag(sel,AutoChar(key,CP_UTF8));
|
||||
} else {
|
||||
info->metadata.SetMetadata(AutoChar(key,CP_UTF8),AutoChar(value,CP_UTF8));
|
||||
}
|
||||
FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
|
||||
ismychange=0;
|
||||
}
|
||||
break;
|
||||
case IDC_BUTTON_DEL:
|
||||
if(sel >= 0){
|
||||
GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
if(sel < info->metadata.GetNumMetadataItems())
|
||||
info->metadata.RemoveMetadata(sel);
|
||||
ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel);
|
||||
sel=-1;
|
||||
FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)L"");
|
||||
ismychange=0;
|
||||
}
|
||||
break;
|
||||
case IDC_BUTTON_DELALL:
|
||||
ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_LIST));
|
||||
SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
|
||||
SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
|
||||
EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
|
||||
sel=-1;
|
||||
{
|
||||
Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
||||
int n = info->metadata.GetNumMetadataItems();
|
||||
while(n>0) {
|
||||
--n;
|
||||
char tag[100] = {0};
|
||||
info->metadata.EnumMetadata(n,tag,100);
|
||||
MultiByteToWideCharSZ(CP_UTF8, 0, tag, -1, key, sizeof(key)/sizeof(*key));
|
||||
FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
|
||||
ismychange=1;
|
||||
SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)L"");
|
||||
ismychange=0;
|
||||
info->metadata.RemoveMetadata(n);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IDC_BUTTON_ADD:
|
||||
{
|
||||
HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
|
||||
LVITEMW lvi={0,0x7FFFFFF0,0};
|
||||
int n = SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
|
||||
ListView_SetItemState(hwndlist,n,LVIS_SELECTED,LVIS_SELECTED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
// return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
|
||||
// if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
|
||||
__declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.
|
||||
// Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").
|
||||
// filename will be valid for the life of your window. n is the tab number. This function will first be
|
||||
// called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).
|
||||
// The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.
|
||||
// when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue");
|
||||
// this will be broadcast to all panes (including yours) as a WM_USER.
|
||||
__declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)
|
||||
{
|
||||
if(n == 0) { // add first pane
|
||||
SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);
|
||||
info = new Info;
|
||||
info->filename = filename;
|
||||
info->metadata.Open(filename, true);
|
||||
return WASABI_API_CREATEDIALOGPARAMW(IDD_INFOCHILD_ADVANCED,parent,ChildProc_Advanced,(LPARAM)info);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
577
Src/Plugins/Input/in_flac/Metadata.cpp
Normal file
577
Src/Plugins/Input/in_flac/Metadata.cpp
Normal file
|
@ -0,0 +1,577 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2011 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
#include "Metadata.h"
|
||||
#include <FLAC/all.h>
|
||||
#include "StreamFileWin32.h" // for FileSize64
|
||||
#include "api__in_flv.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
struct MetadataReader
|
||||
{
|
||||
MetadataReader(HANDLE _handle)
|
||||
{
|
||||
handle = _handle;
|
||||
endOfFile=false;
|
||||
}
|
||||
HANDLE handle;
|
||||
bool endOfFile;
|
||||
};
|
||||
|
||||
static size_t win32_read(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
|
||||
{
|
||||
MetadataReader *reader = (MetadataReader *)handle;
|
||||
DWORD bytesRead=0;
|
||||
BOOL result = ReadFile(reader->handle, ptr, size*nmemb, &bytesRead, NULL);
|
||||
if (result == TRUE && bytesRead == 0)
|
||||
reader->endOfFile=true;
|
||||
return bytesRead/size;
|
||||
}
|
||||
|
||||
static size_t win32_write(const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
|
||||
{
|
||||
MetadataReader *reader = (MetadataReader *)handle;
|
||||
DWORD bytesWritten=0;
|
||||
WriteFile(reader->handle, ptr, size*nmemb, &bytesWritten, NULL);
|
||||
return bytesWritten/size;
|
||||
}
|
||||
|
||||
static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
|
||||
li.QuadPart = distance;
|
||||
|
||||
li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
|
||||
|
||||
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
|
||||
{
|
||||
li.QuadPart = -1;
|
||||
}
|
||||
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
static int win32_seek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
|
||||
{
|
||||
MetadataReader *reader = (MetadataReader *)handle;
|
||||
if (Seek64(reader->handle, offset, whence) == -1)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static FLAC__int64 win32_tell(FLAC__IOHandle handle)
|
||||
{
|
||||
MetadataReader *reader = (MetadataReader *)handle;
|
||||
return Seek64(reader->handle, 0, FILE_CURRENT);
|
||||
}
|
||||
|
||||
static int win32_eof(FLAC__IOHandle handle)
|
||||
{
|
||||
MetadataReader *reader = (MetadataReader *)handle;
|
||||
return !!reader->endOfFile;
|
||||
}
|
||||
|
||||
static int win32_close(FLAC__IOHandle handle)
|
||||
{
|
||||
MetadataReader *reader = (MetadataReader *)handle;
|
||||
|
||||
CloseHandle(reader->handle);
|
||||
reader->handle = INVALID_HANDLE_VALUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FLAC__IOCallbacks unicodeIO =
|
||||
{
|
||||
win32_read,
|
||||
win32_write,
|
||||
win32_seek,
|
||||
win32_tell,
|
||||
win32_eof,
|
||||
win32_close,
|
||||
};
|
||||
|
||||
FLACMetadata::FLACMetadata()
|
||||
{
|
||||
chain=FLAC__metadata_chain_new();
|
||||
itr = FLAC__metadata_iterator_new();
|
||||
block=0;
|
||||
streamInfo=0;
|
||||
filesize=0;
|
||||
}
|
||||
|
||||
FLACMetadata::~FLACMetadata()
|
||||
{
|
||||
if (chain)
|
||||
FLAC__metadata_chain_delete(chain);
|
||||
if (itr)
|
||||
FLAC__metadata_iterator_delete(itr);
|
||||
}
|
||||
|
||||
void FLACMetadata::Reset()
|
||||
{
|
||||
if (chain)
|
||||
FLAC__metadata_chain_delete(chain);
|
||||
if (itr)
|
||||
FLAC__metadata_iterator_delete(itr);
|
||||
chain=FLAC__metadata_chain_new();
|
||||
itr = FLAC__metadata_iterator_new();
|
||||
block=0;
|
||||
streamInfo=0;
|
||||
filesize=0;
|
||||
}
|
||||
|
||||
const FLAC__StreamMetadata_StreamInfo *FLACMetadata::GetStreamInfo()
|
||||
{
|
||||
if (streamInfo)
|
||||
return &streamInfo->data.stream_info;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FLACMetadata::Open(const wchar_t *filename, bool optimize)
|
||||
{
|
||||
if (!chain || !itr)
|
||||
return false;
|
||||
|
||||
HANDLE hfile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
MetadataReader reader(hfile);
|
||||
if (reader.handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
filesize = FileSize64(reader.handle);
|
||||
|
||||
FLAC__bool success = FLAC__metadata_chain_read_with_callbacks(chain, &reader, unicodeIO);
|
||||
CloseHandle(hfile);
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
if (optimize)
|
||||
{
|
||||
FLAC__metadata_chain_sort_padding(chain);
|
||||
FLAC__metadata_chain_merge_padding(chain);
|
||||
}
|
||||
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
while (1)
|
||||
{
|
||||
FLAC__MetadataType type=FLAC__metadata_iterator_get_block_type(itr);
|
||||
switch (type)
|
||||
{
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
block = FLAC__metadata_iterator_get_block(itr);
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
streamInfo = FLAC__metadata_iterator_get_block(itr);
|
||||
break;
|
||||
}
|
||||
if (FLAC__metadata_iterator_next(itr) == false)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *FLACMetadata::GetMetadata(const char *tag)
|
||||
{
|
||||
if (!block)
|
||||
return 0;
|
||||
|
||||
int pos = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, tag);
|
||||
if (pos < 0)
|
||||
{
|
||||
// fail
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *entry = (const char *)block->data.vorbis_comment.comments[pos].entry;
|
||||
const char *metadata = strchr(entry, '='); // find the first equal
|
||||
if (metadata)
|
||||
{
|
||||
return metadata+1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FLACMetadata::SetMetadata(const char *tag, const char *value)
|
||||
{
|
||||
if (!block)
|
||||
{
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
do
|
||||
{
|
||||
if (FLAC__METADATA_TYPE_VORBIS_COMMENT == FLAC__metadata_iterator_get_block_type(itr))
|
||||
{
|
||||
block = FLAC__metadata_iterator_get_block(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (FLAC__metadata_iterator_next(itr) != 0);
|
||||
if (!block)
|
||||
{
|
||||
block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
FLAC__metadata_iterator_insert_block_after(itr, block);
|
||||
}
|
||||
}
|
||||
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
FLAC__StreamMetadata_VorbisComment_Entry entry;
|
||||
size_t totalLen = strlen(tag) + 1 /* = */ + strlen(value);
|
||||
entry.entry = (FLAC__byte *)malloc(totalLen + 1);
|
||||
entry.length = totalLen;
|
||||
|
||||
StringCchPrintfA((char *)entry.entry, totalLen+1, "%s=%s", tag, value);
|
||||
|
||||
int pos = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, tag);
|
||||
if (pos < 0)
|
||||
{
|
||||
//new comment
|
||||
FLAC__metadata_object_vorbiscomment_append_comment(block, entry, true);
|
||||
// would love to not copy, but we can't guarantee that FLAC links to the same CRT as us
|
||||
}
|
||||
else
|
||||
{
|
||||
FLAC__metadata_object_vorbiscomment_set_comment(block, pos, entry, true);
|
||||
// would love to not copy, but we can't guarantee that FLAC links to the same CRT as us
|
||||
}
|
||||
free(entry.entry);
|
||||
}
|
||||
|
||||
void FLACMetadata::RemoveMetadata(const char *tag)
|
||||
{
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, tag);
|
||||
}
|
||||
|
||||
void FLACMetadata::RemoveMetadata(int n)
|
||||
{
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned int)n);
|
||||
}
|
||||
|
||||
static FLAC__StreamMetadata *GetOrMakePadding(FLAC__Metadata_Chain *chain, FLAC__Metadata_Iterator *itr)
|
||||
{
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
while (1)
|
||||
{
|
||||
if (FLAC__METADATA_TYPE_PADDING == FLAC__metadata_iterator_get_block_type(itr))
|
||||
{
|
||||
FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
|
||||
return block;
|
||||
}
|
||||
|
||||
if (FLAC__metadata_iterator_next(itr) == false)
|
||||
break;
|
||||
}
|
||||
FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
|
||||
if (padding)
|
||||
FLAC__metadata_iterator_insert_block_after(itr, padding);
|
||||
|
||||
return padding;
|
||||
}
|
||||
|
||||
bool FLACMetadata::Save(const wchar_t *filename)
|
||||
{
|
||||
if (FLAC__metadata_chain_check_if_tempfile_needed(chain, true))
|
||||
{
|
||||
// since we needed to write a tempfile, let's add some more padding so it doesn't happen again
|
||||
FLAC__metadata_chain_sort_padding(chain);
|
||||
|
||||
FLAC__StreamMetadata *padding = GetOrMakePadding(chain, itr);
|
||||
if (padding && padding->length < 16384)
|
||||
padding->length = 16384; // TODO: configurable padding size
|
||||
|
||||
HANDLE hfile = CreateFileW(filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
|
||||
MetadataReader reader(hfile);
|
||||
if (reader.handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
wchar_t tempPath[MAX_PATH-14] = {0}, tempFile[MAX_PATH] = {0};
|
||||
GetTempPathW(MAX_PATH-14, tempPath);
|
||||
GetTempFileNameW(tempPath, L"waf", 0, tempFile);
|
||||
|
||||
HANDLE hTempFile = CreateFileW(tempFile, GENERIC_READ|GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
|
||||
MetadataReader tempReader(hTempFile);
|
||||
|
||||
FLAC__bool res = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, false, &reader, unicodeIO, &tempReader, unicodeIO);
|
||||
|
||||
CloseHandle(hfile);
|
||||
CloseHandle(hTempFile);
|
||||
if (!MoveFileW(tempFile, filename))
|
||||
{
|
||||
if (CopyFileW(tempFile, filename, FALSE))
|
||||
{
|
||||
DeleteFileW(tempFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteFileW(tempFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !!res;
|
||||
}
|
||||
else
|
||||
{
|
||||
HANDLE hfile = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
|
||||
MetadataReader reader(hfile);
|
||||
if (reader.handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
FLAC__bool res = FLAC__metadata_chain_write_with_callbacks(chain, true, &reader, unicodeIO);
|
||||
|
||||
CloseHandle(hfile);
|
||||
return !!res;
|
||||
}
|
||||
}
|
||||
|
||||
bool FLACMetadata::GetLengthMilliseconds(unsigned __int64 *length)
|
||||
{
|
||||
if (!streamInfo)
|
||||
return false;
|
||||
*length = (__int64)((double)(FLAC__int64)streamInfo->data.stream_info.total_samples / (double)streamInfo->data.stream_info.sample_rate * 1000.0 + 0.5);
|
||||
return true;
|
||||
}
|
||||
|
||||
int FLACMetadata::GetNumMetadataItems()
|
||||
{
|
||||
if (block) return block->data.vorbis_comment.num_comments;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
const char* FLACMetadata::EnumMetadata(int n, char *tag, int taglen)
|
||||
{
|
||||
if (tag) tag[0]=0;
|
||||
if (!block) return 0;
|
||||
const char *entry = (const char *)block->data.vorbis_comment.comments[n].entry;
|
||||
const char *metadata = strchr(entry, '='); // find the first equal
|
||||
if (metadata)
|
||||
{
|
||||
if (tag) lstrcpynA(tag,entry,min(metadata-entry+1,taglen));
|
||||
return metadata+1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void FLACMetadata::SetTag(int pos, const char *tag)
|
||||
{
|
||||
char * value = (char*)EnumMetadata(pos,0,0);
|
||||
value = _strdup(value?value:"");
|
||||
FLAC__StreamMetadata_VorbisComment_Entry entry;
|
||||
size_t totalLen = strlen(tag) + 1 /* = */ + strlen(value);
|
||||
entry.entry = (FLAC__byte *)malloc(totalLen + 1);
|
||||
entry.length = totalLen;
|
||||
StringCchPrintfA((char *)entry.entry, totalLen+1, "%s=%s", tag, value);
|
||||
FLAC__metadata_object_vorbiscomment_set_comment(block, pos, entry, true);
|
||||
free(value);
|
||||
}
|
||||
|
||||
bool FLACMetadata::GetPicture(FLAC__StreamMetadata_Picture_Type type, void **data, size_t *len, wchar_t **mimeType)
|
||||
{
|
||||
if (!chain || !itr)
|
||||
return false;
|
||||
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
while (1)
|
||||
{
|
||||
if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
|
||||
{
|
||||
FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
|
||||
FLAC__StreamMetadata_Picture &picture = block->data.picture;
|
||||
if (picture.type == type)
|
||||
{
|
||||
*len = picture.data_length;
|
||||
*data = WASABI_API_MEMMGR->sysMalloc(picture.data_length);
|
||||
if (!*data)
|
||||
return false;
|
||||
memcpy(*data, picture.data, picture.data_length);
|
||||
|
||||
char *type = 0;
|
||||
if (picture.mime_type)
|
||||
type = strchr(picture.mime_type, '/');
|
||||
|
||||
if (type && *type)
|
||||
{
|
||||
type++;
|
||||
|
||||
char *type2 = strchr(type, '/');
|
||||
if (type2 && *type2) type2++;
|
||||
else type2 = type;
|
||||
|
||||
int typelen = MultiByteToWideChar(CP_ACP, 0, type2, -1, 0, 0);
|
||||
*mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
|
||||
if (*mimeType)
|
||||
MultiByteToWideChar(CP_ACP, 0, type, -1, *mimeType, typelen);
|
||||
}
|
||||
else
|
||||
*mimeType = 0; // unknown!
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAC__metadata_iterator_next(itr) == false)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FLACMetadata::RemovePicture(FLAC__StreamMetadata_Picture_Type type)
|
||||
{
|
||||
if (!chain || !itr)
|
||||
return false;
|
||||
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
while (1)
|
||||
{
|
||||
if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
|
||||
{
|
||||
FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
|
||||
FLAC__StreamMetadata_Picture &picture = block->data.picture;
|
||||
if (picture.type == type)
|
||||
{
|
||||
FLAC__metadata_iterator_delete_block(itr, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAC__metadata_iterator_next(itr) == false)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FLACMetadata::SetPicture(FLAC__StreamMetadata_Picture_Type type, void *data, size_t len, const wchar_t *mimeType, int width, int height)
|
||||
{
|
||||
if (!chain || !itr)
|
||||
return false;
|
||||
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
while (1)
|
||||
{
|
||||
if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
|
||||
{
|
||||
FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
|
||||
FLAC__StreamMetadata_Picture &picture = block->data.picture;
|
||||
if (picture.type == type)
|
||||
{
|
||||
FLAC__metadata_object_picture_set_data(block, (FLAC__byte *)data, len, true);
|
||||
picture.width = width;
|
||||
picture.height = height;
|
||||
picture.depth = 32;
|
||||
picture.colors = 0;
|
||||
|
||||
FLAC__metadata_object_picture_set_description(block, (FLAC__byte *)"", true);// TODO?
|
||||
|
||||
char mime[256] = {0};
|
||||
if (wcsstr(mimeType, L"/") != 0)
|
||||
{
|
||||
StringCchPrintfA(mime, 256, "%S", mimeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfA(mime, 256, "image/%S", mimeType);
|
||||
}
|
||||
FLAC__metadata_object_picture_set_mime_type(block, mime, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAC__metadata_iterator_next(itr) == false)
|
||||
break;
|
||||
}
|
||||
|
||||
// not found. let's add it
|
||||
FLAC__StreamMetadata *newBlock = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE);
|
||||
|
||||
FLAC__metadata_object_picture_set_data(newBlock, (FLAC__byte *)data, len, true);
|
||||
FLAC__StreamMetadata_Picture &picture = newBlock->data.picture;
|
||||
picture.type = type;
|
||||
picture.width = width;
|
||||
picture.height = height;
|
||||
picture.depth = 32;
|
||||
picture.colors = 0;
|
||||
|
||||
FLAC__metadata_object_picture_set_description(newBlock, (FLAC__byte *)"", true);// TODO?
|
||||
|
||||
char mime[256] = {0};
|
||||
StringCchPrintfA(mime, 256, "image/%S", mimeType);
|
||||
FLAC__metadata_object_picture_set_mime_type(newBlock, mime, true);
|
||||
|
||||
FLAC__metadata_iterator_insert_block_after(itr, newBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FLACMetadata::GetIndexPicture(int index, FLAC__StreamMetadata_Picture_Type *type, void **data, size_t *len, wchar_t **mimeType)
|
||||
{
|
||||
if (!chain || !itr)
|
||||
return false;
|
||||
int i=0;
|
||||
FLAC__metadata_iterator_init(itr, chain);
|
||||
while (1)
|
||||
{
|
||||
if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
|
||||
{
|
||||
FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
|
||||
FLAC__StreamMetadata_Picture &picture = block->data.picture;
|
||||
if (i++ == index)
|
||||
{
|
||||
*type = picture.type;
|
||||
*len = picture.data_length;
|
||||
*data = WASABI_API_MEMMGR->sysMalloc(picture.data_length);
|
||||
if (!*data)
|
||||
return false;
|
||||
memcpy(*data, picture.data, picture.data_length);
|
||||
|
||||
char *type = 0;
|
||||
if (picture.mime_type)
|
||||
type = strchr(picture.mime_type, '/');
|
||||
|
||||
if (type && *type)
|
||||
{
|
||||
type++;
|
||||
int typelen = MultiByteToWideChar(CP_ACP, 0, type, -1, 0, 0);
|
||||
*mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
|
||||
if (*mimeType)
|
||||
MultiByteToWideChar(CP_ACP, 0, type, -1, *mimeType, typelen);
|
||||
}
|
||||
else
|
||||
*mimeType = 0; // unknown!
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAC__metadata_iterator_next(itr) == false)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
48
Src/Plugins/Input/in_flac/Metadata.h
Normal file
48
Src/Plugins/Input/in_flac/Metadata.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef NULLSOFT_IN_FLAC_METADATA_H
|
||||
#define NULLSOFT_IN_FLAC_METADATA_H
|
||||
|
||||
#include <FLAC/all.h>
|
||||
|
||||
class FLACMetadata
|
||||
{
|
||||
public:
|
||||
FLACMetadata();
|
||||
~FLACMetadata();
|
||||
bool Open(const wchar_t *filename, bool optimize=false);
|
||||
void Reset();
|
||||
const char *GetMetadata(const char *tag);
|
||||
void SetMetadata(const char *tag, const char *value);
|
||||
void RemoveMetadata(const char *tag);
|
||||
void RemoveMetadata(int n);
|
||||
bool Save(const wchar_t *filename);
|
||||
const FLAC__StreamMetadata_StreamInfo *GetStreamInfo();
|
||||
__int64 GetFileSize() { return filesize; }
|
||||
bool GetLengthMilliseconds(unsigned __int64 *length);
|
||||
int GetNumMetadataItems();
|
||||
const char* EnumMetadata(int n, char *tag, int len);
|
||||
void SetTag(int n, const char *tag);
|
||||
|
||||
bool GetPicture(FLAC__StreamMetadata_Picture_Type type, void **data, size_t *len, wchar_t **mimeType);
|
||||
bool GetIndexPicture(int index, FLAC__StreamMetadata_Picture_Type *type, void **data, size_t *len, wchar_t **mimeType);
|
||||
bool RemovePicture(FLAC__StreamMetadata_Picture_Type type);
|
||||
bool SetPicture(FLAC__StreamMetadata_Picture_Type type, void *data, size_t len, const wchar_t *mimeType, int width, int height);
|
||||
private:
|
||||
FLAC__Metadata_Chain *chain;
|
||||
FLAC__Metadata_Iterator *itr;
|
||||
FLAC__StreamMetadata *block;
|
||||
FLAC__StreamMetadata *streamInfo;
|
||||
__int64 filesize;
|
||||
};
|
||||
|
||||
class Info
|
||||
{
|
||||
public:
|
||||
FLACMetadata metadata;
|
||||
const wchar_t *filename;
|
||||
};
|
||||
|
||||
extern FLACMetadata *getMetadata;
|
||||
extern wchar_t *getFileInfoFn;
|
||||
extern Info *info;
|
||||
|
||||
#endif
|
110
Src/Plugins/Input/in_flac/Preferences.cpp
Normal file
110
Src/Plugins/Input/in_flac/Preferences.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2011 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
#include "main.h"
|
||||
#include <FLAC/all.h>
|
||||
#include "resource.h"
|
||||
#include "../Agave/Language/api_language.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include <assert.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
bool fixBitrate=false;
|
||||
bool config_average_bitrate=true;
|
||||
|
||||
// the return pointer has been malloc'd. Use free() when you are done.
|
||||
char *BuildExtensions(const char *extensions)
|
||||
{
|
||||
char name[64] = {0};
|
||||
WASABI_API_LNGSTRING_BUF(IDS_FLAC_FILES,name,64);
|
||||
size_t length = strlen(extensions) + 1 + strlen(name) + 2;
|
||||
char *newExt = (char *)malloc(length);
|
||||
char *ret = newExt; // save because we modify newExt
|
||||
|
||||
// copy extensions
|
||||
StringCchCopyExA(newExt, length, extensions, &newExt, &length, 0);
|
||||
newExt++;
|
||||
length--;
|
||||
|
||||
// copy description
|
||||
StringCchCopyExA(newExt, length, name, &newExt, &length, 0);
|
||||
newExt++;
|
||||
length--;
|
||||
|
||||
// double null terminate
|
||||
assert(length == 1);
|
||||
*newExt = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK PreferencesProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch(msg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
wchar_t config_extensions[128] = {0};
|
||||
GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, config_extensions, 128, winampINI);
|
||||
SetDlgItemTextW(hwndDlg, IDC_EXTENSIONS, config_extensions);
|
||||
CheckDlgButton(hwndDlg, IDC_AVERAGE_BITRATE, config_average_bitrate?BST_CHECKED:BST_UNCHECKED);
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case WM_DESTROY:
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch(LOWORD(wParam))
|
||||
{
|
||||
case IDOK:
|
||||
{
|
||||
wchar_t config_extensions[128] = {0};
|
||||
GetDlgItemTextW(hwndDlg, IDC_EXTENSIONS, config_extensions, 128);
|
||||
if (!lstrcmpiW(config_extensions, DEFAULT_EXTENSIONSW))
|
||||
WritePrivateProfileStringW(L"in_flac", L"extensions", 0, winampINI);
|
||||
else
|
||||
WritePrivateProfileStringW(L"in_flac", L"extensions", config_extensions, winampINI);
|
||||
|
||||
plugin.FileExtensions = BuildExtensions(AutoChar(config_extensions));
|
||||
config_average_bitrate = !!IsDlgButtonChecked(hwndDlg, IDC_AVERAGE_BITRATE);
|
||||
if (config_average_bitrate)
|
||||
WritePrivateProfileStringW(L"in_flac", L"average_bitrate", L"1", winampINI);
|
||||
else
|
||||
WritePrivateProfileStringW(L"in_flac", L"average_bitrate", L"0", winampINI);
|
||||
|
||||
fixBitrate=true;
|
||||
EndDialog(hwndDlg, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case IDCANCEL:
|
||||
EndDialog(hwndDlg, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Config(HWND hwndParent)
|
||||
{
|
||||
WASABI_API_DIALOGBOXW(IDD_PREFERENCES, hwndParent, PreferencesProc);
|
||||
}
|
50
Src/Plugins/Input/in_flac/QuickBuf.h
Normal file
50
Src/Plugins/Input/in_flac/QuickBuf.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef NULLSOFT_IN_FLAC_QUICKBUF_H
|
||||
#define NULLSOFT_IN_FLAC_QUICKBUF_H
|
||||
|
||||
#include <malloc.h>
|
||||
class QuickBuf
|
||||
{
|
||||
public:
|
||||
QuickBuf() : buffer(0), len(0)
|
||||
{
|
||||
}
|
||||
|
||||
void Reserve(size_t res)
|
||||
{
|
||||
if (res > len)
|
||||
{
|
||||
len=res;
|
||||
free(buffer);
|
||||
buffer = malloc(len);
|
||||
}
|
||||
}
|
||||
|
||||
void Free()
|
||||
{
|
||||
free(buffer);
|
||||
buffer=0;
|
||||
len=0;
|
||||
}
|
||||
|
||||
void Move(size_t offset)
|
||||
{
|
||||
memmove(buffer, (char *)buffer + offset, len-offset);
|
||||
}
|
||||
|
||||
|
||||
operator void *()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
operator char *()
|
||||
{
|
||||
return (char *)buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
void *buffer;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
#endif
|
174
Src/Plugins/Input/in_flac/RawReader.cpp
Normal file
174
Src/Plugins/Input/in_flac/RawReader.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include "RawReader.h"
|
||||
#include "main.h"
|
||||
#include <limits.h>
|
||||
#include <shlwapi.h>
|
||||
#include "../nu/AutoWide.h"
|
||||
#include <new>
|
||||
#include <strsafe.h>
|
||||
#include "nswasabi/ReferenceCounted.h"
|
||||
|
||||
static bool IsMyExtension(const wchar_t *filename)
|
||||
{
|
||||
const wchar_t *extension = PathFindExtensionW(filename);
|
||||
if (extension && *extension)
|
||||
{
|
||||
wchar_t exts[128] = {0};
|
||||
GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, exts, 128, winampINI);
|
||||
|
||||
extension++;
|
||||
wchar_t *b = exts;
|
||||
wchar_t *c;
|
||||
do
|
||||
{
|
||||
wchar_t d[20] = {0};
|
||||
StringCchCopyW(d, 15, b);
|
||||
if ((c = wcschr(b, L';')))
|
||||
{
|
||||
if ((c-b)<15)
|
||||
d[c - b] = 0;
|
||||
}
|
||||
|
||||
if (!lstrcmpiW(extension, d))
|
||||
return true;
|
||||
|
||||
b = c + 1;
|
||||
}
|
||||
while (c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int RawMediaReaderService::CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **out_reader)
|
||||
{
|
||||
if (IsMyExtension(filename))
|
||||
{
|
||||
nx_file_t file;
|
||||
ReferenceCountedNXString filename_nx;
|
||||
ReferenceCountedNXURI filename_uri;
|
||||
NXStringCreateWithUTF16(&filename_nx, filename);
|
||||
NXURICreateWithNXString(&filename_uri, filename_nx);
|
||||
|
||||
int ret = NXFileOpenFile(&file, filename_uri, nx_file_FILE_read_binary);
|
||||
if (ret != NErr_Success)
|
||||
return ret;
|
||||
|
||||
RawMediaReader *reader = new (std::nothrow) RawMediaReader();
|
||||
if (!reader)
|
||||
{
|
||||
NXFileRelease(file);
|
||||
return NErr_OutOfMemory;
|
||||
}
|
||||
|
||||
ret = reader->Initialize(file);
|
||||
NXFileRelease(file);
|
||||
if (ret != NErr_Success)
|
||||
{
|
||||
delete reader;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*out_reader = reader;
|
||||
return NErr_Success;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return NErr_False;
|
||||
}
|
||||
}
|
||||
|
||||
#define CBCLASS RawMediaReaderService
|
||||
START_DISPATCH;
|
||||
CB(CREATERAWMEDIAREADER, CreateRawMediaReader);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||
{
|
||||
//client_data=client_data; // dummy line so i can set a breakpoint
|
||||
}
|
||||
|
||||
static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
|
||||
{
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
RawMediaReader::RawMediaReader()
|
||||
{
|
||||
decoder=0;
|
||||
file=0;
|
||||
}
|
||||
|
||||
RawMediaReader::~RawMediaReader()
|
||||
{
|
||||
if (decoder)
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
NXFileRelease(file);
|
||||
}
|
||||
|
||||
int RawMediaReader::Initialize(nx_file_t file)
|
||||
{
|
||||
this->file = NXFileRetain(file);
|
||||
decoder = FLAC__stream_decoder_new();
|
||||
if (!decoder)
|
||||
return NErr_FailedCreate;
|
||||
|
||||
FLAC__stream_decoder_set_metadata_ignore(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
state.SetFile(file);
|
||||
state.SetObject(this);
|
||||
FLAC__stream_decoder_set_md5_checking(decoder, true);
|
||||
if(FLAC__stream_decoder_init_stream(
|
||||
decoder,
|
||||
FLAC_NXFile_Read,
|
||||
FLAC_NXFile_Seek,
|
||||
FLAC_NXFile_Tell,
|
||||
FLAC_NXFile_Length,
|
||||
FLAC_NXFile_EOF,
|
||||
OnAudio,
|
||||
OnMetadata,
|
||||
OnError,
|
||||
&state
|
||||
) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
decoder=0;
|
||||
return NErr_Error;
|
||||
}
|
||||
|
||||
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
||||
uint64_t position;
|
||||
FLAC__stream_decoder_get_decode_position(decoder, &position);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
decoder=0;
|
||||
NXFileSeek(file, position);
|
||||
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
int RawMediaReader::Read(void *buffer, size_t buffer_size, size_t *bytes_read)
|
||||
{
|
||||
return NXFileRead(file, buffer, buffer_size, bytes_read);
|
||||
}
|
||||
|
||||
size_t RawMediaReader::Release()
|
||||
{
|
||||
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CBCLASS RawMediaReader
|
||||
START_DISPATCH;
|
||||
CB(RELEASE, Release);
|
||||
CB(RAW_READ, Read);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
36
Src/Plugins/Input/in_flac/RawReader.h
Normal file
36
Src/Plugins/Input/in_flac/RawReader.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include "../Agave/DecodeFile/svc_raw_media_reader.h"
|
||||
#include "../Agave/DecodeFile/ifc_raw_media_reader.h"
|
||||
#include "FLACFileCallbacks.h"
|
||||
#include <FLAC/all.h>
|
||||
|
||||
// {E906F4DC-3080-4B9B-951F-85950193ACBF}
|
||||
static const GUID flac_raw_reader_guid =
|
||||
{ 0xe906f4dc, 0x3080, 0x4b9b, { 0x95, 0x1f, 0x85, 0x95, 0x1, 0x93, 0xac, 0xbf } };
|
||||
|
||||
|
||||
class RawMediaReaderService : public svc_raw_media_reader
|
||||
{
|
||||
public:
|
||||
static const char *getServiceName() { return "FLAC Raw Reader"; }
|
||||
static GUID getServiceGuid() { return flac_raw_reader_guid; }
|
||||
int CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **reader);
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
class RawMediaReader : public ifc_raw_media_reader
|
||||
{
|
||||
public:
|
||||
RawMediaReader();
|
||||
~RawMediaReader();
|
||||
int Initialize(nx_file_t );
|
||||
int Read(void *buffer, size_t buffer_size, size_t *bytes_read);
|
||||
size_t Release();
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
private:
|
||||
FLAC__StreamDecoder *decoder;
|
||||
FLACClientData state;
|
||||
nx_file_t file;
|
||||
};
|
65
Src/Plugins/Input/in_flac/Stopper.cpp
Normal file
65
Src/Plugins/Input/in_flac/Stopper.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
** Copyright (C) 2008 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: January 31, 2008
|
||||
**
|
||||
*/
|
||||
#include "Stopper.h"
|
||||
#include "main.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
|
||||
Stopper::Stopper() : isplaying(0), timems(0)
|
||||
{
|
||||
}
|
||||
|
||||
void Stopper::ChangeTracking(bool mode)
|
||||
{
|
||||
SendMessage(plugin.hMainWindow, WM_USER, mode, IPC_ALLOW_PLAYTRACKING); // enable / disable stats updating
|
||||
}
|
||||
|
||||
void Stopper::Stop()
|
||||
{
|
||||
isplaying = SendMessage(plugin.hMainWindow, WM_USER, 0, IPC_ISPLAYING);
|
||||
if (isplaying)
|
||||
{
|
||||
ChangeTracking(0); // disable stats updating
|
||||
timems = SendMessage(plugin.hMainWindow, WM_USER, 0, IPC_GETOUTPUTTIME);
|
||||
SendMessage(plugin.hMainWindow, WM_COMMAND, 40047, 0); // Stop
|
||||
}
|
||||
}
|
||||
|
||||
void Stopper::Play()
|
||||
{
|
||||
if (isplaying) // this works _most_ of the time, not sure why a small portion of the time it doesnt hrmph :/
|
||||
// ideally we should replace it with a system that pauses the decode thread, closes its file,
|
||||
// does the shit, and reopens and reseeks to the new offset. for gaplessness
|
||||
{
|
||||
if (timems)
|
||||
{
|
||||
m_force_seek = timems; // SendMessage(mod.hMainWindow,WM_USER,timems,106);
|
||||
}
|
||||
else
|
||||
m_force_seek = -1;
|
||||
SendMessage(plugin.hMainWindow, WM_COMMAND, 40045, 0); // Play
|
||||
//m_force_seek = -1;
|
||||
if (isplaying & 2)
|
||||
{
|
||||
SendMessage(plugin.hMainWindow, WM_COMMAND, 40046, 0); // Pause
|
||||
}
|
||||
ChangeTracking(1); // enable stats updating
|
||||
}
|
||||
}
|
10
Src/Plugins/Input/in_flac/Stopper.h
Normal file
10
Src/Plugins/Input/in_flac/Stopper.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
class Stopper
|
||||
{
|
||||
public:
|
||||
Stopper();
|
||||
void ChangeTracking(bool);
|
||||
void Stop();
|
||||
void Play();
|
||||
int isplaying, timems;
|
||||
};
|
129
Src/Plugins/Input/in_flac/StreamFileWin32.cpp
Normal file
129
Src/Plugins/Input/in_flac/StreamFileWin32.cpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
** Copyright (C) 2007-2012 Nullsoft, Inc.
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
#include <windows.h>
|
||||
#include <FLAC/all.h>
|
||||
#include <assert.h>
|
||||
#include "StreamFileWin32.h"
|
||||
|
||||
FLAC__StreamDecoderReadStatus Win32_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
|
||||
{
|
||||
assert(*bytes <= 4294967295U);
|
||||
|
||||
HANDLE file = ((Win32_State *)client_data)->handle;
|
||||
if(*bytes > 0)
|
||||
{
|
||||
assert(sizeof(FLAC__byte) == 1);
|
||||
DWORD bytesRead=0, bytesToRead=*bytes;
|
||||
BOOL result = ReadFile(file, buffer, bytesToRead, &bytesRead, NULL);
|
||||
*bytes = bytesRead;
|
||||
|
||||
if (!result)
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
else if(bytesRead == 0)
|
||||
{
|
||||
((Win32_State *)client_data)->endOfFile = true;
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
||||
}
|
||||
else
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
else
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
}
|
||||
|
||||
__int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
|
||||
li.QuadPart = distance;
|
||||
|
||||
li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
|
||||
|
||||
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
|
||||
{
|
||||
li.QuadPart = -1;
|
||||
}
|
||||
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderSeekStatus Win32_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
|
||||
{
|
||||
HANDLE file = ((Win32_State *)client_data)->handle;
|
||||
|
||||
__int64 result = Seek64(file, absolute_byte_offset, FILE_BEGIN);
|
||||
|
||||
if (result == INVALID_SET_FILE_POINTER)
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
else
|
||||
{
|
||||
((Win32_State *)client_data)->endOfFile = false;
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderTellStatus Win32_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||||
{
|
||||
HANDLE file = ((Win32_State *)client_data)->handle;
|
||||
|
||||
__int64 position = Seek64(file, 0, FILE_CURRENT);
|
||||
|
||||
if (position == INVALID_SET_FILE_POINTER)
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
||||
else
|
||||
{
|
||||
*absolute_byte_offset=position;
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
}
|
||||
__int64 FileSize64(HANDLE file)
|
||||
{
|
||||
LARGE_INTEGER position;
|
||||
position.QuadPart=0;
|
||||
position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
|
||||
|
||||
if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
|
||||
return INVALID_FILE_SIZE;
|
||||
else
|
||||
return position.QuadPart;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderLengthStatus Win32_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
|
||||
{
|
||||
HANDLE file = ((Win32_State *)client_data)->handle;
|
||||
|
||||
LARGE_INTEGER position;
|
||||
position.QuadPart=0;
|
||||
position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
|
||||
|
||||
if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
|
||||
else
|
||||
{
|
||||
*stream_length = position.QuadPart;
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
FLAC__bool Win32_EOF(const FLAC__StreamDecoder *decoder, void *client_data)
|
||||
{
|
||||
return ((Win32_State *)client_data)->endOfFile;
|
||||
}
|
23
Src/Plugins/Input/in_flac/StreamFileWin32.h
Normal file
23
Src/Plugins/Input/in_flac/StreamFileWin32.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef NULLSOFT_IN_FLAC_STREAMFILEWIN32_H
|
||||
#define NULLSOFT_IN_FLAC_STREAMFILEWIN32_H
|
||||
|
||||
#include <FLAC/all.h>
|
||||
#include <windows.h>
|
||||
|
||||
struct Win32_State
|
||||
{
|
||||
void *userData;
|
||||
HANDLE handle;
|
||||
bool endOfFile;
|
||||
};
|
||||
|
||||
FLAC__StreamDecoderReadStatus Win32_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
|
||||
FLAC__StreamDecoderSeekStatus Win32_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
|
||||
FLAC__StreamDecoderTellStatus Win32_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
|
||||
FLAC__StreamDecoderLengthStatus Win32_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
|
||||
FLAC__bool Win32_EOF(const FLAC__StreamDecoder *decoder, void *client_data);
|
||||
|
||||
// helper function extern'd here because DecodeThread needs it
|
||||
__int64 FileSize64(HANDLE file);
|
||||
|
||||
#endif
|
11
Src/Plugins/Input/in_flac/api__in_flv.h
Normal file
11
Src/Plugins/Input/in_flac/api__in_flv.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef NULLSOFT_IN_FLAC_API_H
|
||||
#define NULLSOFT_IN_FLAC_API_H
|
||||
|
||||
#include "../Agave/Config/api_config.h"
|
||||
extern api_config *AGAVE_API_CONFIG;
|
||||
|
||||
#include <api/memmgr/api_memmgr.h>
|
||||
extern api_memmgr *memmgr;
|
||||
#define WASABI_API_MEMMGR memmgr
|
||||
|
||||
#endif
|
163
Src/Plugins/Input/in_flac/in_flac.rc
Normal file
163
Src/Plugins/Input/in_flac/in_flac.rc
Normal file
|
@ -0,0 +1,163 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_INFOCHILD_ADVANCED DIALOGEX 0, 0, 341, 164
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "Advanced",IDC_STATIC,0,0,341,164
|
||||
CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,13,164,143
|
||||
LTEXT "Name:",IDC_STATIC,175,13,22,8
|
||||
EDITTEXT IDC_NAME,175,23,159,14,ES_AUTOHSCROLL
|
||||
LTEXT "Value:",IDC_STATIC,175,39,21,8
|
||||
EDITTEXT IDC_VALUE,175,49,159,90,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
|
||||
PUSHBUTTON "Add New",IDC_BUTTON_ADD,176,142,50,13
|
||||
PUSHBUTTON "Delete",IDC_BUTTON_DEL,230,142,50,13
|
||||
PUSHBUTTON "Delete All",IDC_BUTTON_DELALL,284,142,50,13
|
||||
END
|
||||
|
||||
IDD_PREFERENCES DIALOGEX 0, 0, 175, 84
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Nullsoft FLAC Decoder Preferences"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "Extensions",IDC_STATIC,5,4,166,43
|
||||
EDITTEXT IDC_EXTENSIONS,12,16,153,13,ES_AUTOHSCROLL
|
||||
LTEXT "Semi-colon separated (e.g. FLAC;FLA)",IDC_STATIC,12,33,124,8
|
||||
CONTROL "Show average bitrate",IDC_AVERAGE_BITRATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,52,110,10
|
||||
DEFPUSHBUTTON "OK",IDOK,67,67,50,13
|
||||
PUSHBUTTON "Cancel",IDCANCEL,121,67,50,13
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO
|
||||
BEGIN
|
||||
IDD_INFOCHILD_ADVANCED, DIALOG
|
||||
BEGIN
|
||||
RIGHTMARGIN, 340
|
||||
BOTTOMMARGIN, 163
|
||||
END
|
||||
|
||||
IDD_PREFERENCES, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 5
|
||||
RIGHTMARGIN, 171
|
||||
TOPMARGIN, 4
|
||||
BOTTOMMARGIN, 80
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""version.rc2""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_FLAC_DECODER_OLD "Nullsoft FLAC Decoder"
|
||||
IDS_TO_ADVANCED_MODE "to advanced mode >>"
|
||||
IDS_TO_SIMPLE_MODE "<< to simple mode"
|
||||
IDS_NAME "Name"
|
||||
IDS_VALUE "Value"
|
||||
IDS_TRACK_GAIN "Track Gain: %s\n"
|
||||
IDS_ALBUM_GAIN "Album Gain: %s\n"
|
||||
IDS_NOT_PRESENT "not present"
|
||||
IDS_LENGTH_IN_SECONDS "Length: %I64d seconds\n"
|
||||
IDS_CHANNELS "Channels: %d\n"
|
||||
IDS_BITS_PER_SAMPLE "Bits per sample: %d\n"
|
||||
IDS_SAMPLE_RATE "Sample Rate: %d Hz\n"
|
||||
IDS_FILE_SIZE_IN_BYTES "File Size: %I64d bytes\n"
|
||||
IDS_AVERAGE_BITRATE "Average bitrate: %I64d kbps\n"
|
||||
IDS_COMPRESSION_RATIO "Compression Ratio: %u%%\n"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_FLAC_DECODER "Nullsoft FLAC Decoder v%s"
|
||||
65535 "{9475116B-F8C4-4dff-BC19-9601B238557D}"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_LIBFLAC_DLL_MISSING "[libflac.dll missing]"
|
||||
IDS_FLAC_FILES "FLAC Files"
|
||||
IDS_FAMILY_STRING "Free Lossless Audio Codec File"
|
||||
IDS_ABOUT_TEXT "%s\n© 2007-2023 Winamp SA\nWritten by Ben Allison\nBuild date: %hs\n\nUsing FLAC v%hs"
|
||||
IDS_CANNOT_SAVE_METADATA "Cannot save metadata: Error writing data."
|
||||
IDS_ERROR_SAVING_METADATA "Error saving metadata"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
#include "version.rc2"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
105
Src/Plugins/Input/in_flac/in_flac.sln
Normal file
105
Src/Plugins/Input/in_flac/in_flac.sln
Normal file
|
@ -0,0 +1,105 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29613.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_flac", "in_flac.vcxproj", "{2B754A9B-B449-45F2-93B4-49884A7691B6}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9}
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66} = {4CEFBC83-C215-11DB-8314-0800200C9A66}
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\replicant\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\replicant\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\replicant\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_dynamic", "..\libFLAC\libFLAC_dynamic.vcxproj", "{4CEFBC83-C215-11DB-8314-0800200C9A66}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "..\libogg\libogg.vcxproj", "{4FC28B55-2A14-43D5-86F7-201054F338A9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2B754A9B-B449-45F2-93B4-49884A7691B6}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{2B754A9B-B449-45F2-93B4-49884A7691B6}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{2B754A9B-B449-45F2-93B4-49884A7691B6}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{2B754A9B-B449-45F2-93B4-49884A7691B6}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{2B754A9B-B449-45F2-93B4-49884A7691B6}.Release|Win32.Build.0 = Release|Win32
|
||||
{2B754A9B-B449-45F2-93B4-49884A7691B6}.Release|x64.ActiveCfg = Release|Win32
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64
|
||||
{57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64
|
||||
{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|x64.Build.0 = Debug|x64
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|Win32.Build.0 = Release|Win32
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|x64.ActiveCfg = Release|x64
|
||||
{4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|x64.Build.0 = Release|x64
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.Build.0 = Debug|x64
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.Build.0 = Release|Win32
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.ActiveCfg = Release|x64
|
||||
{4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A0F24E75-21BB-4DDF-A287-A481B554B4EB}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
307
Src/Plugins/Input/in_flac/in_flac.vcxproj
Normal file
307
Src/Plugins/Input/in_flac/in_flac.vcxproj
Normal file
|
@ -0,0 +1,307 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2B754A9B-B449-45F2-93B4-49884A7691B6}</ProjectGuid>
|
||||
<RootNamespace>in_flac</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg">
|
||||
<VcpkgEnableManifest>false</VcpkgEnableManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<VcpkgInstalledDir>
|
||||
</VcpkgInstalledDir>
|
||||
<VcpkgUseStatic>false</VcpkgUseStatic>
|
||||
<VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4244;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
|
||||
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4244;4267;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
|
||||
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4995;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;IN_FLAC2_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;REPLICANT_INCLUDE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4244;4267;4995;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>nxlite.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\replicant\nx\nx.vcxproj">
|
||||
<Project>{57c90706-b25d-4aca-9b33-95cdb2427c27}</Project>
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
|
||||
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp" />
|
||||
<ClCompile Include="About.cpp" />
|
||||
<ClCompile Include="AlbumArt.cpp" />
|
||||
<ClCompile Include="DecodeThread.cpp" />
|
||||
<ClCompile Include="ExtendedFileInfo.cpp" />
|
||||
<ClCompile Include="ExtendedRead.cpp" />
|
||||
<ClCompile Include="FileInfo.cpp" />
|
||||
<ClCompile Include="FLACFileCallbacks.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="Metadata.cpp" />
|
||||
<ClCompile Include="mkv_flac_decoder.cpp" />
|
||||
<ClCompile Include="Preferences.cpp" />
|
||||
<ClCompile Include="RawReader.cpp" />
|
||||
<ClCompile Include="Stopper.cpp" />
|
||||
<ClCompile Include="StreamFileWin32.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\Agave\AlbumArt\svc_albumArtProvider.h" />
|
||||
<ClInclude Include="..\..\..\Agave\api\config\api_config.h" />
|
||||
<ClInclude Include="..\..\..\Wasabi\api\memmgr\api_memmgr.h" />
|
||||
<ClInclude Include="..\..\..\Wasabi\api\service\api_service.h" />
|
||||
<ClInclude Include="..\..\..\Winamp\api_language.h" />
|
||||
<ClInclude Include="..\..\..\Winamp\IN2.H" />
|
||||
<ClInclude Include="..\..\..\Winamp\wa_ipc.h" />
|
||||
<ClInclude Include="AlbumArt.h" />
|
||||
<ClInclude Include="api__in_flv.h" />
|
||||
<ClInclude Include="FLACFileCallbacks.h" />
|
||||
<ClInclude Include="main.h" />
|
||||
<ClInclude Include="Metadata.h" />
|
||||
<ClInclude Include="mkv_flac_decoder.h" />
|
||||
<ClInclude Include="QuickBuf.h" />
|
||||
<ClInclude Include="RawReader.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="Stopper.h" />
|
||||
<ClInclude Include="StreamFileWin32.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_flac.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
122
Src/Plugins/Input/in_flac/in_flac.vcxproj.filters
Normal file
122
Src/Plugins/Input/in_flac/in_flac.vcxproj.filters
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="About.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AlbumArt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DecodeThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedFileInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedRead.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLACFileCallbacks.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Metadata.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="mkv_flac_decoder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Preferences.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RawReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Stopper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StreamFileWin32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="StreamFileWin32.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Stopper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RawReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="QuickBuf.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="mkv_flac_decoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Metadata.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLACFileCallbacks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="api__in_flv.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AlbumArt.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Agave\api\config\api_config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Winamp\api_language.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Wasabi\api\memmgr\api_memmgr.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Wasabi\api\service\api_service.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Winamp\IN2.H">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Agave\AlbumArt\svc_albumArtProvider.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Winamp\wa_ipc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{be0e665c-0dc5-41ca-98ab-39750a1644f6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Ressource Files">
|
||||
<UniqueIdentifier>{f00291da-d3eb-4848-bb67-7282ea12bb80}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{f3f6b1c6-d21e-44bd-8b2e-10cdfa6c3e18}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_flac.rc">
|
||||
<Filter>Ressource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
277
Src/Plugins/Input/in_flac/main.cpp
Normal file
277
Src/Plugins/Input/in_flac/main.cpp
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
** Copyright © 2007-2014 Winamp SA
|
||||
**
|
||||
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||||
** liable for any damages arising from the use of this software.
|
||||
**
|
||||
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||||
** alter it and redistribute it freely, subject to the following restrictions:
|
||||
**
|
||||
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
**
|
||||
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
**
|
||||
** 3. This notice may not be removed or altered from any source distribution.
|
||||
**
|
||||
** Author: Ben Allison benski@winamp.com
|
||||
** Created: March 1, 2007
|
||||
**
|
||||
*/
|
||||
#include "main.h"
|
||||
#include "api__in_flv.h"
|
||||
#include "Metadata.h"
|
||||
#include "../Agave/Language/api_language.h"
|
||||
#include <api/service/waServiceFactory.h>
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "../nu/Singleton.h"
|
||||
#include "../nu/Autochar.h"
|
||||
#include <shlwapi.h>
|
||||
#include "resource.h"
|
||||
#include "AlbumArt.h"
|
||||
#include "RawReader.h"
|
||||
#include <strsafe.h>
|
||||
#include "nswasabi/ReferenceCounted.h"
|
||||
#include "mkv_flac_decoder.h"
|
||||
|
||||
api_config *AGAVE_API_CONFIG = 0;
|
||||
api_memmgr *WASABI_API_MEMMGR=0;
|
||||
AlbumArtFactory albumArtFactory;
|
||||
static RawMediaReaderService raw_media_reader_service;
|
||||
static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
|
||||
static MKVDecoder mkv_service;
|
||||
static SingletonServiceFactory<svc_mkvdecoder, MKVDecoder> mkv_factory;
|
||||
// wasabi based services for localisation support
|
||||
api_language *WASABI_API_LNG = 0;
|
||||
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
|
||||
|
||||
HANDLE killswitch=0;
|
||||
HANDLE playThread=0;
|
||||
|
||||
const wchar_t *winampINI=0;
|
||||
void Config(HWND hwndParent);
|
||||
void About(HWND hwndParent);
|
||||
wchar_t pluginName[256] = {0};
|
||||
|
||||
int Init()
|
||||
{
|
||||
if (!IsWindow(plugin.hMainWindow))
|
||||
return IN_INIT_FAILURE;
|
||||
|
||||
killswitch = CreateEvent(0, TRUE, FALSE, 0);
|
||||
plugin.service->service_register(&albumArtFactory);
|
||||
raw_factory.Register(plugin.service, &raw_media_reader_service);
|
||||
mkv_factory.Register(plugin.service, &mkv_service);
|
||||
|
||||
waServiceFactory *sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(AgaveConfigGUID);
|
||||
if (sf) AGAVE_API_CONFIG= (api_config *)sf->getInterface();
|
||||
sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(memMgrApiServiceGuid);
|
||||
if (sf) WASABI_API_MEMMGR= (api_memmgr *)sf->getInterface();
|
||||
|
||||
// loader so that we can get the localisation service api for use
|
||||
sf = plugin.service->service_getServiceByGuid(languageApiGUID);
|
||||
if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
|
||||
|
||||
// need to have this initialised before we try to do anything with localisation features
|
||||
WASABI_API_START_LANG(plugin.hDllInstance,InFlacLangGUID);
|
||||
|
||||
StringCchPrintfW(pluginName,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_FLAC_DECODER),PLUGIN_VER);
|
||||
plugin.description = (char*)pluginName;
|
||||
|
||||
winampINI = (const wchar_t *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILEW);
|
||||
|
||||
wchar_t exts[1024] = {0};
|
||||
GetPrivateProfileStringW(L"in_flac", L"extensions", DEFAULT_EXTENSIONSW, exts, 1024, winampINI);
|
||||
plugin.FileExtensions = BuildExtensions(AutoChar(exts));
|
||||
|
||||
config_average_bitrate = !!GetPrivateProfileIntW(L"in_flac", L"average_bitrate", 1, winampINI);
|
||||
|
||||
plugin.UsesOutputPlug|=8;
|
||||
return IN_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void Quit()
|
||||
{
|
||||
CloseHandle(killswitch);
|
||||
waServiceFactory *sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(AgaveConfigGUID);
|
||||
if (sf) sf->releaseInterface(AGAVE_API_CONFIG);
|
||||
sf = (waServiceFactory *)plugin.service->service_getServiceByGuid(memMgrApiServiceGuid);
|
||||
if (sf) sf->releaseInterface(WASABI_API_MEMMGR);
|
||||
|
||||
plugin.service->service_deregister(&albumArtFactory);
|
||||
raw_factory.Deregister(plugin.service);
|
||||
}
|
||||
|
||||
void GetFileInfo(const in_char *file, in_char *title, int *length_in_ms)
|
||||
{
|
||||
if (length_in_ms)
|
||||
{
|
||||
if (!file || !*file && currentSongLength != -1000)
|
||||
*length_in_ms = currentSongLength;
|
||||
else
|
||||
{
|
||||
FLACMetadata metadata;
|
||||
unsigned __int64 length_in_msec;
|
||||
if (metadata.Open(file) && metadata.GetLengthMilliseconds(&length_in_msec))
|
||||
*length_in_ms = (int)length_in_msec;
|
||||
else
|
||||
*length_in_ms=-1000;
|
||||
}
|
||||
}
|
||||
if (title) *title=0;
|
||||
}
|
||||
|
||||
int InfoBox(const in_char *file, HWND hwndParent) { return 0; }
|
||||
|
||||
int IsOurFile(const in_char *fn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t *lastfn=0;
|
||||
HANDLE threadStarted;
|
||||
extern FLAC__uint64 lastoutputtime;
|
||||
extern volatile int bufferCount;
|
||||
int Play(const in_char *fn)
|
||||
{
|
||||
free(lastfn);
|
||||
lastfn=_wcsdup(fn);
|
||||
currentSongLength=-1000;
|
||||
plugin.is_seekable = 0;
|
||||
plugin.SetInfo(0,0,0,0);
|
||||
lastoutputtime=0;
|
||||
bufferCount=0;
|
||||
ResetEvent(killswitch);
|
||||
DWORD threadId;
|
||||
threadStarted = CreateEvent(0, TRUE, FALSE, 0);
|
||||
|
||||
ReferenceCountedNXString filename_nx;
|
||||
nx_uri_t filename_uri;
|
||||
NXStringCreateWithUTF16(&filename_nx, fn);
|
||||
NXURICreateWithNXString(&filename_uri, filename_nx);
|
||||
|
||||
playThread=CreateThread(0, 0, FLACThread, filename_uri, 0, &threadId);
|
||||
SetThreadPriority(playThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
WaitForSingleObject(threadStarted, INFINITE);
|
||||
CloseHandle(threadStarted);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int localPause=0;
|
||||
void Pause()
|
||||
{
|
||||
localPause=1;
|
||||
QueueUserAPC(APCPause, playThread, (ULONG_PTR)1);
|
||||
}
|
||||
|
||||
void UnPause()
|
||||
{
|
||||
localPause=0;
|
||||
QueueUserAPC(APCPause, playThread, (ULONG_PTR)0);
|
||||
}
|
||||
|
||||
int IsPaused()
|
||||
{
|
||||
return localPause;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
SetEvent(killswitch);
|
||||
WaitForSingleObject(playThread, INFINITE);
|
||||
currentSongLength=-1000;
|
||||
plugin.outMod->Close();
|
||||
plugin.SAVSADeInit();
|
||||
free(lastfn);
|
||||
lastfn=0;
|
||||
}
|
||||
|
||||
int GetLength()
|
||||
{
|
||||
return currentSongLength;
|
||||
}
|
||||
|
||||
|
||||
int GetOutputTime()
|
||||
{
|
||||
if (bufferCount)
|
||||
return bufferCount;
|
||||
|
||||
if (plugin.outMod)
|
||||
{
|
||||
return (int)lastoutputtime + (plugin.outMod->GetOutputTime() - plugin.outMod->GetWrittenTime());
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetOutputTime(int time_in_ms)
|
||||
{
|
||||
lastoutputtime=time_in_ms; // cheating a bit here :)
|
||||
QueueUserAPC(APCSeek, playThread, (ULONG_PTR)time_in_ms);
|
||||
}
|
||||
|
||||
int pan = 0, volume = -666;
|
||||
void SetVolume(int _volume)
|
||||
{
|
||||
volume = _volume;
|
||||
if (plugin.outMod)
|
||||
plugin.outMod->SetVolume(volume);
|
||||
}
|
||||
|
||||
void SetPan(int _pan)
|
||||
{
|
||||
pan = _pan;
|
||||
if (plugin.outMod)
|
||||
plugin.outMod->SetPan(pan);
|
||||
}
|
||||
|
||||
void EQSet(int on, char data[10], int preamp)
|
||||
{}
|
||||
|
||||
In_Module plugin =
|
||||
{
|
||||
IN_VER_RET,
|
||||
"nullsoft(in_flac.dll)",
|
||||
0,
|
||||
0,
|
||||
"FLAC\0FLAC Files\0",
|
||||
1,
|
||||
1,
|
||||
Config,
|
||||
About,
|
||||
Init,
|
||||
Quit,
|
||||
GetFileInfo,
|
||||
InfoBox,
|
||||
IsOurFile,
|
||||
Play,
|
||||
Pause,
|
||||
UnPause,
|
||||
IsPaused,
|
||||
Stop,
|
||||
GetLength,
|
||||
GetOutputTime,
|
||||
SetOutputTime,
|
||||
SetVolume,
|
||||
SetPan,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
EQSet,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) In_Module * winampGetInModule2()
|
||||
{
|
||||
return &plugin;
|
||||
}
|
40
Src/Plugins/Input/in_flac/main.h
Normal file
40
Src/Plugins/Input/in_flac/main.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef NULLSOFT_IN_FLAC_MAIN_H
|
||||
#define NULLSOFT_IN_FLAC_MAIN_H
|
||||
|
||||
#define PLUGIN_VER L"3.2"
|
||||
|
||||
#include <windows.h>
|
||||
extern HANDLE killswitch;
|
||||
|
||||
#include "../Winamp/in2.h"
|
||||
extern In_Module plugin;
|
||||
|
||||
DWORD CALLBACK FLACThread(LPVOID param);
|
||||
extern int pan, volume;
|
||||
extern volatile int currentSongLength;
|
||||
|
||||
void CALLBACK APCPause(ULONG_PTR data);
|
||||
void CALLBACK APCSeek(ULONG_PTR data);
|
||||
|
||||
void ResetMetadataCache();
|
||||
|
||||
#include <FLAC/all.h>
|
||||
void InterleaveAndTruncate(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain=1.0);
|
||||
|
||||
extern const wchar_t *winampINI;
|
||||
char *BuildExtensions(const char *extensions);
|
||||
extern bool config_average_bitrate;
|
||||
extern bool fixBitrate;
|
||||
|
||||
extern int m_force_seek; // set this to something other than -1 to make the file start from the given time (in ms)
|
||||
extern wchar_t *lastfn;
|
||||
|
||||
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
|
||||
static const GUID playbackConfigGroupGUID =
|
||||
{
|
||||
0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }
|
||||
};
|
||||
|
||||
#define DEFAULT_EXTENSIONS "FLAC"
|
||||
#define DEFAULT_EXTENSIONSW L"FLAC"
|
||||
#endif
|
204
Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp
Normal file
204
Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
#include"mkv_flac_decoder.h"
|
||||
#include "main.h"
|
||||
|
||||
static FLAC__StreamDecoderReadStatus Packet_Read(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
|
||||
{
|
||||
packet_client_data_t packet = (packet_client_data_t)client_data;
|
||||
size_t to_copy = *bytes;
|
||||
if (to_copy > packet->buffer_length) {
|
||||
to_copy = packet->buffer_length;
|
||||
}
|
||||
memcpy(buffer, packet->buffer, to_copy);
|
||||
*bytes = to_copy;
|
||||
packet->buffer += to_copy;
|
||||
packet->buffer_length -= to_copy;
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderSeekStatus Packet_Seek(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
|
||||
{
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderTellStatus Packet_Tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||||
{
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderLengthStatus Packet_Length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
|
||||
{
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static FLAC__bool Packet_EOF(const FLAC__StreamDecoder *decoder, void *client_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||
{
|
||||
//client_data=client_data; // dummy line so i can set a breakpoint
|
||||
}
|
||||
|
||||
static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||
{
|
||||
packet_client_data_t packet = (packet_client_data_t)client_data;
|
||||
switch(metadata->type)
|
||||
{
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
{
|
||||
packet->frame_size = metadata->data.stream_info.max_blocksize;
|
||||
packet->bps=metadata->data.stream_info.bits_per_sample;
|
||||
packet->bytes_per_sample = (packet->bps + 7) / 8;
|
||||
packet->channels=metadata->data.stream_info.channels;
|
||||
packet->sample_rate=metadata->data.stream_info.sample_rate;
|
||||
packet->samples=metadata->data.stream_info.total_samples;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
|
||||
{
|
||||
packet_client_data_t packet = (packet_client_data_t)client_data;
|
||||
|
||||
size_t byteLength = packet->bytes_per_sample * packet->channels * frame->header.blocksize;
|
||||
if (byteLength > packet->outputBufferBytes[0]) {
|
||||
FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
}
|
||||
InterleaveAndTruncate(buffer, packet->outputBuffer, packet->bytes_per_sample * 8, packet->channels, frame->header.blocksize);
|
||||
packet->outputBufferBytes[0] = byteLength;
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
MKVFLACDecoder *MKVFLACDecoder::Create(const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels)
|
||||
{
|
||||
FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
|
||||
if (!decoder) {
|
||||
return 0;
|
||||
}
|
||||
packet_client_data_t packet = new packet_client_data_s;
|
||||
packet->buffer = 0;
|
||||
packet->buffer_length = 0;
|
||||
|
||||
if(FLAC__stream_decoder_init_stream(
|
||||
decoder,
|
||||
Packet_Read,
|
||||
Packet_Seek,
|
||||
Packet_Tell,
|
||||
Packet_Length,
|
||||
Packet_EOF,
|
||||
OnAudio,
|
||||
OnMetadata,
|
||||
OnError,
|
||||
packet
|
||||
) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
delete packet;
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
packet->buffer = (const uint8_t *)track_entry_data->codec_private;
|
||||
packet->buffer_length = track_entry_data->codec_private_len;
|
||||
if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) {
|
||||
delete packet;
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MKVFLACDecoder *mkv_decoder = new MKVFLACDecoder(decoder, packet, preferred_bits);
|
||||
if (!mkv_decoder) {
|
||||
delete packet;
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
return 0;
|
||||
}
|
||||
return mkv_decoder;
|
||||
}
|
||||
|
||||
MKVFLACDecoder::MKVFLACDecoder(FLAC__StreamDecoder *decoder, packet_client_data_t packet, unsigned int bps)
|
||||
: decoder(decoder), packet(packet), bps(bps)
|
||||
{
|
||||
}
|
||||
|
||||
MKVFLACDecoder::~MKVFLACDecoder()
|
||||
{
|
||||
delete packet;
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
}
|
||||
|
||||
int MKVFLACDecoder::OutputFrameSize(size_t *frame_size)
|
||||
{
|
||||
*frame_size = packet->frame_size * packet->bytes_per_sample * packet->channels;
|
||||
return MKV_SUCCESS;
|
||||
}
|
||||
|
||||
int MKVFLACDecoder::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat)
|
||||
{
|
||||
*sampleRate = packet->sample_rate;
|
||||
*channels = packet->channels;
|
||||
*bitsPerSample = packet->bps;
|
||||
*isFloat = false;
|
||||
|
||||
return MKV_SUCCESS;
|
||||
}
|
||||
|
||||
int MKVFLACDecoder::DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes)
|
||||
{
|
||||
packet->buffer = (const uint8_t *)inputBuffer;
|
||||
packet->buffer_length = inputBufferBytes;
|
||||
|
||||
packet->outputBuffer = outputBuffer;
|
||||
packet->outputBufferBytes = outputBufferBytes;
|
||||
|
||||
if (FLAC__stream_decoder_process_single(decoder) == 0) {
|
||||
return MKV_FAILURE;
|
||||
}
|
||||
|
||||
return MKV_SUCCESS;
|
||||
}
|
||||
|
||||
void MKVFLACDecoder::Flush()
|
||||
{
|
||||
FLAC__stream_decoder_flush(decoder);
|
||||
}
|
||||
|
||||
void MKVFLACDecoder::Close()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
#define CBCLASS MKVFLACDecoder
|
||||
START_DISPATCH;
|
||||
CB(OUTPUT_FRAME_SIZE, OutputFrameSize)
|
||||
CB(GET_OUTPUT_PROPERTIES, GetOutputProperties)
|
||||
CB(DECODE_BLOCK, DecodeBlock)
|
||||
VCB(FLUSH, Flush)
|
||||
VCB(CLOSE, Close)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
|
||||
int MKVDecoder::CreateAudioDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels,bool floating_point, ifc_mkvaudiodecoder **decoder)
|
||||
{
|
||||
if (!strcmp(codec_id, "A_FLAC"))
|
||||
{
|
||||
MKVFLACDecoder *flac_decoder = MKVFLACDecoder::Create(track_entry_data, audio_data, preferred_bits, max_channels);
|
||||
if (flac_decoder)
|
||||
{
|
||||
*decoder = flac_decoder;
|
||||
return CREATEDECODER_SUCCESS;
|
||||
}
|
||||
return CREATEDECODER_FAILURE;
|
||||
}
|
||||
|
||||
return CREATEDECODER_NOT_MINE;
|
||||
}
|
||||
|
||||
#define CBCLASS MKVDecoder
|
||||
START_DISPATCH;
|
||||
CB(CREATE_AUDIO_DECODER, CreateAudioDecoder)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
65
Src/Plugins/Input/in_flac/mkv_flac_decoder.h
Normal file
65
Src/Plugins/Input/in_flac/mkv_flac_decoder.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
#include "../in_mkv/svc_mkvdecoder.h"
|
||||
#include "../in_mkv/ifc_mkvaudiodecoder.h"
|
||||
#include <FLAC/all.h>
|
||||
|
||||
// {F6AF0AD9-608F-4206-892F-765412574A7D}
|
||||
static const GUID FLACMKVGUID =
|
||||
{ 0xf6af0ad9, 0x608f, 0x4206, { 0x89, 0x2f, 0x76, 0x54, 0x12, 0x57, 0x4a, 0x7d } };
|
||||
|
||||
|
||||
|
||||
class packet_client_data_s
|
||||
{
|
||||
public:
|
||||
const uint8_t *buffer;
|
||||
size_t buffer_length;
|
||||
|
||||
void *outputBuffer;
|
||||
size_t *outputBufferBytes;
|
||||
|
||||
uint32_t frame_size;
|
||||
uint32_t bps;
|
||||
uint32_t bytes_per_sample;
|
||||
uint32_t channels;
|
||||
uint32_t sample_rate;
|
||||
uint64_t samples;
|
||||
};
|
||||
|
||||
typedef packet_client_data_s *packet_client_data_t;
|
||||
|
||||
class MKVDecoder : public svc_mkvdecoder
|
||||
{
|
||||
public:
|
||||
static const char *getServiceName() { return "FLAC MKV Decoder"; }
|
||||
static GUID getServiceGuid() { return FLACMKVGUID; }
|
||||
int CreateAudioDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int preferred_channels, bool floating_point, ifc_mkvaudiodecoder **decoder);
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
class MKVFLACDecoder : public ifc_mkvaudiodecoder
|
||||
{
|
||||
public:
|
||||
static MKVFLACDecoder *Create(const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, unsigned int preferred_bits, unsigned int max_channels);
|
||||
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
private:
|
||||
/* ifc_mkvaudiodecoder implementation */
|
||||
int OutputFrameSize(size_t *frame_size);
|
||||
int GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat);
|
||||
int DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes);
|
||||
void Flush();
|
||||
void Close();
|
||||
|
||||
private:
|
||||
MKVFLACDecoder(FLAC__StreamDecoder *decoder, packet_client_data_t packet, unsigned int bps);
|
||||
~MKVFLACDecoder();
|
||||
/* internal implementation */
|
||||
|
||||
/* data */
|
||||
FLAC__StreamDecoder *decoder;
|
||||
unsigned int bps;
|
||||
packet_client_data_t packet;
|
||||
};
|
71
Src/Plugins/Input/in_flac/resource.h
Normal file
71
Src/Plugins/Input/in_flac/resource.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by in_flac.rc
|
||||
//
|
||||
#define IDS_NULLSOFT_FLAC_DECODER_OLD 0
|
||||
#define IDS_TO_ADVANCED_MODE 1
|
||||
#define IDS_TO_SIMPLE_MODE 2
|
||||
#define IDS_NAME 3
|
||||
#define IDS_VALUE 4
|
||||
#define IDS_TRACK_GAIN 5
|
||||
#define IDS_ALBUM_GAIN 6
|
||||
#define IDS_NOT_PRESENT 7
|
||||
#define IDS_LENGTH_IN_SECONDS 8
|
||||
#define IDS_CHANNELS 9
|
||||
#define IDS_BITS_PER_SAMPLE 10
|
||||
#define IDS_SAMPLE_RATE 11
|
||||
#define IDS_FILE_SIZE_IN_BYTES 12
|
||||
#define IDS_AVERAGE_BITRATE 13
|
||||
#define IDS_COMPRESSION_RATIO 14
|
||||
#define IDS_ABOUT_STR 15
|
||||
#define IDS_LIBFLAC_DLL_MISSING 16
|
||||
#define IDS_FLAC_FILES 17
|
||||
#define IDS_FAMILY_STRING 18
|
||||
#define IDS_ABOUT_TEXT 19
|
||||
#define IDS_CANNOT_SAVE_METADATA 20
|
||||
#define IDS_ERROR_SAVING_METADATA 21
|
||||
#define IDD_PREFERENCES 102
|
||||
#define IDD_INFOCHILD_ADVANCED 105
|
||||
#define IDC_FILENAME 1001
|
||||
#define IDC_TITLE 1002
|
||||
#define IDC_ARTIST 1003
|
||||
#define IDC_YEAR 1004
|
||||
#define IDC_ALBUM 1005
|
||||
#define IDC_GENRE 1006
|
||||
#define IDC_COMMENTS 1007
|
||||
#define IDC_FILEINFO 1008
|
||||
#define IDC_ALBUMARTIST 1009
|
||||
#define IDC_UPDATE 1010
|
||||
#define IDC_COMPOSER 1011
|
||||
#define IDC_EDIT1 1012
|
||||
#define IDC_TRACK 1012
|
||||
#define IDC_EXTENSIONS 1012
|
||||
#define IDC_NAME 1012
|
||||
#define IDC_REPLAYGAIN 1013
|
||||
#define IDC_DISC 1014
|
||||
#define IDC_PUBLISHER 1015
|
||||
#define IDC_PUBLISHER2 1017
|
||||
#define IDC_TOOL 1017
|
||||
#define IDC_CHECK1 1017
|
||||
#define IDC_AVERAGE_BITRATE 1017
|
||||
#define IDC_COMBO1 1018
|
||||
#define IDC_SWITCH 1019
|
||||
#define IDC_PLACEHOLDER 1020
|
||||
#define IDC_LIST 1022
|
||||
#define IDC_VALUE 1024
|
||||
#define IDC_BUTTON_ADD 1025
|
||||
#define IDC_BUTTON_DEL 1026
|
||||
#define IDC_BUTTON5 1028
|
||||
#define IDC_BUTTON_DELALL 1028
|
||||
#define IDS_NULLSOFT_FLAC_DECODER 65534
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 112
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1029
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
39
Src/Plugins/Input/in_flac/version.rc2
Normal file
39
Src/Plugins/Input/in_flac/version.rc2
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
#include "../../../Winamp/buildType.h"
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,2,0,0
|
||||
PRODUCTVERSION WINAMP_PRODUCTVER
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Winamp SA"
|
||||
VALUE "FileDescription", "Winamp Input Plug-in"
|
||||
VALUE "FileVersion", "3,2,0,0"
|
||||
VALUE "InternalName", "Nullsoft FLAC Decoder"
|
||||
VALUE "LegalCopyright", "Copyright © 2007-2023 Winamp SA"
|
||||
VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
|
||||
VALUE "OriginalFilename", "in_flac.dll"
|
||||
VALUE "ProductName", "Winamp"
|
||||
VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
Loading…
Add table
Add a link
Reference in a new issue