Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View 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);
}

View 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

View 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

View 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;
}

View 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;
}

View 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);
}
}

View 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;
}

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

View 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;
}
};

View 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;
}

View 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

View 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);
}

View 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

View 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

View 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;
};

View 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
}
}

View file

@ -0,0 +1,10 @@
#pragma once
class Stopper
{
public:
Stopper();
void ChangeTracking(bool);
void Stop();
void Play();
int isplaying, timems;
};

View 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;
}

View 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

View 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

View 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

View 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

View 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>

View 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>

View 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;
}

View 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

View 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

View 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;
};

View 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

View 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