Initial community commit
@@ -0,0 +1,94 @@
|
||||
#include "AACFrame.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "resource.h"
|
||||
#include "in2.h"
|
||||
extern In_Module mod;
|
||||
|
||||
void AACFrame::ReadBuffer( unsigned __int8 *buffer )
|
||||
{
|
||||
syncword = ( buffer[ 0 ] << 4 ) | ( buffer[ 1 ] >> 4 );
|
||||
id = ( buffer[ 1 ] >> 3 ) & 1;
|
||||
layer = ( buffer[ 1 ] >> 1 ) & 3;
|
||||
protection = ( buffer[ 1 ] ) & 1;
|
||||
profile = ( buffer[ 2 ] >> 6 ) & 3;
|
||||
sampleRateIndex = ( buffer[ 2 ] >> 2 ) & 0xF;
|
||||
privateBit = ( buffer[ 2 ] >> 1 ) & 1;
|
||||
channelConfiguration = ( ( buffer[ 2 ] & 1 ) << 2 ) | ( ( buffer[ 3 ] >> 6 ) & 3 );
|
||||
original = ( buffer[ 3 ] >> 5 ) & 1;
|
||||
home = ( buffer[ 3 ] >> 4 ) & 1;
|
||||
|
||||
//copyright_identification_bit = (buffer[3] >> 3) & 1;
|
||||
//copyright_identification_start = (buffer[3] >> 2) & 1;
|
||||
frameLength = ( ( buffer[ 3 ] & 3 ) << 11 ) | ( buffer[ 4 ] << 3 ) | ( ( buffer[ 5 ] >> 5 ) & 7 );
|
||||
bufferFullness = ( ( buffer[ 5 ] & 0xF8 ) << 5 ) | ( ( buffer[ 6 ] >> 2 ) & 0x3F );
|
||||
numDataBlocks = buffer[ 6 ] & 3;
|
||||
}
|
||||
|
||||
bool AACFrame::OK()
|
||||
{
|
||||
if (syncword == SYNC
|
||||
&& layer == 0
|
||||
&& sampleRateIndex < 13
|
||||
//&& profile != LTP // TODO: can coding technologies decoder do LTP?
|
||||
)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static const unsigned int aac_sratetab[] =
|
||||
{
|
||||
96000,
|
||||
88200,
|
||||
64000,
|
||||
48000,
|
||||
44100,
|
||||
32000,
|
||||
24000,
|
||||
22050,
|
||||
16000,
|
||||
12000,
|
||||
11025,
|
||||
8000,
|
||||
7350,
|
||||
};
|
||||
|
||||
int AACFrame::GetSampleRate()
|
||||
{
|
||||
return aac_sratetab[sampleRateIndex];
|
||||
}
|
||||
|
||||
static const wchar_t *aac_profiletab[] = {L"Main", L"LC", L"SSR", L"LTP"};
|
||||
|
||||
const wchar_t *AACFrame::GetProfileName()
|
||||
{
|
||||
return aac_profiletab[profile];
|
||||
}
|
||||
|
||||
//static const char *aac_channels[] = {"Custom", "Mono", "Stereo", "3 channel", "4 channel", "surround", "5.1", "7.1"};
|
||||
static wchar_t aac_channels_str[64];
|
||||
static int aac_channels_id[] = {IDS_CUSTOM, IDS_MONO, IDS_STEREO, IDS_3_CHANNEL, IDS_4_CHANNEL, IDS_SURROUND, IDS_5_1, IDS_7_1};
|
||||
const wchar_t *AACFrame::GetChannelConfigurationName()
|
||||
{
|
||||
return WASABI_API_LNGSTRINGW_BUF(aac_channels_id[channelConfiguration],aac_channels_str,64);
|
||||
}
|
||||
|
||||
int AACFrame::GetNumChannels()
|
||||
{
|
||||
switch(channelConfiguration)
|
||||
{
|
||||
case 7:
|
||||
return 8;
|
||||
default:
|
||||
return channelConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
int AACFrame::GetMPEGVersion()
|
||||
{
|
||||
if (id == 0)
|
||||
return 2;
|
||||
else
|
||||
return 4;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef NULLSOFT_AACFRAME_H
|
||||
#define NULLSOFT_AACFRAME_H
|
||||
|
||||
class AACFrame
|
||||
{
|
||||
public:
|
||||
void ReadBuffer(unsigned __int8 *buffer);
|
||||
bool OK();
|
||||
|
||||
enum
|
||||
{
|
||||
NOT_PROTECTED=1,
|
||||
PROTECTED=0,
|
||||
SYNC = 0xFFF,
|
||||
MAIN = 0x00,
|
||||
LC = 0x01,
|
||||
SSR = 0x10,
|
||||
LTP = 0x11,
|
||||
};
|
||||
int GetNumChannels();
|
||||
int GetSampleRate();
|
||||
const wchar_t *GetProfileName();
|
||||
const wchar_t *GetChannelConfigurationName();
|
||||
int GetMPEGVersion(); // returns 2 or 4
|
||||
public:
|
||||
int syncword;
|
||||
int layer;
|
||||
int id;
|
||||
int protection;
|
||||
int profile;
|
||||
int sampleRateIndex;
|
||||
int privateBit;
|
||||
int channelConfiguration;
|
||||
int original;
|
||||
int home;
|
||||
int frameLength;
|
||||
int bufferFullness;
|
||||
int numDataBlocks;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
#include "main.h"
|
||||
#include "Metadata.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "AlbumArt.h"
|
||||
#include "Stopper.h"
|
||||
#include <shlwapi.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
bool IsMyExtension(const wchar_t *filename)
|
||||
{
|
||||
// check if it's the current stream and is playing and is SHOUTcast2
|
||||
if (PathIsURLW(filename))
|
||||
{
|
||||
if (g_playing_file)
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file &&
|
||||
(g_playing_file->uvox_artwork.uvox_stream_artwork || g_playing_file->uvox_artwork.uvox_playing_artwork) &&
|
||||
!lstrcmpW(lastfn, filename)) // check again now that we've acquired the lock
|
||||
{
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return true;
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
}
|
||||
}
|
||||
// otherwise handle as normal embedded
|
||||
else
|
||||
{
|
||||
const wchar_t *extension = PathFindExtension(filename);
|
||||
if (extension && *extension)
|
||||
{
|
||||
AutoWide wideList(config_extlist); // TODO: build a copy of this at config load time so we don't have to run this every time
|
||||
extension++;
|
||||
wchar_t *b = wideList;
|
||||
|
||||
wchar_t *c = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
bool ID3v2_AlbumArtProvider::IsMine(const wchar_t *filename)
|
||||
{
|
||||
return IsMyExtension(filename);
|
||||
}
|
||||
|
||||
int ID3v2_AlbumArtProvider::ProviderType()
|
||||
{
|
||||
return ALBUMARTPROVIDER_TYPE_EMBEDDED;
|
||||
}
|
||||
|
||||
int ID3v2_AlbumArtProvider::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
|
||||
{
|
||||
if (g_playing_file)
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file && !lstrcmpW(lastfn, filename)) // check again now that we've acquired the lock
|
||||
{
|
||||
wchar_t* mimeType[] = {
|
||||
L"image/jpeg",
|
||||
L"image/png",
|
||||
L"image/bmp",
|
||||
L"image/gif"
|
||||
};
|
||||
if (!g_playing_file->uvox_artwork.uvox_stream_artwork)
|
||||
{
|
||||
int ret = g_playing_file->info.GetAlbumArt(type, bits, len, mimeType);
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
// will handle "playing" and "cover" - cover is the stream branding
|
||||
// with "playing" used to provide song specific stream artwork
|
||||
if (!_wcsicmp(type, L"playing"))
|
||||
{
|
||||
if (g_playing_file->uvox_artwork.uvox_playing_artwork_len > 0)
|
||||
{
|
||||
*len = g_playing_file->uvox_artwork.uvox_playing_artwork_len;
|
||||
int type = g_playing_file->uvox_artwork.uvox_playing_artwork_type;
|
||||
if(type >= 0 && type <= 3)
|
||||
{
|
||||
*mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(12 * sizeof(wchar_t));
|
||||
wcsncpy(*mimeType, mimeType[type], 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
*mimeType = 0;
|
||||
}
|
||||
|
||||
*bits = WASABI_API_MEMMGR->sysMalloc(*len);
|
||||
memcpy(*bits, g_playing_file->uvox_artwork.uvox_playing_artwork, *len);
|
||||
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_playing_file->uvox_artwork.uvox_stream_artwork_len > 0)
|
||||
{
|
||||
*len = g_playing_file->uvox_artwork.uvox_stream_artwork_len;
|
||||
|
||||
int type = g_playing_file->uvox_artwork.uvox_stream_artwork_type;
|
||||
if(type >= 0 && type <= 3)
|
||||
{
|
||||
*mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(12 * sizeof(wchar_t));
|
||||
wcsncpy(*mimeType, mimeType[type], 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
*mimeType = 0;
|
||||
}
|
||||
|
||||
*bits = WASABI_API_MEMMGR->sysMalloc(*len);
|
||||
memcpy(*bits, g_playing_file->uvox_artwork.uvox_stream_artwork, *len);
|
||||
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
}
|
||||
|
||||
Metadata metadata;
|
||||
if (metadata.Open(filename) == METADATA_SUCCESS)
|
||||
{
|
||||
return metadata.id3v2.GetAlbumArt(type, bits, len, mimeType);
|
||||
}
|
||||
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
extern Metadata *m_ext_get_mp3info;
|
||||
|
||||
int ID3v2_AlbumArtProvider::SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
|
||||
{
|
||||
Metadata metadata;
|
||||
if (metadata.Open(filename) == METADATA_SUCCESS)
|
||||
{
|
||||
int ret = metadata.id3v2.SetAlbumArt(type, bits, len, mimeType);
|
||||
if (ret == METADATA_SUCCESS)
|
||||
{
|
||||
// flush our read cache too :)
|
||||
if (m_ext_get_mp3info) m_ext_get_mp3info->Release();
|
||||
m_ext_get_mp3info = NULL;
|
||||
|
||||
Stopper stopper;
|
||||
if (metadata.IsMe(lastfn))
|
||||
stopper.Stop();
|
||||
metadata.Save();
|
||||
stopper.Play();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int ID3v2_AlbumArtProvider::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
|
||||
{
|
||||
Metadata metadata;
|
||||
if (metadata.Open(filename) == METADATA_SUCCESS)
|
||||
{
|
||||
int ret = metadata.id3v2.DeleteAlbumArt(type);
|
||||
if (ret == METADATA_SUCCESS)
|
||||
{
|
||||
// flush our read cache too :)
|
||||
if (m_ext_get_mp3info) m_ext_get_mp3info->Release();
|
||||
m_ext_get_mp3info = NULL;
|
||||
|
||||
Stopper stopper;
|
||||
if (metadata.IsMe(lastfn))
|
||||
stopper.Stop();
|
||||
metadata.Save();
|
||||
stopper.Play();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
#define CBCLASS ID3v2_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 ID3v2_AlbumArtProvider albumArtProvider;
|
||||
|
||||
// {C8222317-8F0D-4e79-9222-447381C46E07}
|
||||
static const GUID id3v2_albumartproviderGUID =
|
||||
{ 0xc8222317, 0x8f0d, 0x4e79, { 0x92, 0x22, 0x44, 0x73, 0x81, 0xc4, 0x6e, 0x7 } };
|
||||
|
||||
FOURCC AlbumArtFactory::GetServiceType()
|
||||
{
|
||||
return svc_albumArtProvider::SERVICETYPE;
|
||||
}
|
||||
|
||||
const char *AlbumArtFactory::GetServiceName()
|
||||
{
|
||||
return "ID3v2 Album Art Provider";
|
||||
}
|
||||
|
||||
GUID AlbumArtFactory::GetGUID()
|
||||
{
|
||||
return id3v2_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;
|
||||
}
|
||||
|
||||
#ifdef CBCLASS
|
||||
#undef CBCLASS
|
||||
#endif
|
||||
|
||||
#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;
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ALBUMART_H
|
||||
#define NULLSOFT_IN_MP3_ALBUMART_H
|
||||
|
||||
#include "../Agave/AlbumArt/svc_albumArtProvider.h"
|
||||
|
||||
class ID3v2_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>
|
||||
#include <api/service/services.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
|
||||
@@ -0,0 +1,327 @@
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// (C) copyright Fraunhofer - IIS (2000)
|
||||
// All Rights Reserved
|
||||
//
|
||||
// filename: CVbriHeader.cpp
|
||||
// MPEG Layer-3 Audio Decoder
|
||||
// author : Martin Weishart martin.weishart@iis.fhg.de
|
||||
// date : 2000-02-11
|
||||
// contents/description: provides functions to read a VBRI header
|
||||
// of a MPEG Layer 3 bitstream encoded
|
||||
// with variable bitrate using Fraunhofer
|
||||
// variable bitrate format
|
||||
//
|
||||
//--------------------------------------------------------------------------/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "CVbriHeader.h"
|
||||
#include "LAMEInfo.h"
|
||||
#include <malloc.h>
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Constructor: set position in buffer to parse and create a
|
||||
// VbriHeaderTable
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
CVbriHeader::CVbriHeader(){
|
||||
position = 0;
|
||||
VbriTable=0;
|
||||
VbriStreamFrames=0;
|
||||
encoderDelay=0;
|
||||
h_id=0;
|
||||
SampleRate=0;
|
||||
VbriTableSize=0;
|
||||
VbriEntryFrames=0;
|
||||
VbriStreamBytes=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Destructor: delete a VbriHeaderTable and a VbriHeader
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
CVbriHeader::~CVbriHeader(){
|
||||
free(VbriTable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Method: checkheader
|
||||
// Reads the header to a struct that has to be stored and is
|
||||
// used in other functions to determine file offsets
|
||||
// Input: buffer containing the first frame
|
||||
// Output: fills struct VbriHeader
|
||||
// Return: 0 on success; 1 on error
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
int CVbriHeader::readVbriHeader(unsigned char *Hbuffer)
|
||||
{
|
||||
position=0;
|
||||
// MPEG header
|
||||
MPEGFrame frame;
|
||||
frame.ReadBuffer(Hbuffer);
|
||||
if (!frame.IsSync())
|
||||
return 0;
|
||||
|
||||
SampleRate = frame.GetSampleRate();
|
||||
h_id = frame.mpegVersion & 1;
|
||||
|
||||
position += DWORD ;
|
||||
|
||||
// data indicating silence
|
||||
position += (8*DWORD) ;
|
||||
|
||||
// if a VBRI Header exists read it
|
||||
|
||||
if ( *(Hbuffer+position ) == 'V' &&
|
||||
*(Hbuffer+position+1) == 'B' &&
|
||||
*(Hbuffer+position+2) == 'R' &&
|
||||
*(Hbuffer+position+3) == 'I'){
|
||||
|
||||
position += DWORD;
|
||||
|
||||
//position += WORD;
|
||||
/*unsigned int vbriVersion = */readFromBuffer(Hbuffer, WORD); // version
|
||||
|
||||
encoderDelay = readFromBuffer(Hbuffer, WORD); // delay
|
||||
|
||||
position += WORD;
|
||||
//readFromBuffer(Hbuffer, WORD); // quality
|
||||
|
||||
VbriStreamBytes = readFromBuffer(Hbuffer, DWORD);
|
||||
VbriStreamFrames = readFromBuffer(Hbuffer, DWORD);
|
||||
VbriTableSize = readFromBuffer(Hbuffer, WORD);
|
||||
unsigned int VbriTableScale = readFromBuffer(Hbuffer, WORD);
|
||||
unsigned int VbriEntryBytes = readFromBuffer(Hbuffer, WORD);
|
||||
VbriEntryFrames = readFromBuffer(Hbuffer, WORD);
|
||||
|
||||
if (VbriTableSize > 32768) return 1;
|
||||
|
||||
VbriTable = (int *)calloc((VbriTableSize + 1), sizeof(int));
|
||||
|
||||
for (unsigned int i = 0 ; i <= VbriTableSize ; i++){
|
||||
VbriTable[i] = readFromBuffer(Hbuffer, VbriEntryBytes*BYTE)
|
||||
* VbriTableScale ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return frame.FrameSize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Method: seekPointByTime
|
||||
// Returns a point in the file to decode in bytes that is nearest
|
||||
// to a given time in seconds
|
||||
// Input: time in seconds
|
||||
// Output: None
|
||||
// Returns: point belonging to the given time value in bytes
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
int CVbriHeader::seekPointByTime(float EntryTimeInMilliSeconds){
|
||||
|
||||
unsigned int SamplesPerFrame, i=0, SeekPoint = 0 , fraction = 0;
|
||||
|
||||
float TotalDuration ;
|
||||
float DurationPerVbriFrames ;
|
||||
float AccumulatedTime = 0.0f ;
|
||||
|
||||
(SampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576) ;
|
||||
|
||||
TotalDuration = ((float)VbriStreamFrames * (float)SamplesPerFrame)
|
||||
/ (float)SampleRate * 1000.0f ;
|
||||
DurationPerVbriFrames = (float)TotalDuration / (float)(VbriTableSize+1) ;
|
||||
|
||||
if ( EntryTimeInMilliSeconds > TotalDuration ) EntryTimeInMilliSeconds = TotalDuration;
|
||||
|
||||
while ( AccumulatedTime <= EntryTimeInMilliSeconds ){
|
||||
|
||||
SeekPoint += VbriTable[i] ;
|
||||
AccumulatedTime += DurationPerVbriFrames;
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
// Searched too far; correct result
|
||||
fraction = ( (int)(((( AccumulatedTime - EntryTimeInMilliSeconds ) / DurationPerVbriFrames )
|
||||
+ (1.0f/(2.0f*(float)VbriEntryFrames))) * (float)VbriEntryFrames));
|
||||
|
||||
|
||||
SeekPoint -= (int)((float)VbriTable[i-1] * (float)(fraction)
|
||||
/ (float)VbriEntryFrames) ;
|
||||
|
||||
return SeekPoint ;
|
||||
|
||||
}
|
||||
|
||||
int CVbriHeader::getNumMS()
|
||||
{
|
||||
if (!VbriStreamFrames || !SampleRate) return 0;
|
||||
|
||||
int nf=VbriStreamFrames;
|
||||
int sr=SampleRate;
|
||||
if (sr >= 32000) sr/=2;
|
||||
//576
|
||||
return MulDiv(nf,576*1000,sr);
|
||||
}
|
||||
|
||||
#if 0
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Method: seekTimeByPoint
|
||||
// Returns a time in the file to decode in seconds that is
|
||||
// nearest to a given point in bytes
|
||||
// Input: time in seconds
|
||||
// Output: None
|
||||
// Returns: point belonging to the given time value in bytes
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
float CVbriHeader::seekTimeByPoint(unsigned int EntryPointInBytes){
|
||||
|
||||
unsigned int SamplesPerFrame, i=0, AccumulatedBytes = 0, fraction = 0;
|
||||
|
||||
float SeekTime = 0.0f;
|
||||
float TotalDuration ;
|
||||
float DurationPerVbriFrames ;
|
||||
|
||||
(SampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576) ;
|
||||
|
||||
TotalDuration = ((float)VbriStreamFrames * (float)SamplesPerFrame)
|
||||
/ (float)SampleRate;
|
||||
DurationPerVbriFrames = (float)TotalDuration / (float)(VbriTableSize+1) ;
|
||||
|
||||
while (AccumulatedBytes <= EntryPointInBytes){
|
||||
|
||||
AccumulatedBytes += VbriTable[i] ;
|
||||
SeekTime += DurationPerVbriFrames;
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
// Searched too far; correct result
|
||||
fraction = (int)(((( AccumulatedBytes - EntryPointInBytes ) / (float)VbriTable[i-1])
|
||||
+ (1/(2*(float)VbriEntryFrames))) * (float)VbriEntryFrames);
|
||||
|
||||
SeekTime -= (DurationPerVbriFrames * (float) ((float)(fraction) / (float)VbriEntryFrames)) ;
|
||||
|
||||
return SeekTime ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Method: seekPointByPercent
|
||||
// Returns a point in the file to decode in bytes that is
|
||||
// nearest to a given percentage of the time of the stream
|
||||
// Input: percent of time
|
||||
// Output: None
|
||||
// Returns: point belonging to the given time percentage value in bytes
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
int CVbriHeader::seekPointByPercent(float percent){
|
||||
|
||||
int SamplesPerFrame;
|
||||
|
||||
float TotalDuration ;
|
||||
|
||||
if (percent >= 100.0f) percent = 100.0f;
|
||||
if (percent <= 0.0f) percent = 0.0f;
|
||||
|
||||
(SampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576) ;
|
||||
|
||||
TotalDuration = ((float)VbriStreamFrames * (float)SamplesPerFrame)
|
||||
/ (float)SampleRate;
|
||||
|
||||
return seekPointByTime( (percent/100.0f) * TotalDuration * 1000.0f );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Method: GetSampleRate
|
||||
// Returns the sampling rate of the file to decode
|
||||
// Input: Buffer containing the part of the first frame after the
|
||||
// syncword
|
||||
// Output: None
|
||||
// Return: sampling rate of the file to decode
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
/*int CVbriHeader::getSampleRate(unsigned char * buffer){
|
||||
|
||||
unsigned char id, idx, mpeg ;
|
||||
|
||||
id = (0xC0 & (buffer[1] << 3)) >> 4;
|
||||
idx = (0xC0 & (buffer[2] << 4)) >> 6;
|
||||
|
||||
mpeg = id | idx;
|
||||
|
||||
switch ((int)mpeg){
|
||||
|
||||
case 0 : return 11025;
|
||||
case 1 : return 12000;
|
||||
case 2 : return 8000;
|
||||
case 8 : return 22050;
|
||||
case 9 : return 24000;
|
||||
case 10: return 16000;
|
||||
case 12: return 44100;
|
||||
case 13: return 48000;
|
||||
case 14: return 32000;
|
||||
default: return 0;
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------\
|
||||
//
|
||||
// Method: readFromBuffer
|
||||
// reads from a buffer a segment to an int value
|
||||
// Input: Buffer containig the first frame
|
||||
// Output: none
|
||||
// Return: number containing int value of buffer segmenet
|
||||
// length
|
||||
//
|
||||
//---------------------------------------------------------------------------/
|
||||
|
||||
int CVbriHeader::readFromBuffer ( unsigned char * HBuffer, int length ){
|
||||
|
||||
if (HBuffer)
|
||||
{
|
||||
int number = 0;
|
||||
for(int i = 0; i < length ; i++ )
|
||||
{
|
||||
int b = length-1-i ;
|
||||
number = number | (unsigned int)( HBuffer[position+i] & 0xff ) << ( 8*b );
|
||||
}
|
||||
position += length ;
|
||||
return number;
|
||||
}
|
||||
else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef _VBRIHEADER_H_
|
||||
#define _VBRIHEADER_H_
|
||||
|
||||
class CVbriHeader{
|
||||
|
||||
public:
|
||||
|
||||
CVbriHeader();
|
||||
~CVbriHeader();
|
||||
|
||||
int readVbriHeader(unsigned char *Hbuffer);
|
||||
|
||||
int seekPointByTime(float EntryTimeInSeconds);
|
||||
#if 0
|
||||
float seekTimeByPoint(unsigned int EntryPointInBytes);
|
||||
int seekPointByPercent(float percent);
|
||||
#endif
|
||||
|
||||
int getNumFrames() { return VbriStreamFrames; }
|
||||
int getNumMS();
|
||||
int getEncoderDelay() { return encoderDelay; }
|
||||
int getBytes() { return VbriStreamBytes; }
|
||||
int h_id;
|
||||
private:
|
||||
|
||||
int getSampleRate(unsigned char * buffer);
|
||||
int readFromBuffer ( unsigned char * HBuffer, int length );
|
||||
|
||||
int SampleRate;
|
||||
unsigned int VbriStreamBytes;
|
||||
unsigned int VbriStreamFrames;
|
||||
unsigned int VbriTableSize;
|
||||
unsigned int VbriEntryFrames;
|
||||
int * VbriTable;
|
||||
int encoderDelay;
|
||||
|
||||
int position ;
|
||||
|
||||
enum offset{
|
||||
|
||||
BYTE = 1,
|
||||
WORD = 2,
|
||||
DWORD = 4
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif//_VBRIHEADER_H_
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef NULLSOFT_CREATEFILEH
|
||||
#define NULLSOFT_CREATEFILEH
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
/*---- DXhead.c --------------------------------------------
|
||||
|
||||
|
||||
decoder MPEG Layer III
|
||||
|
||||
handle Xing header
|
||||
|
||||
mod 12/7/98 add vbr scale
|
||||
|
||||
Copyright 1998 Xing Technology Corp.
|
||||
-----------------------------------------------------------*/
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include "dxhead.h"
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
int SeekPoint(unsigned char TOC[100], int file_bytes, float percent)
|
||||
{
|
||||
// interpolate in TOC to get file seek point in bytes
|
||||
int a, seekpoint;
|
||||
float fa, fb, fx;
|
||||
|
||||
|
||||
if (percent < 0.0f)
|
||||
percent = 0.0f;
|
||||
if (percent > 100.0f)
|
||||
percent = 100.0f;
|
||||
|
||||
a = (int)percent;
|
||||
if (a > 99) a = 99;
|
||||
fa = TOC[a];
|
||||
if (a < 99)
|
||||
{
|
||||
fb = TOC[a + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
fb = 256.0f;
|
||||
}
|
||||
|
||||
|
||||
fx = fa + (fb - fa) * (percent - a);
|
||||
|
||||
seekpoint = (int) ((1.0f / 256.0f) * fx * file_bytes);
|
||||
|
||||
|
||||
return seekpoint;
|
||||
}
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -0,0 +1,43 @@
|
||||
/*---- DXhead.h --------------------------------------------
|
||||
|
||||
|
||||
decoder MPEG Layer III
|
||||
|
||||
handle Xing header
|
||||
|
||||
|
||||
Copyright 1998 Xing Technology Corp.
|
||||
-----------------------------------------------------------*/
|
||||
// A Xing header may be present in the ancillary
|
||||
// data field of the first frame of an mp3 bitstream
|
||||
// The Xing header (optionally) contains
|
||||
// frames total number of audio frames in the bitstream
|
||||
// bytes total number of bytes in the bitstream
|
||||
// toc table of contents
|
||||
|
||||
// toc (table of contents) gives seek points
|
||||
// for random access
|
||||
// the ith entry determines the seek point for
|
||||
// i-percent duration
|
||||
// seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes
|
||||
// e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes
|
||||
|
||||
|
||||
#define FRAMES_FLAG 0x0001
|
||||
#define BYTES_FLAG 0x0002
|
||||
#define TOC_FLAG 0x0004
|
||||
#define VBR_SCALE_FLAG 0x0008
|
||||
|
||||
#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
|
||||
|
||||
|
||||
int SeekPoint(unsigned char TOC[100], int file_bytes, float percent);
|
||||
// return seekpoint in bytes (may be at eof if percent=100.0)
|
||||
// TOC = table of contents from Xing header
|
||||
// file_bytes = number of bytes in mp3 file
|
||||
// percent = play time percentage of total playtime. May be
|
||||
// fractional (e.g. 87.245)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,810 @@
|
||||
#include "DecodeThread.h"
|
||||
#include "giofile.h"
|
||||
#include "main.h"
|
||||
#include "pdtimer.h"
|
||||
#include "mpegutil.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "config.h"
|
||||
#include <shlwapi.h>
|
||||
#include "adts.h"
|
||||
#include "adts_vlb.h"
|
||||
#include <foundation/error.h>
|
||||
|
||||
// {19450308-90D7-4E45-8A9D-DC71E67123E2}
|
||||
static const GUID adts_aac_guid =
|
||||
{ 0x19450308, 0x90d7, 0x4e45, { 0x8a, 0x9d, 0xdc, 0x71, 0xe6, 0x71, 0x23, 0xe2 } };
|
||||
|
||||
// {4192FE3F-E843-445c-8D62-51BE5EE5E68C}
|
||||
static const GUID adts_mp2_guid =
|
||||
{ 0x4192fe3f, 0xe843, 0x445c, { 0x8d, 0x62, 0x51, 0xbe, 0x5e, 0xe5, 0xe6, 0x8c } };
|
||||
|
||||
extern int m_is_stream;
|
||||
extern bool m_is_stream_seekable;
|
||||
|
||||
// post this to the main window at end of file (after playback as stopped)
|
||||
#define WM_WA_MPEG_EOF WM_USER+2
|
||||
|
||||
/* public data */
|
||||
int last_decode_pos_ms;
|
||||
int decode_pos_ms; // current decoding position, in milliseconds.
|
||||
volatile int seek_needed; // if != -1, it is the point that the decode
|
||||
// thread should seek to, in ms.
|
||||
int g_ds;
|
||||
|
||||
size_t g_bits;
|
||||
int g_sndopened;
|
||||
int g_bufferstat;
|
||||
int g_length = -1000;
|
||||
int g_vis_enabled;
|
||||
volatile int g_closeaudio = 0;
|
||||
|
||||
CGioFile *g_playing_file=0;
|
||||
/* private data */
|
||||
static size_t g_samplebuf_used;
|
||||
static int need_prebuffer;
|
||||
static int g_srate, g_nch, g_br_add, g_br_div, g_avg_vbr_br;
|
||||
int g_br;
|
||||
|
||||
class EndCutter
|
||||
{
|
||||
public:
|
||||
EndCutter() : buffer(0), cutSize(0), filledSize(0), preCutSize(0), preCut(0), decoderDelay(0)
|
||||
{}
|
||||
~EndCutter()
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
void SetEndSize(int postSize)
|
||||
{
|
||||
postSize -= decoderDelay;
|
||||
if (postSize < 0)
|
||||
postSize = 0;
|
||||
else if (postSize)
|
||||
{
|
||||
free(buffer);
|
||||
buffer = (char *)calloc(postSize, sizeof(char));
|
||||
cutSize = postSize;
|
||||
}
|
||||
}
|
||||
|
||||
void SetSize(int decoderDelaySize, int preSize, int postSize)
|
||||
{
|
||||
decoderDelay = decoderDelaySize;
|
||||
SetEndSize(postSize);
|
||||
|
||||
preCutSize = preSize;
|
||||
preCut = preCutSize + decoderDelay;
|
||||
}
|
||||
|
||||
void Flush(int time_in_ms)
|
||||
{
|
||||
if (time_in_ms == 0) // TODO: calculate actual delay if we seek within the encoder delay area
|
||||
preCut = preCutSize; // reset precut size if we seek to the start
|
||||
|
||||
filledSize = 0;
|
||||
mod.outMod->Flush(time_in_ms);
|
||||
}
|
||||
|
||||
void Write(char *out, int outSize)
|
||||
{
|
||||
if (!out && (!outSize))
|
||||
{
|
||||
mod.outMod->Write(0, 0);
|
||||
return ;
|
||||
}
|
||||
|
||||
// cut pre samples, if necessary
|
||||
int pre = min(preCut, outSize);
|
||||
out += pre;
|
||||
outSize -= pre;
|
||||
preCut -= pre;
|
||||
|
||||
if (!outSize)
|
||||
return ;
|
||||
|
||||
int remainingFill = cutSize - filledSize;
|
||||
int fillWrite = min(outSize - remainingFill, filledSize); // only write fill buffer if we've got enough left to fill it up
|
||||
|
||||
if (fillWrite > 0)
|
||||
{
|
||||
mod.outMod->Write((char *)buffer, fillWrite);
|
||||
if (cutSize - fillWrite)
|
||||
memmove(buffer, buffer + fillWrite, cutSize - fillWrite);
|
||||
filledSize -= fillWrite;
|
||||
|
||||
}
|
||||
remainingFill = cutSize - filledSize;
|
||||
int outWrite = max(0, outSize - remainingFill);
|
||||
if (outWrite)
|
||||
mod.outMod->Write((char *)out, outWrite);
|
||||
out += outWrite;
|
||||
outSize -= outWrite;
|
||||
|
||||
if (outSize)
|
||||
{
|
||||
memcpy(buffer + filledSize, out, outSize);
|
||||
filledSize += outSize;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
char *buffer;
|
||||
int cutSize;
|
||||
int filledSize;
|
||||
int preCut, preCutSize, decoderDelay;
|
||||
};
|
||||
|
||||
class DecodeLoop
|
||||
{
|
||||
public:
|
||||
DecodeLoop() : decoder(0)
|
||||
{
|
||||
isAac = 0;
|
||||
isEAAC = 0;
|
||||
|
||||
last_bpos = -1;
|
||||
need_synclight = true;
|
||||
done = 0;
|
||||
br = 0;
|
||||
|
||||
g_framesize = 0;
|
||||
maxlatency = 0;
|
||||
sampleFrameSize = 0;
|
||||
memset(&g_samplebuf, 0, sizeof(g_samplebuf));
|
||||
}
|
||||
|
||||
~DecodeLoop()
|
||||
{
|
||||
if (decoder)
|
||||
{
|
||||
decoder->Close();
|
||||
decoder->Release();
|
||||
}
|
||||
decoder=0;
|
||||
|
||||
}
|
||||
|
||||
DWORD Loop();
|
||||
DWORD OpenDecoder();
|
||||
void Seek(int seekPosition);
|
||||
void PreBuffer();
|
||||
void Decode();
|
||||
void Viz();
|
||||
void CalculateCodecDelay();
|
||||
DWORD OpenOutput(int numChannels, int sampleRate, int bitsPerSample);
|
||||
void SetupStream();
|
||||
|
||||
BYTE g_samplebuf[6*3*2*2*1152];
|
||||
|
||||
int g_framesize;
|
||||
int isAac;
|
||||
int isEAAC;
|
||||
|
||||
CGioFile file;
|
||||
|
||||
int maxlatency;
|
||||
int last_bpos;
|
||||
bool need_synclight;
|
||||
int done; // set to TRUE if decoding has finished, 2 if all has been written
|
||||
size_t br;
|
||||
|
||||
EndCutter endCutter;
|
||||
int sampleFrameSize;
|
||||
adts *decoder;
|
||||
};
|
||||
|
||||
static int CalcPreBuffer(int buffer_setting, int bitrate)
|
||||
{
|
||||
if (bitrate < 8)
|
||||
bitrate = 8;
|
||||
else if (bitrate > 320)
|
||||
bitrate = 320;
|
||||
int prebuffer = (buffer_setting * bitrate) / 128;
|
||||
if (prebuffer > 100)
|
||||
prebuffer=100;
|
||||
return prebuffer;
|
||||
}
|
||||
|
||||
void DecodeLoop::SetupStream()
|
||||
{
|
||||
char buf[1024] = {0};
|
||||
int len;
|
||||
|
||||
m_is_stream = file.IsStream();
|
||||
|
||||
//Wait until we have data...
|
||||
while (!killDecodeThread && file.Peek(buf, 1024, &len) == NErr_Success && !len)
|
||||
Sleep(50);
|
||||
|
||||
m_is_stream_seekable = file.IsStreamSeekable();
|
||||
char *content_type = file.m_content_type;
|
||||
if (content_type)
|
||||
{
|
||||
if (!_strnicmp(content_type, "misc/ultravox", 13))
|
||||
{
|
||||
switch (file.uvox_last_message)
|
||||
{
|
||||
case 0x8001:
|
||||
case 0x8003:
|
||||
isEAAC = 1;
|
||||
isAac = 1;
|
||||
break;
|
||||
|
||||
case 0x8000:
|
||||
isAac = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!_strnicmp(content_type, "audio/aac", 9))
|
||||
{
|
||||
isEAAC = 1;
|
||||
isAac = 1;
|
||||
}
|
||||
else if (!_strnicmp(content_type, "audio/aacp", 10))
|
||||
{
|
||||
isEAAC = 1;
|
||||
isAac = 1;
|
||||
}
|
||||
else if (!_strnicmp(content_type, "audio/apl", 10))
|
||||
{
|
||||
isEAAC = 1;
|
||||
isAac = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: poll until connected to see if we get aac uvox frames or a content-type:aac header
|
||||
}
|
||||
|
||||
DWORD DecodeLoop::OpenOutput(int numChannels, int sampleRate, int bitsPerSample)
|
||||
{
|
||||
maxlatency = mod.outMod->Open(sampleRate, numChannels, bitsPerSample, -1, -1);
|
||||
|
||||
// maxlatency is the maxium latency between a outMod->Write() call and
|
||||
// when you hear those samples. In ms. Used primarily by the visualization
|
||||
// system.
|
||||
|
||||
if (maxlatency < 0) // error opening device
|
||||
{
|
||||
PostMessage(mod.hMainWindow, WM_COMMAND, 40047, 0);
|
||||
return 0;
|
||||
}
|
||||
g_sndopened = 1;
|
||||
if (maxlatency == 0 && file.IsStream() == 2) // can't use with disk writer
|
||||
{
|
||||
if (!killDecodeThread)
|
||||
{
|
||||
EnterCriticalSection(&g_lfnscs);
|
||||
WASABI_API_LNGSTRING_BUF(IDS_CANNOT_WRITE_STREAMS_TO_DISK,lastfn_status,256);
|
||||
LeaveCriticalSection(&g_lfnscs);
|
||||
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
|
||||
}
|
||||
if (!killDecodeThread) Sleep(200);
|
||||
if (!killDecodeThread) PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
g_bufferstat = 0;
|
||||
g_closeaudio = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (paused) mod.outMod->Pause(1);
|
||||
|
||||
// set the output plug-ins default volume.
|
||||
// volume is 0-255, -666 is a token for
|
||||
// current volume.
|
||||
|
||||
mod.outMod->SetVolume(-666);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DecodeLoop::CalculateCodecDelay()
|
||||
{
|
||||
int decoderDelaySamples = (int)decoder->GetDecoderDelay();
|
||||
|
||||
endCutter.SetSize(decoderDelaySamples*sampleFrameSize,
|
||||
file.prepad*sampleFrameSize,
|
||||
file.postpad*sampleFrameSize);
|
||||
}
|
||||
|
||||
void DecodeLoop::Viz()
|
||||
{
|
||||
if (!config_fastvis || (decoder->GetLayer() != 3 || g_ds))
|
||||
{
|
||||
int vis_waveNch;
|
||||
int vis_specNch;
|
||||
int csa = mod.SAGetMode();
|
||||
int is_vis_running = mod.VSAGetMode(&vis_specNch, &vis_waveNch);
|
||||
if (csa || is_vis_running)
|
||||
{
|
||||
int l = 576 * sampleFrameSize;
|
||||
int ti = decode_pos_ms;
|
||||
{
|
||||
if (g_ds == 2)
|
||||
{
|
||||
memcpy(g_samplebuf + g_samplebuf_used, g_samplebuf, g_samplebuf_used);
|
||||
}
|
||||
size_t pos = 0;
|
||||
while (pos < g_samplebuf_used)
|
||||
{
|
||||
int a, b;
|
||||
if (mod.SAGetMode()) mod.SAAddPCMData((char *)g_samplebuf + pos, g_nch, (int)g_bits, ti);
|
||||
if (mod.VSAGetMode(&a, &b)) mod.VSAAddPCMData((char *)g_samplebuf + pos, g_nch, (int)g_bits, ti);
|
||||
ti += ((l / sampleFrameSize * 1000) / g_srate);
|
||||
pos += l >> g_ds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int l = (576 * (int)g_bits * g_nch);
|
||||
int ti = decode_pos_ms;
|
||||
size_t pos = 0;
|
||||
int x = 0;
|
||||
while (pos < g_samplebuf_used)
|
||||
{
|
||||
do_layer3_vis((short*)(g_samplebuf + pos), &g_vis_table[x++][0][0][0], g_nch, ti);
|
||||
ti += (l / g_nch / 2 * 1000) / g_srate;
|
||||
pos += l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecodeLoop::Decode()
|
||||
{
|
||||
while (g_samplebuf_used < (size_t)g_framesize && !killDecodeThread && seek_needed == -1)
|
||||
{
|
||||
size_t newl = 0;
|
||||
size_t br=0;
|
||||
size_t endCut=0;
|
||||
int res = decoder->Decode(&file, g_samplebuf + g_samplebuf_used, sizeof(g_samplebuf) / 2 - g_samplebuf_used, &newl, &br, &endCut);
|
||||
|
||||
if (config_gapless && endCut)
|
||||
endCutter.SetEndSize((int)endCut* sampleFrameSize);
|
||||
|
||||
// we're not using switch here because we sometimes need to break out of the while loop
|
||||
if (res == adts::SUCCESS)
|
||||
{
|
||||
if (!file.m_vbr_frames)
|
||||
{
|
||||
if (br) {
|
||||
bool do_real_br=false;
|
||||
if (!(config_miscopts&2) && br != decoder->GetCurrentBitrate())
|
||||
{
|
||||
do_real_br=true;
|
||||
}
|
||||
|
||||
int r = (int)br;
|
||||
g_br_add += r;
|
||||
g_br_div++;
|
||||
r = (g_br_add + g_br_div / 2) / g_br_div;
|
||||
if (g_br != r)
|
||||
{
|
||||
need_synclight = false;
|
||||
g_br = r;
|
||||
if (!file.m_vbr_frames && file.IsSeekable()) g_length = MulDiv(file.GetContentLength(), 8, g_br);
|
||||
if (!do_real_br)
|
||||
mod.SetInfo(g_br, -1, -1, 1);
|
||||
}
|
||||
if (do_real_br)
|
||||
mod.SetInfo((int)br, -1, -1, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (br) {
|
||||
int r;
|
||||
if (!(config_miscopts&2) || !g_avg_vbr_br)
|
||||
r = (int)br;
|
||||
else r = g_avg_vbr_br;
|
||||
if (g_br != r)
|
||||
{
|
||||
need_synclight = false;
|
||||
g_br = r;
|
||||
mod.SetInfo(g_br, -1, -1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (need_synclight)
|
||||
{
|
||||
need_synclight = false;
|
||||
mod.SetInfo(-1, -1, -1, 1);
|
||||
}
|
||||
g_samplebuf_used += newl;
|
||||
}
|
||||
else if (res == adts::ENDOFFILE)
|
||||
{
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
else if (res == adts::NEEDMOREDATA)
|
||||
{
|
||||
if (file.IsStream() && !need_synclight)
|
||||
{
|
||||
need_synclight = true; mod.SetInfo(-1, -1, -1, 0);
|
||||
}
|
||||
if (file.IsStream() && !mod.outMod->IsPlaying())
|
||||
{
|
||||
need_prebuffer = CalcPreBuffer(config_http_prebuffer_underrun, (int)br);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!need_synclight) mod.SetInfo(-1, -1, -1, 0);
|
||||
need_synclight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecodeLoop::PreBuffer()
|
||||
{
|
||||
int p = file.RunStream();
|
||||
int pa = file.PercentAvailable();
|
||||
if (pa >= need_prebuffer || p == 2)
|
||||
{
|
||||
EnterCriticalSection(&g_lfnscs);
|
||||
lastfn_status[0] = 0;
|
||||
LeaveCriticalSection(&g_lfnscs);
|
||||
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
|
||||
need_prebuffer = 0;
|
||||
g_bufferstat = 0;
|
||||
last_bpos = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int bpos = pa * 100 / need_prebuffer;
|
||||
if (!g_bufferstat) g_bufferstat = decode_pos_ms;
|
||||
if (bpos != last_bpos)
|
||||
{
|
||||
last_bpos = bpos;
|
||||
EnterCriticalSection(&g_lfnscs);
|
||||
if (stricmp(lastfn_status, "stream temporarily interrupted"))
|
||||
{
|
||||
char langbuf[512] = {0};
|
||||
wsprintfA(lastfn_status, WASABI_API_LNGSTRING_BUF(IDS_BUFFER_X,langbuf,512), bpos);
|
||||
}
|
||||
LeaveCriticalSection(&g_lfnscs);
|
||||
|
||||
int csa = mod.SAGetMode();
|
||||
char tempdata[75*2] = {0, };
|
||||
int x;
|
||||
if (csa&1)
|
||||
{
|
||||
for (x = 0; x < bpos*75 / 100; x ++)
|
||||
{
|
||||
tempdata[x] = x * 16 / 75;
|
||||
}
|
||||
}
|
||||
if (csa&2)
|
||||
{
|
||||
int offs = (csa & 1) ? 75 : 0;
|
||||
x = 0;
|
||||
while (x < bpos*75 / 100)
|
||||
{
|
||||
tempdata[offs + x++] = -6 + x * 14 / 75;
|
||||
}
|
||||
while (x < 75)
|
||||
{
|
||||
tempdata[offs + x++] = 0;
|
||||
}
|
||||
}
|
||||
if (csa == 4)
|
||||
{
|
||||
tempdata[0] = tempdata[1] = (bpos * 127 / 100);
|
||||
}
|
||||
|
||||
if (csa) mod.SAAdd(tempdata, ++g_bufferstat, (csa == 3) ? 0x80000003 : csa);
|
||||
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecodeLoop::Seek(int seekPosition)
|
||||
{
|
||||
if (done == 3)
|
||||
return;
|
||||
done=0;
|
||||
int br = (int)decoder->GetCurrentBitrate();
|
||||
|
||||
need_prebuffer = CalcPreBuffer(config_http_prebuffer_underrun, br);
|
||||
if (need_prebuffer < 1) need_prebuffer = 5;
|
||||
|
||||
last_decode_pos_ms = decode_pos_ms = seekPosition;
|
||||
|
||||
seek_needed = -1;
|
||||
endCutter.Flush(decode_pos_ms);
|
||||
decoder->Flush(&file);
|
||||
done = 0;
|
||||
g_samplebuf_used = 0;
|
||||
|
||||
int r = g_br;
|
||||
if (g_br_div) r = (g_br_add + g_br_div / 2) / g_br_div;
|
||||
file.Seek(decode_pos_ms, r);
|
||||
// need_prebuffer=config_http_prebuffer/8;
|
||||
// g_br_add=g_br_div=0;
|
||||
|
||||
}
|
||||
|
||||
DWORD DecodeLoop::OpenDecoder()
|
||||
{
|
||||
mod.UsesOutputPlug &= ~8;
|
||||
if (isAac)
|
||||
{
|
||||
if (isEAAC)
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(adts_aac_guid);
|
||||
if (factory)
|
||||
decoder = (adts *)factory->getInterface();
|
||||
|
||||
mod.UsesOutputPlug|=8;
|
||||
}
|
||||
if (!decoder)
|
||||
{
|
||||
decoder = new ADTS_VLB;
|
||||
mod.UsesOutputPlug &= ~8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(adts_mp2_guid);
|
||||
if (factory)
|
||||
decoder = (adts *)factory->getInterface();
|
||||
|
||||
mod.UsesOutputPlug|=8;
|
||||
}
|
||||
|
||||
if (decoder) {
|
||||
decoder->SetDecoderHooks(mp3GiveVisData, mp2Equalize, mp3Equalize);
|
||||
}
|
||||
|
||||
if (decoder
|
||||
&& decoder->Initialize(AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false),
|
||||
config_downmix == 2,
|
||||
AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true),
|
||||
(int)AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16), true, false,
|
||||
(config_miscopts&1)/*crc*/) == adts::SUCCESS
|
||||
&& decoder->Open(&file))
|
||||
{
|
||||
// sync to stream
|
||||
while (1)
|
||||
{
|
||||
switch (decoder->Sync(&file, g_samplebuf, sizeof(g_samplebuf), &g_samplebuf_used, &br))
|
||||
{
|
||||
case adts::SUCCESS:
|
||||
return 1;
|
||||
case adts::FAILURE:
|
||||
case adts::ENDOFFILE:
|
||||
if (!killDecodeThread)
|
||||
{
|
||||
if (!lastfn_status_err)
|
||||
{
|
||||
EnterCriticalSection(&g_lfnscs);
|
||||
WASABI_API_LNGSTRING_BUF(IDS_ERROR_SYNCING_TO_STREAM,lastfn_status,256);
|
||||
LeaveCriticalSection(&g_lfnscs);
|
||||
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
|
||||
}
|
||||
}
|
||||
if (!killDecodeThread) Sleep(200);
|
||||
if (!killDecodeThread) PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
return 0;
|
||||
case adts::NEEDMOREDATA:
|
||||
if (!killDecodeThread && file.IsStream()) Sleep(25);
|
||||
if (killDecodeThread) return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD DecodeLoop::Loop()
|
||||
{
|
||||
last_decode_pos_ms = 0;
|
||||
|
||||
if (file.Open(lastfn, config_max_bufsize_k) != NErr_Success)
|
||||
{
|
||||
if (!killDecodeThread) Sleep(200);
|
||||
if (!killDecodeThread) PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file.IsSeekable()) mod.is_seekable = 1;
|
||||
|
||||
wchar_t *ext = PathFindExtension(lastfn);
|
||||
if (!_wcsicmp(ext, L".aac")
|
||||
|| !_wcsicmp(ext, L".vlb")
|
||||
|| !_wcsicmp(ext, L".apl"))
|
||||
{
|
||||
if (file.IsStream())
|
||||
SetupStream();
|
||||
else
|
||||
{
|
||||
isAac = 1;
|
||||
if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".apl")) isEAAC = 1;
|
||||
}
|
||||
}
|
||||
else if (file.IsStream())
|
||||
SetupStream();
|
||||
|
||||
if (OpenDecoder() == 0)
|
||||
return 0;
|
||||
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
g_playing_file = &file;
|
||||
if (file.uvox_3901)
|
||||
{
|
||||
PostMessage(mod.hMainWindow, WM_WA_IPC, (WPARAM) "0x3901", IPC_METADATA_CHANGED);
|
||||
PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
|
||||
|
||||
EnterCriticalSection(&g_lfnscs);
|
||||
lastfn_status[0] = 0;
|
||||
LeaveCriticalSection(&g_lfnscs);
|
||||
|
||||
lastfn_data_ready = 1;
|
||||
|
||||
// TODO? if (decoder != &aacp) // hack because aac+ bitrate isn't accurate at this point
|
||||
br = decoder->GetCurrentBitrate();
|
||||
|
||||
need_prebuffer = CalcPreBuffer(config_http_prebuffer, (int)br);
|
||||
|
||||
if (((!(config_eqmode&4) && decoder->GetLayer() == 3) ||
|
||||
((config_eqmode&8) && decoder->GetLayer() < 3)))
|
||||
{
|
||||
mod.UsesOutputPlug |= 2;
|
||||
}
|
||||
else
|
||||
mod.UsesOutputPlug &= ~2;
|
||||
|
||||
decoder->CalculateFrameSize(&g_framesize);
|
||||
decoder->GetOutputParameters(&g_bits, &g_nch, &g_srate);
|
||||
|
||||
if (!killDecodeThread && file.IsStream() == 1)
|
||||
{
|
||||
DWORD_PTR dw;
|
||||
if (!killDecodeThread) SendMessageTimeout(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE, SMTO_BLOCK, 100, &dw);
|
||||
if (!killDecodeThread) SendMessageTimeout(mod.hMainWindow, WM_TIMER, 38, 0, SMTO_BLOCK, 100, &dw);
|
||||
}
|
||||
|
||||
sampleFrameSize = g_nch * ((int)g_bits/8);
|
||||
|
||||
if (config_gapless)
|
||||
CalculateCodecDelay();
|
||||
|
||||
if (OpenOutput(g_nch, g_srate, (int)g_bits) == 0)
|
||||
return 0;
|
||||
|
||||
/* ----- send info to winamp and vis: bitrate, etc ----- */
|
||||
g_br = (int)decoder->GetCurrentBitrate();
|
||||
|
||||
g_br_add = g_br;
|
||||
g_br_div = 1;
|
||||
g_avg_vbr_br = file.GetAvgVBRBitrate();
|
||||
mod.SetInfo(g_br, g_srate / 1000, g_nch, 0);
|
||||
|
||||
// initialize visualization stuff
|
||||
mod.SAVSAInit((maxlatency << g_ds), g_srate);
|
||||
mod.VSASetInfo(g_srate, g_nch);
|
||||
/* ----- end send info to winamp and vis ----- */
|
||||
|
||||
if (file.IsSeekable() && g_br)
|
||||
{
|
||||
mod.is_seekable = 1;
|
||||
if (!file.m_vbr_frames) g_length = MulDiv(file.GetContentLength(), 8, g_br);
|
||||
else g_length = file.m_vbr_ms;
|
||||
}
|
||||
|
||||
if (file.IsStream())
|
||||
{
|
||||
if (need_prebuffer < config_http_prebuffer / 2)
|
||||
need_prebuffer = config_http_prebuffer / 2;
|
||||
}
|
||||
|
||||
while (!killDecodeThread)
|
||||
{
|
||||
if (seek_needed != -1)
|
||||
Seek(seek_needed);
|
||||
|
||||
if (need_prebuffer && file.IsStream() && maxlatency && !file.EndOf())
|
||||
PreBuffer();
|
||||
|
||||
int needsleep = 1;
|
||||
|
||||
if (done == 2) // done was set to TRUE during decoding, signaling eof
|
||||
{
|
||||
mod.outMod->CanWrite(); // some output drivers need CanWrite
|
||||
// to be called on a regular basis.
|
||||
|
||||
if (!mod.outMod->IsPlaying())
|
||||
{
|
||||
// we're done playing, so tell Winamp and quit the thread.
|
||||
if (!killDecodeThread) PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
done=3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int fs = (g_framesize * ((mod.dsp_isactive() == 1) ? 2 : 1));
|
||||
// TODO: we should really support partial writes, there's no gaurantee that CanWrite() will EVER get big enough
|
||||
if (mod.outMod->CanWrite() >= fs && (!need_prebuffer || !file.IsStream() || !maxlatency))
|
||||
// CanWrite() returns the number of bytes you can write, so we check that
|
||||
// to the block size. the reason we multiply the block size by two if
|
||||
// mod.dsp_isactive() is that DSP plug-ins can change it by up to a
|
||||
// factor of two (for tempo adjustment).
|
||||
{
|
||||
int p = mod.SAGetMode();
|
||||
g_vis_enabled = ((p & 1) || p == 4);
|
||||
if (!g_vis_enabled)
|
||||
{
|
||||
int s, a;
|
||||
mod.VSAGetMode(&s, &a);
|
||||
if (s) g_vis_enabled = 1;
|
||||
}
|
||||
|
||||
Decode();
|
||||
|
||||
if ((g_samplebuf_used >= (size_t)g_framesize || (done && g_samplebuf_used > 0)) && seek_needed == -1)
|
||||
{
|
||||
// adjust decode position variable
|
||||
if (file.isSeekReset())
|
||||
last_decode_pos_ms = decode_pos_ms = 0;
|
||||
else
|
||||
decode_pos_ms += ((int)g_samplebuf_used / sampleFrameSize * 1000) / g_srate;
|
||||
|
||||
// if we have a DSP plug-in, then call it on our samples
|
||||
if (mod.dsp_isactive())
|
||||
{
|
||||
g_samplebuf_used = mod.dsp_dosamples((short *)g_samplebuf, (int)g_samplebuf_used / sampleFrameSize, (int)g_bits, g_nch, g_srate) * sampleFrameSize;
|
||||
}
|
||||
Viz();
|
||||
endCutter.Write((char *)g_samplebuf, (int)g_samplebuf_used);
|
||||
g_samplebuf_used = 0;
|
||||
needsleep = 0;
|
||||
//memcpy(g_samplebuf,g_samplebuf+r,g_samplebuf_used);
|
||||
}
|
||||
if (done)
|
||||
{
|
||||
endCutter.Write(0, 0);
|
||||
done = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decode_pos_ms > last_decode_pos_ms + 1000)
|
||||
{
|
||||
last_decode_pos_ms = decode_pos_ms;
|
||||
}
|
||||
|
||||
if (needsleep) Sleep(10);
|
||||
// if we can't write data, wait a little bit. Otherwise, continue
|
||||
// through the loop writing more data (without sleeping)
|
||||
}
|
||||
|
||||
/* ---- change some globals to let everyone know we're done */
|
||||
EnterCriticalSection(&g_lfnscs);
|
||||
lastfn_status[0] = 0;
|
||||
LeaveCriticalSection(&g_lfnscs);
|
||||
g_bufferstat = 0;
|
||||
g_closeaudio = 1;
|
||||
/* ---- */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI DecodeThread(LPVOID b)
|
||||
{
|
||||
DecodeLoop loop;
|
||||
|
||||
|
||||
|
||||
DWORD ret = loop.Loop();
|
||||
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
g_playing_file = 0;
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef NULLSOFT_DECODETHREADH
|
||||
#define NULLSOFT_DECODETHREADH
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
DWORD WINAPI DecodeThread(LPVOID b);
|
||||
|
||||
extern volatile int seek_needed;
|
||||
extern CRITICAL_SECTION g_lfnscs;
|
||||
extern int g_ds;
|
||||
extern int g_sndopened;
|
||||
extern int g_bufferstat;
|
||||
extern int g_length;
|
||||
extern volatile int g_closeaudio;
|
||||
extern int decode_pos_ms; // current decoding position, in milliseconds.
|
||||
extern int g_vis_enabled;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,315 @@
|
||||
#include "main.h"
|
||||
#include "Metadata.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "../nu/ns_wc.h"
|
||||
#include "uvox_3901.h"
|
||||
#include "uvox_3902.h"
|
||||
#include "Stopper.h"
|
||||
#include <shlwapi.h>
|
||||
#include "../Agave/Language/api_language.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
extern CGioFile *g_playing_file;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Metadata *m_ext_set_mp3info = NULL;
|
||||
Metadata *m_ext_get_mp3info = NULL;
|
||||
extern "C" __declspec(dllexport)
|
||||
int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
if (!_stricmp(data, "type"))
|
||||
{
|
||||
dest[0] = '0';
|
||||
dest[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
if (!_stricmp(data, "rateable"))
|
||||
{
|
||||
dest[0] = '1';
|
||||
dest[1] = 0;
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(data, "family"))
|
||||
{
|
||||
if (!fn || !fn[0]) return 0;
|
||||
int len = lstrlenW(fn);
|
||||
if (len < 4 || L'.' != fn[len - 4]) return 0;
|
||||
const wchar_t *p = &fn[len - 3];
|
||||
if (!_wcsicmp(p, L"MP3")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING_MP3, dest, destlen); return 1; }
|
||||
if (!_wcsicmp(p, L"MP2")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING_MP2, dest, destlen); return 1; }
|
||||
if (!_wcsicmp(p, L"MP1")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING_MP1, dest, destlen); return 1; }
|
||||
if (!_wcsicmp(p, L"AAC")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING_MPEG2_AAC, dest, destlen); return 1; }
|
||||
if (!_wcsicmp(p, L"VLB")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING_DOLBY, dest, destlen); return 1; }
|
||||
return 0;
|
||||
}
|
||||
else if (!_stricmp(data, "mime"))
|
||||
{
|
||||
if (!fn || !fn[0]) return 0;
|
||||
int len = lstrlenW(fn);
|
||||
if (len < 4 || L'.' != fn[len - 4]) return 0;
|
||||
const wchar_t *p = &fn[len - 3];
|
||||
if (!_wcsicmp(p, L"MP3")) { StringCchCopyW(dest, destlen, L"audio/mpeg"); return 1; }
|
||||
if (!_wcsicmp(p, L"MP2")) { StringCchCopyW(dest, destlen, L"audio/mpeg"); return 1; }
|
||||
if (!_wcsicmp(p, L"MP1")) { StringCchCopyW(dest, destlen, L"audio/mpeg"); return 1; }
|
||||
if (!_wcsicmp(p, L"AAC")) { StringCchCopyW(dest, destlen, L"audio/aac"); return 1; }
|
||||
if (!_wcsicmp(p, L"VLB")) { StringCchCopyW(dest, destlen, L"audio/vlb"); return 1; }
|
||||
return 0;
|
||||
}
|
||||
else if (!_strnicmp(data, "uvox/", 5))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file)
|
||||
{
|
||||
if (g_playing_file->uvox_3901) // check again now that we've acquired the lock
|
||||
{
|
||||
Ultravox3901 uvox_metadata;
|
||||
if (uvox_metadata.Parse(g_playing_file->uvox_3901) != API_XML_FAILURE)
|
||||
{
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return uvox_metadata.GetExtendedData(data, dest, (int)destlen);
|
||||
}
|
||||
}
|
||||
else if (g_playing_file->uvox_3902)
|
||||
{
|
||||
Ultravox3902 uvox_metadata;
|
||||
if (uvox_metadata.Parse(g_playing_file->uvox_3902) != API_XML_FAILURE)
|
||||
{
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return uvox_metadata.GetExtendedData(data, dest, (int)destlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 0;
|
||||
}
|
||||
else if (_stricmp(data, "0x3901") == 0)
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file && g_playing_file->uvox_3901) // check again now that we've acquired the lock
|
||||
{
|
||||
if (dest == NULL) // It's empty, he's looking for the size of the 0x3901
|
||||
{
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, g_playing_file->uvox_3901, -1, 0, 0);
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return size;
|
||||
}
|
||||
else
|
||||
{
|
||||
MultiByteToWideCharSZ(CP_UTF8, 0, g_playing_file->uvox_3901, -1, dest, (int)destlen);
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 0;
|
||||
}
|
||||
else if (!_stricmp(data, "streamtype"))
|
||||
{
|
||||
if (lstrcmpW(lastfn, fn))
|
||||
return 0;
|
||||
|
||||
if (g_playing_file)
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file) // check again now that we've acquired the lock
|
||||
{
|
||||
StringCchPrintfW(dest, destlen, L"%d", g_playing_file->IsStream());
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (!_stricmp(data, "streammetadata"))
|
||||
{
|
||||
if (lstrcmpW(lastfn, fn))
|
||||
return 0;
|
||||
|
||||
if (g_playing_file)
|
||||
{
|
||||
uint32_t len=0;
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file && g_playing_file->GetID3v2(&len) && len > 0) // check again now that we've acquired the lock
|
||||
{
|
||||
lstrcpynW(dest, L"1", (int)destlen);
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1; // always return 1 to ensure we can do title lookups
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (!_stricmp(data, "streamtitle"))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file) // check again now that we've acquired the lock
|
||||
ConvertTryUTF8(g_playing_file->stream_current_title, dest, destlen);
|
||||
else
|
||||
dest[0]=0;
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(data, "streamname"))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file) // check again now that we've acquired the lock
|
||||
ConvertTryUTF8(g_playing_file->stream_name, dest, destlen);
|
||||
else
|
||||
dest[0]=0;
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(data, "streamurl"))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file) // check again now that we've acquired the lock
|
||||
ConvertTryUTF8(g_playing_file->stream_url, dest, destlen);
|
||||
else
|
||||
dest[0]=0;
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(data, "streamgenre"))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file) // check again now that we've acquired the lock
|
||||
ConvertTryUTF8(g_playing_file->stream_genre, dest, destlen);
|
||||
else
|
||||
dest[0]=0;
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(data, "streaminformation"))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
if (g_playing_file)
|
||||
g_playing_file->GetStreamInfo(dest, destlen);
|
||||
else
|
||||
dest[0]=0;
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fn || !fn[0]) return 0;
|
||||
|
||||
if (!_wcsnicmp(fn, L"uvox://", 7))
|
||||
return 0;
|
||||
|
||||
if (g_playing_file && PathIsURL(fn) && !lstrcmpW(lastfn, fn))
|
||||
{
|
||||
EnterCriticalSection(&streamInfoLock);
|
||||
uint32_t len = 0;
|
||||
if (g_playing_file && g_playing_file->GetID3v2(&len) && len > 0) // check again now that we've acquired the lock
|
||||
{
|
||||
Metadata meta(g_playing_file, fn);
|
||||
int ret = meta.GetExtendedData(data, dest, (int)destlen);
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
return ret;
|
||||
}
|
||||
LeaveCriticalSection(&streamInfoLock);
|
||||
}
|
||||
|
||||
if (PathIsURL(fn))
|
||||
return 0;
|
||||
|
||||
if (m_ext_get_mp3info && (!m_ext_get_mp3info->IsMe(fn) || HasFileTimeChanged(fn)))
|
||||
{
|
||||
m_ext_get_mp3info->Release();
|
||||
m_ext_get_mp3info=0;
|
||||
}
|
||||
|
||||
if (!m_ext_get_mp3info)
|
||||
{
|
||||
m_ext_get_mp3info = new Metadata;
|
||||
m_ext_get_mp3info->Open(fn);
|
||||
}
|
||||
|
||||
return m_ext_get_mp3info->GetExtendedData(data, dest, (int)destlen);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, const wchar_t *val)
|
||||
{
|
||||
if (!m_ext_set_mp3info || (m_ext_set_mp3info && !m_ext_set_mp3info->IsMe(fn)))
|
||||
{
|
||||
if(m_ext_set_mp3info) m_ext_set_mp3info->Release();
|
||||
m_ext_set_mp3info = new Metadata;
|
||||
m_ext_set_mp3info->Open(fn);
|
||||
}
|
||||
return m_ext_set_mp3info->SetExtendedData(data, val);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__declspec(dllexport) int winampWriteExtendedFileInfo()
|
||||
{
|
||||
// flush our read cache too :)
|
||||
if(m_ext_get_mp3info) m_ext_get_mp3info->Release();
|
||||
m_ext_get_mp3info = NULL;
|
||||
|
||||
if (!m_ext_set_mp3info) return 0;
|
||||
|
||||
Stopper stopper;
|
||||
if (m_ext_set_mp3info->IsMe(lastfn))
|
||||
stopper.Stop();
|
||||
|
||||
// just in-case something changed
|
||||
if (!m_ext_set_mp3info) return 0;
|
||||
|
||||
int ret = m_ext_set_mp3info->Save();
|
||||
stopper.Play();
|
||||
m_ext_set_mp3info->Release();
|
||||
m_ext_set_mp3info = NULL;
|
||||
|
||||
// update last modified so we're not re-queried on our own updates
|
||||
UpdateFileTimeChanged(lastfn);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport)
|
||||
int winampClearExtendedFileInfoW(const wchar_t *fn)
|
||||
{
|
||||
Metadata meta;
|
||||
if (meta.Open(fn)==METADATA_SUCCESS)
|
||||
{
|
||||
meta.id3v2.Clear();
|
||||
Stopper stopper;
|
||||
if (meta.IsMe(lastfn))
|
||||
stopper.Stop();
|
||||
meta.Save();
|
||||
stopper.Play();
|
||||
|
||||
// update last modified so we're not re-queried on our own updates
|
||||
UpdateFileTimeChanged(fn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
#include "main.h"
|
||||
#include "adts.h"
|
||||
#include <memory.h>
|
||||
#include <malloc.h>
|
||||
#include <xutility>
|
||||
#include <assert.h>
|
||||
#include <shlwapi.h>
|
||||
#include <foundation/error.h>
|
||||
#include "../nu/RingBuffer.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
|
||||
// {19450308-90D7-4E45-8A9D-DC71E67123E2}
|
||||
static const GUID adts_aac_guid =
|
||||
{ 0x19450308, 0x90d7, 0x4e45, { 0x8a, 0x9d, 0xdc, 0x71, 0xe6, 0x71, 0x23, 0xe2 } };
|
||||
|
||||
// {4192FE3F-E843-445c-8D62-51BE5EE5E68C}
|
||||
static const GUID adts_mp2_guid =
|
||||
{ 0x4192fe3f, 0xe843, 0x445c, { 0x8d, 0x62, 0x51, 0xbe, 0x5e, 0xe5, 0xe6, 0x8c } };
|
||||
|
||||
class GapCutter
|
||||
{
|
||||
public:
|
||||
GapCutter() {}
|
||||
|
||||
void SetEndSize( int postSize );
|
||||
void SetSize( int preSize, int postSize );
|
||||
void Flush( int time_in_ms );
|
||||
int Write( void *dest, void *input, size_t inputBytes );
|
||||
|
||||
private:
|
||||
RingBuffer ringBuffer;
|
||||
|
||||
int preCut = 0;
|
||||
int preCutSize = 0;
|
||||
};
|
||||
|
||||
void GapCutter::SetEndSize(int postSize)
|
||||
{
|
||||
if (postSize < 0)
|
||||
postSize = 0;
|
||||
|
||||
if (postSize)
|
||||
{
|
||||
ringBuffer.Reset();
|
||||
ringBuffer.reserve(postSize);
|
||||
}
|
||||
}
|
||||
|
||||
void GapCutter::SetSize( int preSize, int postSize )
|
||||
{
|
||||
if ( preSize < 0 )
|
||||
preSize = 0;
|
||||
|
||||
if ( postSize < 0 )
|
||||
postSize = 0;
|
||||
|
||||
SetEndSize( postSize );
|
||||
|
||||
preCutSize = preSize;
|
||||
preCut = preSize;
|
||||
}
|
||||
|
||||
void GapCutter::Flush( int time_in_ms )
|
||||
{
|
||||
// if (time_in_ms == 0) // TODO: calculate actual delay if we seek within the encoder delay area
|
||||
preCut = preCutSize; // reset precut size if we seek to the start
|
||||
|
||||
ringBuffer.clear();
|
||||
}
|
||||
|
||||
int GapCutter::Write( void *dest, void *input, size_t inputBytes ) // returns # of bytes written
|
||||
{
|
||||
int bytesWritten = 0;
|
||||
unsigned __int8 *in = (unsigned __int8 *)input;
|
||||
unsigned __int8 *out = (unsigned __int8 *)dest;
|
||||
// cut pre samples, if necessary
|
||||
|
||||
intptr_t pre = min( preCut, (intptr_t)inputBytes );
|
||||
in += pre;
|
||||
inputBytes -= pre;
|
||||
preCut -= (int)pre;
|
||||
|
||||
if ( !inputBytes )
|
||||
return bytesWritten;
|
||||
|
||||
size_t remainingFill = ringBuffer.avail();
|
||||
intptr_t fillWrite = min( (intptr_t)( inputBytes - remainingFill ), (intptr_t)ringBuffer.size() ); // only write fill buffer if we've got enough left to fill it up
|
||||
|
||||
if ( fillWrite > 0 )
|
||||
{
|
||||
size_t written = ringBuffer.read( out, fillWrite );
|
||||
|
||||
bytesWritten += (int)written;
|
||||
out += written;
|
||||
}
|
||||
|
||||
remainingFill = ringBuffer.avail();
|
||||
|
||||
int outWrite = (int)max( 0, (intptr_t)( inputBytes - remainingFill ) );
|
||||
if ( outWrite )
|
||||
memcpy( out, in, outWrite );
|
||||
|
||||
bytesWritten += outWrite;
|
||||
in += outWrite;
|
||||
inputBytes -= outWrite;
|
||||
|
||||
if ( inputBytes )
|
||||
ringBuffer.write( in, inputBytes );
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
struct ExtendedRead
|
||||
{
|
||||
ExtendedRead() { memset(&data, 0, sizeof(data)); }
|
||||
~ExtendedRead()
|
||||
{
|
||||
file.Close();
|
||||
if ( decoder )
|
||||
{
|
||||
decoder->Close();
|
||||
decoder->Release();
|
||||
}
|
||||
}
|
||||
|
||||
bool Open( const wchar_t *fn, int *size, int *bps, int *nch, int *srate, bool useFloat );
|
||||
|
||||
adts *decoder = NULL;
|
||||
int bits = 0;
|
||||
size_t initialData = 0;
|
||||
int frameSize = 0;
|
||||
|
||||
GapCutter cutter;
|
||||
CGioFile file;
|
||||
|
||||
#define DATA_SIZE (6*4*2*2*1152)
|
||||
unsigned char data[DATA_SIZE];
|
||||
};
|
||||
|
||||
bool ExtendedRead::Open(const wchar_t *fn, int *size, int *bps, int *nch, int *srate, bool useFloat)
|
||||
{
|
||||
if (file.Open(fn, config_max_bufsize_k) != NErr_Success)
|
||||
return false;
|
||||
|
||||
int downmix = 0;
|
||||
bool allowsurround = 1;
|
||||
if (*nch == 1)
|
||||
{
|
||||
downmix = 1;
|
||||
allowsurround = 0;
|
||||
}
|
||||
else if (*nch == 2)
|
||||
{
|
||||
allowsurround = 0;
|
||||
}
|
||||
|
||||
if (useFloat)
|
||||
bits=32;
|
||||
else if (*bps == 24)
|
||||
bits = 24;
|
||||
else
|
||||
{
|
||||
bits = 16;
|
||||
*bps = 16;
|
||||
}
|
||||
|
||||
wchar_t *ext = PathFindExtensionW(fn);
|
||||
if (!_wcsicmp(ext, L".vlb"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".apl"))
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(adts_aac_guid);
|
||||
if (factory)
|
||||
decoder = (adts *)factory->getInterface();
|
||||
}
|
||||
else
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(adts_mp2_guid);
|
||||
if (factory)
|
||||
decoder = (adts *)factory->getInterface();
|
||||
}
|
||||
|
||||
if (!decoder)
|
||||
return false;
|
||||
|
||||
decoder->Initialize(!!downmix, 0, allowsurround, bits, false, useFloat);
|
||||
decoder->Open(&file);
|
||||
size_t bitrate;
|
||||
bool done=false;
|
||||
while (!done)
|
||||
{
|
||||
switch (decoder->Sync(&file, data, sizeof(data), &initialData, &bitrate))
|
||||
{
|
||||
case adts::SUCCESS:
|
||||
done=true;
|
||||
break;
|
||||
case adts::FAILURE:
|
||||
case adts::ENDOFFILE:
|
||||
return false;
|
||||
case adts::NEEDMOREDATA:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t numBits = 0;
|
||||
decoder->GetOutputParameters(&numBits, nch, srate);
|
||||
*bps = bits = (int)numBits;
|
||||
frameSize = bits / 8 * *nch;
|
||||
if (config_gapless)
|
||||
cutter.SetSize((file.prepad + (int)decoder->GetDecoderDelay())*frameSize, (file.postpad - (int)decoder->GetDecoderDelay())*frameSize);
|
||||
|
||||
if (file.m_vbr_samples) // exact number of samples in the LAME header, how nice :)
|
||||
*size = (int)file.m_vbr_samples*frameSize;
|
||||
else if (file.m_vbr_ms) // if we know the milliseconds accurately
|
||||
*size = MulDiv(*srate * frameSize, file.m_vbr_ms, 1000); // our size should be mostly accurate
|
||||
else // no helpful info to go on
|
||||
{
|
||||
// just guess based on bitrate and content length
|
||||
bitrate=decoder->GetCurrentBitrate();
|
||||
int len_ms = MulDiv(file.GetContentLength(), 8, (int)bitrate);
|
||||
*size = MulDiv(*srate * frameSize, len_ms, 1000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
//returns handle!=0 if successful, 0 if error
|
||||
//size will return the final nb of bytes written to the output, -1 if unknown
|
||||
__declspec(dllexport) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
|
||||
{
|
||||
ExtendedRead *ext = new ExtendedRead;
|
||||
if (ext)
|
||||
{
|
||||
if (ext->Open(fn, size, bps, nch, srate, false))
|
||||
return reinterpret_cast<intptr_t>(ext);
|
||||
delete ext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) intptr_t winampGetExtendedRead_openW_float(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
|
||||
{
|
||||
ExtendedRead *ext = new ExtendedRead;
|
||||
if (ext)
|
||||
{
|
||||
if (ext->Open(fn, size, bps, nch, srate, true))
|
||||
return reinterpret_cast<intptr_t>(ext);
|
||||
delete ext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//returns nb of bytes read. -1 if read error (like CD ejected). if (ret==0), EOF is assumed
|
||||
__declspec(dllexport) size_t winampGetExtendedRead_getData(intptr_t handle, char *dest, size_t len, int *killswitch)
|
||||
{
|
||||
ExtendedRead *ext = (ExtendedRead *)handle;
|
||||
int copied = 0;
|
||||
if (ext)
|
||||
{
|
||||
len -= (len % ext->frameSize); // only do whole frames
|
||||
while (len)
|
||||
{
|
||||
size_t toMove = min(len, ext->initialData);
|
||||
int toCopy = ext->cutter.Write(dest, ext->data, toMove);
|
||||
|
||||
if (ext->initialData != toMove)
|
||||
memmove(ext->data, ext->data + toMove, ext->initialData - toMove);
|
||||
|
||||
ext->initialData -= toMove;
|
||||
len -= toCopy;
|
||||
copied += toCopy;
|
||||
dest += toCopy;
|
||||
|
||||
if (!ext->initialData)
|
||||
{
|
||||
size_t written = 0, bitrate, endCut = 0;
|
||||
int ret = ext->decoder->Decode(&ext->file, ext->data, DATA_SIZE, &written, &bitrate, &endCut);
|
||||
if (config_gapless && endCut)
|
||||
ext->cutter.SetEndSize((int)(endCut - ext->decoder->GetDecoderDelay())*ext->frameSize);
|
||||
ext->initialData = written;
|
||||
if (/*ret != adts::SUCCESS && */!ext->initialData && (copied || ret == adts::ENDOFFILE))
|
||||
return copied;
|
||||
|
||||
if (ret == adts::FAILURE)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
// return nonzero on success, zero on failure.
|
||||
__declspec(dllexport) int winampGetExtendedRead_setTime(intptr_t handle, int millisecs)
|
||||
{
|
||||
ExtendedRead *ext = (ExtendedRead *)handle;
|
||||
if (ext)
|
||||
{
|
||||
if (!ext->file.IsSeekable()) return 0; // not seekable
|
||||
|
||||
int br = ext->file.GetAvgVBRBitrate();
|
||||
if (!br) br = (int)ext->decoder->GetCurrentBitrate();
|
||||
if (!br) return 0; // can't find a valid bitrate
|
||||
|
||||
ext->cutter.Flush(millisecs); // fucko?
|
||||
ext->decoder->Flush(&ext->file);
|
||||
|
||||
ext->file.Seek(millisecs,br);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void winampGetExtendedRead_close(intptr_t handle)
|
||||
{
|
||||
ExtendedRead *ext = (ExtendedRead *)handle;
|
||||
if (ext) delete ext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "api__in_mp3.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
|
||||
template <class api_T>
|
||||
void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
|
||||
{
|
||||
if (mod.service)
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
|
||||
if (factory)
|
||||
api_t = reinterpret_cast<api_T *>( factory->getInterface() );
|
||||
}
|
||||
}
|
||||
|
||||
template <class api_T>
|
||||
void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
|
||||
{
|
||||
if (mod.service && api_t)
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
|
||||
if (factory)
|
||||
factory->releaseInterface(api_t);
|
||||
}
|
||||
api_t = NULL;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "main.h"
|
||||
#include "MP3Info.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include "id3.h"
|
||||
#include "api.h"
|
||||
#include <math.h>
|
||||
#include "config.h"
|
||||
#include "LAMEinfo.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
#include "ID3v1.h"
|
||||
#include "../nu/ns_wc.h"
|
||||
#include <windows.h>
|
||||
#include "config.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
const wchar_t *id3v1_genres[] =
|
||||
{
|
||||
L"Blues", L"Classic Rock", L"Country", L"Dance", L"Disco", L"Funk", L"Grunge",
|
||||
L"Hip-Hop", L"Jazz", L"Metal", L"New Age", L"Oldies", L"Other", L"Pop", L"R&B",
|
||||
L"Rap", L"Reggae", L"Rock", L"Techno", L"Industrial", L"Alternative", L"Ska",
|
||||
L"Death Metal", L"Pranks", L"Soundtrack", L"Euro-Techno", L"Ambient", L"Trip-Hop",
|
||||
L"Vocal", L"Jazz+Funk", L"Fusion", L"Trance", L"Classical", L"Instrumental",
|
||||
L"Acid", L"House", L"Game", L"Sound Clip", L"Gospel", L"Noise", L"Alt Rock",
|
||||
L"Bass", L"Soul", L"Punk", L"Space", L"Meditative", L"Instrumental Pop",
|
||||
L"Instrumental Rock", L"Ethnic", L"Gothic", L"Darkwave", L"Techno-Industrial",
|
||||
L"Electronic", L"Pop-Folk", L"Eurodance", L"Dream", L"Southern Rock", L"Comedy",
|
||||
L"Cult", L"Gangsta Rap", L"Top 40", L"Christian Rap", L"Pop/Funk", L"Jungle",
|
||||
L"Native American", L"Cabaret", L"New Wave", L"Psychedelic", L"Rave", L"Showtunes",
|
||||
L"Trailer", L"Lo-Fi", L"Tribal", L"Acid Punk", L"Acid Jazz", L"Polka", L"Retro",
|
||||
L"Musical", L"Rock & Roll", L"Hard Rock", L"Folk", L"Folk-Rock", L"National Folk",
|
||||
L"Swing", L"Fast-Fusion", L"Bebop", L"Latin", L"Revival", L"Celtic", L"Bluegrass",
|
||||
L"Avantgarde", L"Gothic Rock", L"Progressive Rock", L"Psychedelic Rock",
|
||||
L"Symphonic Rock", L"Slow Rock", L"Big Band", L"Chorus", L"Easy Listening",
|
||||
L"Acoustic", L"Humour", L"Speech", L"Chanson", L"Opera", L"Chamber Music", L"Sonata",
|
||||
L"Symphony", L"Booty Bass", L"Primus", L"Porn Groove", L"Satire", L"Slow Jam",
|
||||
L"Club", L"Tango", L"Samba", L"Folklore", L"Ballad", L"Power Ballad", L"Rhythmic Soul",
|
||||
L"Freestyle", L"Duet", L"Punk Rock", L"Drum Solo", L"A Cappella", L"Euro-House",
|
||||
L"Dance Hall", L"Goa", L"Drum & Bass", L"Club-House", L"Hardcore", L"Terror", L"Indie",
|
||||
L"BritPop", L"Afro-Punk", L"Polsk Punk", L"Beat", L"Christian Gangsta Rap",
|
||||
L"Heavy Metal", L"Black Metal", L"Crossover", L"Contemporary Christian",
|
||||
L"Christian Rock", L"Merengue", L"Salsa", L"Thrash Metal", L"Anime", L"JPop",
|
||||
L"Synthpop", L"Abstract", L"Art Rock", L"Baroque", L"Bhangra", L"Big Beat",
|
||||
L"Breakbeat", L"Chillout", L"Downtempo", L"Dub", L"EBM", L"Eclectic", L"Electro",
|
||||
L"Electroclash", L"Emo", L"Experimental", L"Garage", L"Global", L"IDM", L"Illbient",
|
||||
L"Industro-Goth", L"Jam Band", L"Krautrock", L"Leftfield", L"Lounge", L"Math Rock",
|
||||
L"New Romantic", L"Nu-Breakz", L"Post-Punk", L"Post-Rock", L"Psytrance", L"Shoegaze",
|
||||
L"Space Rock", L"Trop Rock", L"World Music", L"Neoclassical", L"Audiobook",
|
||||
L"Audio Theatre", L"Neue Deutsche Welle", L"Podcast", L"Indie Rock", L"G-Funk",
|
||||
L"Dubstep", L"Garage Rock", L"Psybient", L"Glam Rock", L"Dream Pop", L"Merseybeat",
|
||||
L"K-Pop", L"Chiptune", L"Grime", L"Grindcore", L"Indietronic", L"Indietronica",
|
||||
L"Jazz Rock", L"Jazz Fusion", L"Post-Punk Revival", L"Electronica", L"Psychill",
|
||||
L"Ethnotronic", L"Americana", L"Ambient Dub", L"Digital Dub", L"Chillwave", L"Stoner Rock",
|
||||
L"Slowcore", L"Softcore", L"Flamenco", L"Hi-NRG", L"Ethereal", L"Drone", L"Doom Metal",
|
||||
L"Doom Jazz", L"Mainstream", L"Glitch", L"Balearic", L"Modern Classical", L"Mod",
|
||||
L"Contemporary Classical", L"Psybreaks", L"Psystep", L"Psydub", L"Chillstep", L"Berlin School",
|
||||
L"Future Jazz", L"Djent", L"Musique Concrète", L"Electroacoustic", L"Folktronica", L"Texas Country", L"Red Dirt",
|
||||
L"Arabic", L"Asian", L"Bachata", L"Bollywood", L"Cajun", L"Calypso", L"Creole", L"Darkstep", L"Jewish", L"Reggaeton", L"Smooth Jazz",
|
||||
L"Soca", L"Spiritual", L"Turntablism", L"Zouk", L"Neofolk", L"Nu Jazz",
|
||||
};
|
||||
|
||||
size_t numGenres = sizeof(id3v1_genres)/sizeof(*id3v1_genres);
|
||||
|
||||
ID3v1::ID3v1()
|
||||
{
|
||||
title[0]=0;
|
||||
artist[0]=0;
|
||||
album[0]=0;
|
||||
comment[0]=0;
|
||||
year[0]=0;
|
||||
genre=255;
|
||||
track=0;
|
||||
hasData=false;
|
||||
dirty=false;
|
||||
title[30]=0;
|
||||
artist[30]=0;
|
||||
album[30]=0;
|
||||
comment[30]=0;
|
||||
year[4]=0;
|
||||
}
|
||||
|
||||
int ID3v1::Decode(const void *data)
|
||||
{
|
||||
const char *fbuf = (const char *)data;
|
||||
ptrdiff_t x;
|
||||
|
||||
hasData = false;
|
||||
title[0] = artist[0] = album[0] = year[0] = comment[0] = 0;
|
||||
genre = 255; track = 0;
|
||||
|
||||
if (memcmp(fbuf, "TAG", 3))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
memcpy(title, fbuf + 3, 30); x = 29; while (x >= 0 && title[x] == ' ') x--; title[x + 1] = 0;
|
||||
memcpy(artist, fbuf + 33, 30); x = 29; while (x >= 0 && artist[x] == ' ') x--; artist[x + 1] = 0;
|
||||
memcpy(album, fbuf + 63, 30); x = 29; while (x >= 0 && album[x] == ' ') x--; album[x + 1] = 0;
|
||||
memcpy(year, fbuf + 93, 4); x = 3; while (x >= 0 && year[x] == ' ') x--; year[x + 1] = 0;
|
||||
memcpy(comment, fbuf + 97, 30); x = 29; while (x >= 0 && comment[x] == ' ') x--; comment[x + 1] = 0;
|
||||
if (fbuf[97 + 28] == 0 && fbuf[97 + 28 + 1] != 0) track = fbuf[97 + 28 + 1];
|
||||
genre = ((unsigned char *)fbuf)[127];
|
||||
hasData = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ID3v1::Encode(void *data)
|
||||
{
|
||||
if (!hasData)
|
||||
return 1;
|
||||
char *fbuf = (char *)data;
|
||||
size_t x;
|
||||
fbuf[0] = 'T';fbuf[1] = 'A';fbuf[2] = 'G';
|
||||
if (title) strncpy(fbuf + 3, title, 30); for (x = 3 + strlen(title); x < 33; x ++) fbuf[x] = 0;
|
||||
if (artist) strncpy(fbuf + 33, artist, 30); for (x = 33 + strlen(artist); x < 63; x ++) fbuf[x] = 0;
|
||||
if (album) strncpy(fbuf + 63, album, 30); for (x = 63 + strlen(album); x < 93; x ++) fbuf[x] = 0;
|
||||
if (year) strncpy(fbuf + 93, year, 4); for (x = 93 + strlen(year); x < 97; x ++) fbuf[x] = 0;
|
||||
if (comment) strncpy(fbuf + 97, comment, 30); for (x = 97 + strlen(comment); x < 127; x ++) fbuf[x] = 0;
|
||||
if (track)
|
||||
{
|
||||
fbuf[97 + 28] = 0;
|
||||
fbuf[97 + 28 + 1] = track;
|
||||
}
|
||||
((unsigned char *)fbuf)[127] = genre;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ID3V1_CODEPAGE ((config_read_mode==READ_LOCAL)?CP_ACP:28591)
|
||||
int ID3v1::GetString(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!hasData)
|
||||
return 0;
|
||||
|
||||
if (!_stricmp(tag, "title"))
|
||||
{
|
||||
MultiByteToWideCharSZ(ID3V1_CODEPAGE, 0, title, -1, data, dataLen);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "artist"))
|
||||
{
|
||||
MultiByteToWideCharSZ(ID3V1_CODEPAGE, 0, artist, -1, data, dataLen);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "album"))
|
||||
{
|
||||
MultiByteToWideCharSZ(ID3V1_CODEPAGE, 0, album, -1, data, dataLen);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "comment"))
|
||||
{
|
||||
MultiByteToWideCharSZ(ID3V1_CODEPAGE, 0, comment, -1, data, dataLen);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "year"))
|
||||
{
|
||||
MultiByteToWideCharSZ(ID3V1_CODEPAGE, 0, year, -1, data, dataLen);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "genre"))
|
||||
{
|
||||
if (genre >= numGenres) return -1;
|
||||
StringCchCopyW(data, dataLen, id3v1_genres[genre]);
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "track"))
|
||||
{
|
||||
if (track == 0) return -1;
|
||||
StringCchPrintfW(data, dataLen, L"%u", track);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ID3v1::SetString(const char *tag, const wchar_t *data)
|
||||
{
|
||||
if (!_stricmp(tag, "title"))
|
||||
WideCharToMultiByteSZ(ID3V1_CODEPAGE, 0, data, -1, title, 31, 0 ,0);
|
||||
else if (!_stricmp(tag, "artist"))
|
||||
WideCharToMultiByteSZ(ID3V1_CODEPAGE, 0, data, -1, artist, 31, 0 ,0);
|
||||
else if (!_stricmp(tag, "album"))
|
||||
WideCharToMultiByteSZ(ID3V1_CODEPAGE, 0, data, -1, album, 31, 0 ,0);
|
||||
else if (!_stricmp(tag, "comment"))
|
||||
WideCharToMultiByteSZ(ID3V1_CODEPAGE, 0, data, -1, comment, 31, 0 ,0);
|
||||
else if (!_stricmp(tag, "year"))
|
||||
WideCharToMultiByteSZ(ID3V1_CODEPAGE, 0, data, -1, year, 5, 0 ,0);
|
||||
else if (!_stricmp(tag, "genre"))
|
||||
{
|
||||
genre=255;
|
||||
if (data)
|
||||
{
|
||||
for (size_t i=0;i<numGenres;i++)
|
||||
{
|
||||
if (!_wcsicmp(id3v1_genres[i], data))
|
||||
{
|
||||
genre= (unsigned char)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!_stricmp(tag, "track"))
|
||||
{
|
||||
int t = _wtoi(data);
|
||||
if(t > 255) track = 0;
|
||||
else track = t;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
dirty=true;
|
||||
hasData = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ID3v1::Clear()
|
||||
{
|
||||
hasData=false;
|
||||
dirty=true;
|
||||
//clear data
|
||||
title[0]=artist[0]=album[0]=comment[0]=year[0]=0;
|
||||
genre = track = 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ID3V1_H
|
||||
#define NULLSOFT_IN_MP3_ID3V1_H
|
||||
|
||||
#include <bfc/platform/types.h>
|
||||
class ID3v1
|
||||
{
|
||||
public:
|
||||
ID3v1();
|
||||
int Decode(const void *data);
|
||||
// return -1 for empty, 1 for OK, 0 for "don't understand tag name"
|
||||
int GetString(const char *tag, wchar_t *data, int dataLen);
|
||||
// returns 1 for OK, 0 for "don't understand tag name"
|
||||
int SetString(const char *tag, const wchar_t *data);
|
||||
int Encode(void *data);
|
||||
bool IsDirty() { return dirty; }
|
||||
bool HasData() { return hasData; }
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
char title[31],artist[31],album[31],comment[31];
|
||||
char year[5];
|
||||
unsigned char genre;
|
||||
unsigned char track;
|
||||
|
||||
bool hasData;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,654 @@
|
||||
#include "ID3v2.h"
|
||||
#include "id3.h"
|
||||
#include "config.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "../Agave/AlbumArt/svc_albumArtProvider.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include "../nu/AutoWide.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
static inline const wchar_t *IncSafe(const wchar_t *val, int x)
|
||||
{
|
||||
while (x--)
|
||||
{
|
||||
if (val && *val)
|
||||
val++;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
extern const wchar_t *id3v1_genres[];
|
||||
extern size_t numGenres;
|
||||
|
||||
ID3v2::ID3v2()
|
||||
{
|
||||
hasData=false;
|
||||
dirty=false;
|
||||
}
|
||||
|
||||
int ID3v2::Decode(const void *data, size_t len)
|
||||
{
|
||||
if (!config_parse_id3v2 || !data)
|
||||
{
|
||||
hasData=false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
id3v2.Parse((uchar *)data, (uchar *)data+ID3_TAGHEADERSIZE);
|
||||
if (id3v2.NumFrames() > 0)
|
||||
{
|
||||
hasData=true;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
// return -1 for empty, 1 for OK, 0 for "don't understand tag name"
|
||||
int ID3v2::GetString(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!_stricmp(tag, "title"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_TITLE, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "album"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_ALBUM, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "artist"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_LEADARTIST, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "albumartist"))
|
||||
{
|
||||
if (!ID3_GetTagText(&id3v2, ID3FID_BAND, data, dataLen) && !ID3_GetUserText(&id3v2, L"ALBUM ARTIST", data, dataLen) && !ID3_GetUserText(&id3v2, L"ALBUMARTIST", data, dataLen))
|
||||
return ID3_GetUserText(&id3v2, L"Band", data, dataLen)?1:-1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "comment"))
|
||||
return ID3_GetComment(&id3v2, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "year"))
|
||||
{
|
||||
if (!ID3_GetTagText(&id3v2, ID3FID_RECORDINGTIME, data, dataLen))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_YEAR, data, dataLen)?1:-1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "composer"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_COMPOSER, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "replaygain_track_gain"))
|
||||
return ID3_GetUserText(&id3v2, L"replaygain_track_gain", data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "replaygain_album_gain"))
|
||||
return ID3_GetUserText(&id3v2, L"replaygain_album_gain", data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "replaygain_track_peak"))
|
||||
return ID3_GetUserText(&id3v2, L"replaygain_track_peak", data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "replaygain_album_peak"))
|
||||
return ID3_GetUserText(&id3v2, L"replaygain_album_peak", data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "genre"))
|
||||
{
|
||||
data[0] = 0;
|
||||
if (ID3_GetTagText(&id3v2, ID3FID_CONTENTTYPE, data, dataLen))
|
||||
{
|
||||
wchar_t *tmp = data;
|
||||
while (tmp && *tmp == ' ') tmp++;
|
||||
if (tmp && (*tmp == '(' || (*tmp >= '0' && *tmp <= '9'))) // both (%d) and %d forms
|
||||
{
|
||||
int noparam = 0;
|
||||
if (*tmp == '(') tmp++;
|
||||
else noparam = 1;
|
||||
size_t genre_index = _wtoi(tmp);
|
||||
int cnt = 0;
|
||||
while (tmp && *tmp >= '0' && *tmp <= '9') cnt++, tmp++;
|
||||
while (tmp && *tmp == ' ') tmp++;
|
||||
|
||||
if (tmp && (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0))
|
||||
{
|
||||
if (genre_index < numGenres)
|
||||
StringCchCopyW(data, dataLen, id3v1_genres[genre_index]);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "track"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_TRACKNUM, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "disc"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_PARTINSET, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "bpm"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_BPM, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "rating"))
|
||||
return ID3_GetRating(&id3v2, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "conductor"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_CONDUCTOR, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "key"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_KEY, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "mood"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_MOOD, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "subtitle"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_SUBTITLE, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "lyricist"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_LYRICIST, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "ISRC"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_ISRC, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "media"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_MEDIATYPE, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "remixing"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_MIXARTIST, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "originalartist"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_ORIGARTIST, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "encoder"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_ENCODERSETTINGS, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "publisher"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_PUBLISHER, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "copyright"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_COPYRIGHT, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "compilation"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_COMPILATION, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "url"))
|
||||
return ID3_GetTagUrl(&id3v2, ID3FID_WWWUSER, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "GracenoteFileID"))
|
||||
return ID3_GetGracenoteTagID(&id3v2, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "GracenoteExtData"))
|
||||
{
|
||||
if (!ID3_GetUserText(&id3v2, L"GN_ExtData", data, dataLen))
|
||||
return ID3_GetUserText(&id3v2, L"GN/ExtData", data, dataLen)?1:-1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "tool"))
|
||||
return ID3_GetTagText(&id3v2, ID3FID_ENCODEDBY, data, dataLen)?1:-1;
|
||||
else if (!_stricmp(tag, "pregap"))
|
||||
{
|
||||
data[0] = 0;
|
||||
// first, check for stupid iTunSMPB TXXX frame
|
||||
wchar_t gaps[128] = L"";
|
||||
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||||
if (itr && *itr)
|
||||
{
|
||||
itr = IncSafe(itr, 9);
|
||||
unsigned int prepad = wcstoul(itr, 0, 16);
|
||||
StringCchPrintfW(data, dataLen, L"%u", prepad);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "postgap"))
|
||||
{
|
||||
data[0] = 0;
|
||||
// first, check for stupid iTunSMPB TXXX frame
|
||||
wchar_t gaps[128] = L"";
|
||||
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||||
if (itr && *itr)
|
||||
{
|
||||
itr = IncSafe(itr, 18);
|
||||
unsigned int postpad = wcstoul(itr, 0, 16);
|
||||
StringCchPrintfW(data, dataLen, L"%u", postpad);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "numsamples"))
|
||||
{
|
||||
data[0] = 0;
|
||||
// first, check for stupid iTunSMPB TXXX frame
|
||||
wchar_t gaps[128] = L"";
|
||||
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||||
if (itr && *itr)
|
||||
{
|
||||
itr = IncSafe(itr, 27);
|
||||
unsigned __int64 samples = wcstoul(itr, 0, 16);
|
||||
StringCchPrintfW(data, dataLen, L"%I64u", samples);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "endoffset"))
|
||||
{
|
||||
data[0] = 0;
|
||||
// first, check for stupid iTunSMPB TXXX frame
|
||||
wchar_t gaps[128] = L"";
|
||||
const wchar_t *itr = ID3_GetComment(&id3v2, L"iTunSMPB", gaps, 128);
|
||||
if (itr && *itr)
|
||||
{
|
||||
itr = IncSafe(itr, 53);
|
||||
unsigned __int32 endoffset = wcstoul(itr, 0, 16);
|
||||
StringCchPrintfW(data, dataLen, L"%I32u", endoffset);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "category"))
|
||||
{
|
||||
return ID3_GetTagText(&id3v2, ID3FID_CONTENTGROUP, data, dataLen)?1:-1;
|
||||
}
|
||||
// things generally added by Musicbrainz tagging (either specific or additional)
|
||||
else if (!_stricmp(tag, "acoustid") || !_stricmp(tag, "acoustid_id"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"Acoustid Id", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "acoustid_fingerprint"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"Acoustid Fingerprint", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "asin"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"ASIN", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "barcode"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"BARCODE", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "catalognumber"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"CATALOGNUMBER", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "script"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"SCRIPT", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_recordingid")) // (track id)
|
||||
{
|
||||
return ID3_GetMusicbrainzRecordingID(&id3v2, data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_trackid")) // TODO not working (album track id)
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Release Track Id", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_albumid"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Id", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_artistid"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Artist Id", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_albumartistid"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Artist Id", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_releasestatus") || !_stricmp(tag, "musicbrainz_albumstatus"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Status", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_releasetype") || !_stricmp(tag, "musicbrainz_albumtype"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Type", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_releasecountry") || !_stricmp(tag, "musicbrainz_albumcountry"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Album Release Country", data, dataLen)?1:-1;
|
||||
}
|
||||
else if (!_stricmp(tag, "musicbrainz_releasegroupid") || !_stricmp(tag, "musicbrainz_albumgroupid"))
|
||||
{
|
||||
return ID3_GetUserText(&id3v2, L"MusicBrainz Release Group Id", data, dataLen)?1:-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ID3v2::add_set_latin_id3v2_frame(ID3_FrameID id, const wchar_t *c)
|
||||
{
|
||||
ID3_Frame *f = id3v2.Find(id);
|
||||
if (!c)
|
||||
{
|
||||
if (f)
|
||||
id3v2.RemoveFrame(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
SetFrameEncoding(f, ENCODING_FORCE_ASCII);
|
||||
AutoChar temp(c); //AutoChar temp(c, 28591); // todo: benski> changed back to local to keep old winamp tagged files working
|
||||
f->Field(ID3FN_URL).SetLocal(temp); //f->Field(ID3FN_TEXT).SetLatin(temp);// todo: benski> changed back to local to keep old winamp tagged files working
|
||||
}
|
||||
else
|
||||
{
|
||||
f = new ID3_Frame(id);
|
||||
SetFrameEncoding(f, ENCODING_FORCE_ASCII);
|
||||
AutoChar temp(c); //AutoChar temp(c, 28591); // todo: benski> changed back to local to keep old winamp tagged files working
|
||||
f->Field(ID3FN_URL).SetLocal(temp); //f->Field(ID3FN_TEXT).SetLatin(temp);// todo: benski> changed back to local to keep old winamp tagged files working
|
||||
id3v2.AddFrame(f, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ID3v2::SetString(const char *tag, const wchar_t *data)
|
||||
{
|
||||
if (!_stricmp(tag, "artist"))
|
||||
add_set_id3v2_frame(ID3FID_LEADARTIST, data);
|
||||
else if (!_stricmp(tag, "album"))
|
||||
add_set_id3v2_frame(ID3FID_ALBUM, data);
|
||||
else if (!_stricmp(tag, "albumartist"))
|
||||
{
|
||||
add_set_id3v2_frame(ID3FID_BAND, data);
|
||||
if (!data || !*data) // if we're deleting the field
|
||||
{
|
||||
ID3_AddUserText(&id3v2, L"ALBUM ARTIST", data); // delete this alternate field also, or it's gonna look like it didn't "take" with a fb2k file
|
||||
ID3_AddUserText(&id3v2, L"ALBUMARTIST", data); // delete this alternate field also, or it's gonna look like it didn't "take" with an mp3tag file
|
||||
ID3_AddUserText(&id3v2, L"Band", data); // delete this alternate field also, or it's gonna look like it didn't "take" with an audacity file
|
||||
}
|
||||
}
|
||||
else if (!_stricmp(tag, "comment"))
|
||||
ID3_AddSetComment(&id3v2, data);
|
||||
else if (!_stricmp(tag, "title"))
|
||||
add_set_id3v2_frame(ID3FID_TITLE, data);
|
||||
else if (!_stricmp(tag, "year"))
|
||||
{
|
||||
add_set_id3v2_frame(ID3FID_YEAR, data);
|
||||
if (id3v2.version >= 4) // work around the fact that our id3 code doesn't handle versioning like this too well
|
||||
add_set_id3v2_frame(ID3FID_RECORDINGTIME, data);
|
||||
else
|
||||
add_set_id3v2_frame(ID3FID_RECORDINGTIME, (wchar_t *)0);
|
||||
}
|
||||
else if (!_stricmp(tag, "genre"))
|
||||
add_set_id3v2_frame(ID3FID_CONTENTTYPE, data);
|
||||
else if (!_stricmp(tag, "track"))
|
||||
add_set_id3v2_frame(ID3FID_TRACKNUM, data);
|
||||
else if (!_stricmp(tag, "disc"))
|
||||
add_set_id3v2_frame(ID3FID_PARTINSET, data);
|
||||
else if (!_stricmp(tag, "bpm"))
|
||||
add_set_id3v2_frame(ID3FID_BPM, data);
|
||||
else if (!_stricmp(tag, "rating"))
|
||||
ID3_AddSetRating(&id3v2, data);
|
||||
else if (!_stricmp(tag, "tool"))
|
||||
add_set_id3v2_frame(ID3FID_ENCODEDBY, data);
|
||||
else if (!_stricmp(tag, "composer"))
|
||||
add_set_id3v2_frame(ID3FID_COMPOSER, data);
|
||||
else if (!_stricmp(tag, "replaygain_track_gain"))
|
||||
ID3_AddUserText(&id3v2, L"replaygain_track_gain", data, ENCODING_FORCE_ASCII);
|
||||
else if (!_stricmp(tag, "replaygain_track_peak"))
|
||||
ID3_AddUserText(&id3v2, L"replaygain_track_peak", data, ENCODING_FORCE_ASCII);
|
||||
else if (!_stricmp(tag, "replaygain_album_gain"))
|
||||
ID3_AddUserText(&id3v2, L"replaygain_album_gain", data, ENCODING_FORCE_ASCII);
|
||||
else if (!_stricmp(tag, "replaygain_album_peak"))
|
||||
ID3_AddUserText(&id3v2, L"replaygain_album_peak", data, ENCODING_FORCE_ASCII);
|
||||
else if (!_stricmp(tag, "originalartist"))
|
||||
add_set_id3v2_frame(ID3FID_ORIGARTIST, data);
|
||||
else if (!_stricmp(tag, "encoder"))
|
||||
add_set_id3v2_frame(ID3FID_ENCODERSETTINGS, data);
|
||||
else if (!_stricmp(tag, "publisher"))
|
||||
add_set_id3v2_frame(ID3FID_PUBLISHER, data);
|
||||
else if (!_stricmp(tag, "copyright"))
|
||||
add_set_id3v2_frame(ID3FID_COPYRIGHT, data);
|
||||
else if (!_stricmp(tag, "compilation"))
|
||||
add_set_id3v2_frame(ID3FID_COMPILATION, data);
|
||||
else if (!_stricmp(tag, "remixing"))
|
||||
add_set_id3v2_frame(ID3FID_MIXARTIST, data);
|
||||
else if (!_stricmp(tag, "ISRC"))
|
||||
add_set_id3v2_frame(ID3FID_ISRC, data);
|
||||
else if (!_stricmp(tag, "url"))
|
||||
add_set_latin_id3v2_frame(ID3FID_WWWUSER, data); // TODO: we should %## escape invalid characters
|
||||
//add_set_id3v2_frame(ID3FID_WWWUSER, data);
|
||||
else if (!_stricmp(tag, "GracenoteFileID"))
|
||||
ID3_AddSetGracenoteTagID(&id3v2, data);
|
||||
else if (!_stricmp(tag, "GracenoteExtData"))
|
||||
{
|
||||
ID3_AddUserText(&id3v2, L"GN_ExtData", data, ENCODING_FORCE_ASCII);
|
||||
ID3_AddUserText(&id3v2, L"GN_ExtData",0); // delete this alternate field also
|
||||
}
|
||||
else if (!_stricmp(tag, "category"))
|
||||
add_set_id3v2_frame(ID3FID_CONTENTGROUP, data);
|
||||
else
|
||||
return 0;
|
||||
hasData=true;
|
||||
dirty=true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ID3v2::add_set_id3v2_frame(ID3_FrameID id, const wchar_t *c)
|
||||
{
|
||||
ID3_Frame *f = id3v2.Find(id);
|
||||
if (!c || !*c)
|
||||
{
|
||||
if (f)
|
||||
id3v2.RemoveFrame(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
SetFrameEncoding(f);
|
||||
f->Field(ID3FN_TEXT).SetUnicode(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
f = new ID3_Frame(id);
|
||||
SetFrameEncoding(f);
|
||||
f->Field(ID3FN_TEXT).SetUnicode(c);
|
||||
id3v2.AddFrame(f, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ID3v2::EncodeSize()
|
||||
{
|
||||
if (!hasData)
|
||||
return 0; // simple :)
|
||||
|
||||
return (uint32_t)id3v2.Size();
|
||||
}
|
||||
|
||||
int ID3v2::Encode(const void *data, size_t len)
|
||||
{
|
||||
id3v2.Render((uchar *)data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool NameToAPICType(const wchar_t *name, int &num)
|
||||
{
|
||||
if (!name || !*name) // default to cover
|
||||
num=0x3;
|
||||
else if (!_wcsicmp(name, L"fileicon")) // 32x32 pixels 'file icon' (PNG only)
|
||||
num=0x1;
|
||||
else if (!_wcsicmp(name, L"icon")) // Other file icon
|
||||
num=0x2;
|
||||
else if (!_wcsicmp(name, L"cover")) // Cover (front)
|
||||
num=0x3;
|
||||
else if (!_wcsicmp(name, L"back")) // Cover (back)
|
||||
num=0x4;
|
||||
else if (!_wcsicmp(name, L"leaflet")) // Leaflet page
|
||||
num=0x5;
|
||||
else if (!_wcsicmp(name, L"media")) // Media (e.g. lable side of CD)
|
||||
num=0x6;
|
||||
else if (!_wcsicmp(name, L"leadartist")) //Lead artist/lead performer/soloist
|
||||
num=0x7;
|
||||
else if (!_wcsicmp(name, L"artist")) // Artist/performer
|
||||
num=0x8;
|
||||
else if (!_wcsicmp(name, L"conductor")) // Conductor
|
||||
num=0x9;
|
||||
else if (!_wcsicmp(name, L"band")) // Band/Orchestra
|
||||
num=0xA;
|
||||
else if (!_wcsicmp(name, L"composer")) // Composer
|
||||
num=0xB;
|
||||
else if (!_wcsicmp(name, L"lyricist")) // Lyricist/text writer
|
||||
num=0xC;
|
||||
else if (!_wcsicmp(name, L"location")) // Recording Location
|
||||
num=0xD;
|
||||
else if (!_wcsicmp(name, L"recording")) // During recording
|
||||
num=0xE;
|
||||
else if (!_wcsicmp(name, L"performance")) // During performance
|
||||
num=0xF;
|
||||
else if (!_wcsicmp(name, L"preview")) // Movie/video screen capture
|
||||
num=0x10;
|
||||
else if (!_wcsicmp(name, L"fish")) // A bright coloured fish
|
||||
num=0x11;
|
||||
else if (!_wcsicmp(name, L"illustration")) // Illustration
|
||||
num=0x12;
|
||||
else if (!_wcsicmp(name, L"artistlogo")) // Band/artist logotype
|
||||
num=0x13;
|
||||
else if (!_wcsicmp(name, L"publisherlogo")) // Publisher/Studio logotype
|
||||
num=0x14;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ID3v2::GetAlbumArt(const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
|
||||
{
|
||||
int pictype = 0;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
// try to get our specific picture type
|
||||
ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
|
||||
|
||||
if (!frame && pictype == 3) // if not, just try a generic one
|
||||
{
|
||||
frame = id3v2.Find(ID3FID_PICTURE);
|
||||
/*benski> CUT!
|
||||
if (frame)
|
||||
{
|
||||
ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
|
||||
if (field.Get())
|
||||
frame=0;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (frame)
|
||||
{
|
||||
char *fulltype = ID3_GetString(frame, ID3FN_MIMETYPE);
|
||||
char *type = 0;
|
||||
if (fulltype && *fulltype)
|
||||
{
|
||||
type = strchr(fulltype, '/');
|
||||
}
|
||||
|
||||
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));
|
||||
MultiByteToWideChar(CP_ACP, 0, type2, -1, *mimeType, typelen);
|
||||
free(fulltype);
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to work out a mime type from known 'invalid' values
|
||||
if (fulltype && *fulltype)
|
||||
{
|
||||
if (!strcmpi(fulltype, "png") || !strcmpi(fulltype, "bmp") ||
|
||||
!strcmpi(fulltype, "jpg") || !strcmpi(fulltype, "jpeg") ||
|
||||
!strcmpi(fulltype, "gif"))
|
||||
{
|
||||
int typelen = MultiByteToWideChar(CP_ACP, 0, fulltype, -1, 0, 0);// + 6;
|
||||
*mimeType = (wchar_t*)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
|
||||
MultiByteToWideChar(CP_ACP, 0, fulltype, -1, *mimeType, typelen);
|
||||
CharLowerBuff(*mimeType, typelen);
|
||||
free(fulltype);
|
||||
fulltype = 0;
|
||||
}
|
||||
if (0 != fulltype)
|
||||
{
|
||||
free(fulltype);
|
||||
fulltype = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*mimeType = 0; // unknown!
|
||||
}
|
||||
}
|
||||
|
||||
ID3_Field &field = frame->Field(ID3FN_DATA);
|
||||
*len = field.Size();
|
||||
*bits = WASABI_API_MEMMGR->sysMalloc(*len);
|
||||
field.Get((uchar *)*bits, *len);
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int ID3v2::SetAlbumArt(const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
|
||||
{
|
||||
int pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
// try to get our specific picture type
|
||||
ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
|
||||
|
||||
if (!frame && pictype == 3) // if not, just try a generic one
|
||||
{
|
||||
frame = id3v2.Find(ID3FID_PICTURE);
|
||||
/* benski> cut
|
||||
if (frame)
|
||||
{
|
||||
ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
|
||||
if (field.Get())
|
||||
frame=0;
|
||||
}*/
|
||||
}
|
||||
bool newFrame=false;
|
||||
if (!frame)
|
||||
{
|
||||
frame = new ID3_Frame(ID3FID_PICTURE);
|
||||
newFrame = true;
|
||||
}
|
||||
|
||||
if (frame)
|
||||
{
|
||||
wchar_t mt[32] = {L"image/jpeg"};
|
||||
if (mimeType)
|
||||
{
|
||||
if (wcsstr(mimeType, L"/") != 0)
|
||||
{
|
||||
StringCchCopyW(mt, 32, mimeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfW(mt, 32, L"image/%s", mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
frame->Field(ID3FN_MIMETYPE).SetLatin(AutoChar(mt, 28591));
|
||||
frame->Field(ID3FN_PICTURETYPE).Set(pictype);
|
||||
frame->Field(ID3FN_DESCRIPTION).Clear();
|
||||
frame->Field(ID3FN_DATA).Set((uchar *)bits, len);
|
||||
if (newFrame)
|
||||
id3v2.AddFrame(frame, TRUE);
|
||||
dirty=1;
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
int ID3v2::DeleteAlbumArt(const wchar_t *type)
|
||||
{
|
||||
int pictype;
|
||||
if (NameToAPICType(type, pictype))
|
||||
{
|
||||
// try to get our specific picture type
|
||||
ID3_Frame *frame = id3v2.Find(ID3FID_PICTURE, ID3FN_PICTURETYPE, pictype);
|
||||
|
||||
if (!frame && pictype == 3) // if not, just try a generic one
|
||||
{
|
||||
frame = id3v2.Find(ID3FID_PICTURE);
|
||||
/* benski> cut
|
||||
if (frame)
|
||||
{
|
||||
ID3_Field &field = frame->Field(ID3FN_PICTURETYPE);
|
||||
if (field.Get())
|
||||
frame=0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
if (frame)
|
||||
{
|
||||
id3v2.RemoveFrame(frame);
|
||||
dirty=1;
|
||||
return ALBUMARTPROVIDER_SUCCESS;
|
||||
}
|
||||
}
|
||||
return ALBUMARTPROVIDER_FAILURE;
|
||||
}
|
||||
|
||||
void ID3v2::Clear()
|
||||
{
|
||||
dirty=1;
|
||||
hasData=false;
|
||||
id3v2.Clear();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ID3v2_H
|
||||
#define NULLSOFT_IN_MP3_ID3v2_H
|
||||
|
||||
#include "../id3v2/id3_tag.h"
|
||||
|
||||
class ID3v2
|
||||
{
|
||||
public:
|
||||
ID3v2();
|
||||
bool HasData() { return hasData; }
|
||||
bool IsDirty() { return dirty; }
|
||||
int Decode(const void *data, size_t len);
|
||||
int Encode(const void *data, size_t len);
|
||||
uint32_t EncodeSize();
|
||||
// return -1 for empty, 1 for OK, 0 for "don't understand tag name"
|
||||
int GetString(const char *tag, wchar_t *data, int dataLen);
|
||||
int SetString(const char *tag, const wchar_t *data);
|
||||
|
||||
int GetAlbumArt(const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType);
|
||||
int SetAlbumArt(const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType);
|
||||
int DeleteAlbumArt(const wchar_t *type);
|
||||
|
||||
void Clear();
|
||||
private:
|
||||
void add_set_id3v2_frame(ID3_FrameID id, const wchar_t *c);
|
||||
void add_set_latin_id3v2_frame(ID3_FrameID id, const wchar_t *c);
|
||||
bool hasData;
|
||||
bool dirty;
|
||||
public:
|
||||
ID3_Tag id3v2;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef UNICODE_INPUT_PLUGIN
|
||||
#define UNICODE_INPUT_PLUGIN
|
||||
#endif
|
||||
#include "../Winamp/in2.h"
|
||||
@@ -0,0 +1,398 @@
|
||||
#include "LAMEinfo.h"
|
||||
#include <windows.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include "api__in_mp3.h"
|
||||
#include "resource.h"
|
||||
#include "in2.h"
|
||||
#pragma intrinsic(memcmp)
|
||||
|
||||
extern In_Module mod;
|
||||
|
||||
// Xing header -
|
||||
// 4 Xing
|
||||
// 4 flags
|
||||
// 4 frames
|
||||
// 4 bytes
|
||||
// 100 toc
|
||||
// 4 bytes VBR quality
|
||||
|
||||
// Lame tag
|
||||
// 9 bytes - release name
|
||||
// 11
|
||||
|
||||
// Lame extended info tag
|
||||
|
||||
// http://gabriel.mp3-tech.org/mp3infotag.html
|
||||
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
static int32_t ExtractI4(unsigned char *buf)
|
||||
{
|
||||
int x;
|
||||
// big endian extract
|
||||
|
||||
x = buf[0];
|
||||
x <<= 8;
|
||||
x |= buf[1];
|
||||
x <<= 8;
|
||||
x |= buf[2];
|
||||
x <<= 8;
|
||||
x |= buf[3];
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static int16_t ExtractI2(unsigned char *buf)
|
||||
{
|
||||
int x;
|
||||
// big endian extract
|
||||
|
||||
x = buf[0];
|
||||
x <<= 8;
|
||||
x |= buf[1];
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
const static int bitrateV1L3[] = { 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0};
|
||||
const static int bitrateV1L1[] = { 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, 0};
|
||||
const static int bitrateV1L2[] = { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, 0};
|
||||
const static int bitrateV2L1[] = { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, 0};
|
||||
const static int bitrateV2L2L3[] = { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0};
|
||||
|
||||
const static int sampleRateV1[] = {44100, 48000, 32000, 0};
|
||||
const static int sampleRateV2[] = {22050, 24000, 16000, 0};
|
||||
const static int sampleRateV2_5[] = {11025, 12000, 8000, 0};
|
||||
|
||||
// [mpeg_version][layer]
|
||||
static const int samples_per_frame[4][4] =
|
||||
{
|
||||
// Layer 3, Layer 2, Layer 1
|
||||
{ 0, 576, 1152, 384}, // MPEG2.5
|
||||
{ 0, },
|
||||
{ 0, 576, 1152, 384}, // MPEG2
|
||||
{ 0, 1152, 1152, 384}, // MPEG1
|
||||
};
|
||||
|
||||
void MPEGFrame::ReadBuffer(const unsigned char *buffer)
|
||||
{
|
||||
sync = ((unsigned short)buffer[0] << 3) | (buffer[1] >> 5);
|
||||
mpegVersion = (buffer[1] >> 3) & 3;
|
||||
layer = (buffer[1] >> 1) & 3;
|
||||
protection = (buffer[1]) & 1;
|
||||
bitrateIndex = (buffer[2] >> 4) & 0xF;
|
||||
sampleRateIndex = (buffer[2] >> 2) & 3;
|
||||
paddingBit = (buffer[2] >> 1) & 1;
|
||||
privateBit = buffer[2] & 1;
|
||||
channelMode = (buffer[3] >> 6) & 3;
|
||||
modeExtension = (buffer[3] >> 4) & 3;
|
||||
copyright = (buffer[3] >> 3) & 1;
|
||||
original = (buffer[3] >> 2) & 1;
|
||||
emphasis = (buffer[3]) & 3;
|
||||
}
|
||||
bool MPEGFrame::IsSync()
|
||||
{
|
||||
return sync == 0x07FF
|
||||
&& layer != LayerError
|
||||
&& mpegVersion != MPEG_Error
|
||||
&& bitrateIndex != 15
|
||||
&& bitrateIndex != 0
|
||||
&& sampleRateIndex != 3
|
||||
&& !(mpegVersion == MPEG2 && layer != Layer3)
|
||||
&& !(mpegVersion == MPEG2_5 && layer != Layer3);
|
||||
|
||||
}
|
||||
int MPEGFrame::GetBitrate()
|
||||
{
|
||||
switch (mpegVersion)
|
||||
{
|
||||
case MPEG1:
|
||||
switch (layer)
|
||||
{
|
||||
case Layer1:
|
||||
return bitrateV1L1[bitrateIndex];
|
||||
case Layer2:
|
||||
return bitrateV1L2[bitrateIndex];
|
||||
case Layer3:
|
||||
return bitrateV1L3[bitrateIndex];
|
||||
}
|
||||
break;
|
||||
case MPEG2:
|
||||
case MPEG2_5:
|
||||
switch (layer)
|
||||
{
|
||||
case Layer1:
|
||||
return bitrateV2L1[bitrateIndex];
|
||||
case Layer2:
|
||||
case Layer3:
|
||||
return bitrateV2L2L3[bitrateIndex];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0; // shouldn't get here
|
||||
}
|
||||
int MPEGFrame::GetPadding()
|
||||
{
|
||||
if (paddingBit == NotPadded)
|
||||
return 0;
|
||||
|
||||
if (layer == Layer1)
|
||||
return 4;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
int MPEGFrame::HeaderSize()
|
||||
{
|
||||
if (protection == CRC)
|
||||
return 4 + 2; // 32bits frame header, 16bits CRC
|
||||
else
|
||||
return 4; // 32bits frame ehader
|
||||
}
|
||||
|
||||
int MPEGFrame::GetSampleRate() const
|
||||
{
|
||||
switch(mpegVersion)
|
||||
{
|
||||
case MPEG1: return sampleRateV1[sampleRateIndex];
|
||||
case MPEG2:return sampleRateV2[sampleRateIndex];
|
||||
case MPEG2_5:return sampleRateV2_5[sampleRateIndex];
|
||||
default: return 99999999; // return something that will hopefully cause the framesize to be 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int MPEGFrame::GetSamplesPerFrame() const
|
||||
{
|
||||
return samples_per_frame[mpegVersion][layer];
|
||||
}
|
||||
|
||||
bool MPEGFrame::IsCopyright()
|
||||
{
|
||||
return copyright == 1;
|
||||
}
|
||||
bool MPEGFrame::IsCRC()
|
||||
{
|
||||
return protection == CRC;
|
||||
}
|
||||
|
||||
bool MPEGFrame::IsOriginal()
|
||||
{
|
||||
return original == 1;
|
||||
}
|
||||
|
||||
const char *MPEGFrame::GetEmphasisString()
|
||||
{
|
||||
static char tempGE[32];
|
||||
switch (emphasis)
|
||||
{
|
||||
case Emphasis_None:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_NONE,tempGE,32);
|
||||
case Emphasis_50_15_ms:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_50_15_MICROSEC,tempGE,32);
|
||||
case Emphasis_reserved:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_INVALID,tempGE,32);
|
||||
case Emphasis_CCIT_J_17:
|
||||
return "CITT j.17";
|
||||
default:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_ERROR,tempGE,32);
|
||||
}
|
||||
}
|
||||
|
||||
int MPEGFrame::FrameSize()
|
||||
{
|
||||
if (layer == Layer1)
|
||||
{
|
||||
return (int)floor((48.0f*(float)GetBitrate())/GetSampleRate()) + GetPadding();
|
||||
}
|
||||
else if (layer == Layer2 || layer == Layer3)
|
||||
{
|
||||
if (mpegVersion == MPEG1)
|
||||
return (int)floor((144.0f*(float)GetBitrate())/GetSampleRate()) + GetPadding();
|
||||
else
|
||||
return (int)floor((72.0f*(float)GetBitrate())/GetSampleRate()) + GetPadding();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *MPEGFrame::GetMPEGVersionString()
|
||||
{
|
||||
switch(mpegVersion)
|
||||
{
|
||||
case MPEG1:
|
||||
return "MPEG-1";
|
||||
case MPEG2:
|
||||
return "MPEG-2";
|
||||
case MPEG2_5:
|
||||
return "MPEG-2.5";
|
||||
default:
|
||||
static char tempMF[16];
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_ERROR,tempMF,16);
|
||||
}
|
||||
}
|
||||
|
||||
const char *MPEGFrame::GetChannelModeString()
|
||||
{
|
||||
static char tempGC[32];
|
||||
switch(channelMode)
|
||||
{
|
||||
case Stereo:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_STEREO,tempGC,32);
|
||||
case JointStereo:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_JOINT_STEREO,tempGC,32);
|
||||
case DualChannel:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_2_CHANNEL,tempGC,32);
|
||||
case Mono:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_MONO,tempGC,32);
|
||||
default:
|
||||
return WASABI_API_LNGSTRING_BUF(IDS_ERROR,tempGC,32);
|
||||
}
|
||||
}
|
||||
|
||||
int MPEGFrame::GetLayer()
|
||||
{
|
||||
switch(layer)
|
||||
{
|
||||
case Layer1:
|
||||
return 1;
|
||||
case Layer2:
|
||||
return 2;
|
||||
case Layer3:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int MPEGFrame::GetNumChannels()
|
||||
{
|
||||
switch(channelMode)
|
||||
{
|
||||
case Stereo:
|
||||
return 2;
|
||||
case JointStereo:
|
||||
return 2;
|
||||
case DualChannel:
|
||||
return 2;
|
||||
case Mono:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ReadLAMEinfo(unsigned char *buffer, LAMEinfo *lameInfo)
|
||||
{
|
||||
int flags;
|
||||
MPEGFrame frame;
|
||||
frame.ReadBuffer(buffer);
|
||||
|
||||
if (!frame.IsSync())
|
||||
return 0;
|
||||
|
||||
lameInfo->h_id = frame.mpegVersion & 1;
|
||||
lameInfo->samprate = frame.GetSampleRate();
|
||||
// determine offset of header
|
||||
if (frame.mpegVersion == MPEGFrame::MPEG1) // MPEG 1
|
||||
{
|
||||
if (frame.channelMode == MPEGFrame::Mono)
|
||||
buffer += (17 + 4);//frame.HeaderSize());
|
||||
|
||||
else
|
||||
buffer += (32 + 4);//frame.HeaderSize());
|
||||
}
|
||||
else if (frame.mpegVersion == MPEGFrame::MPEG2) // MPEG 2
|
||||
{
|
||||
if (frame.channelMode == MPEGFrame::Mono)
|
||||
buffer += (9 + 4);//frame.HeaderSize());
|
||||
else
|
||||
buffer += (17 + 4);//frame.HeaderSize());
|
||||
}
|
||||
else if (frame.mpegVersion == MPEGFrame::MPEG2_5) // MPEG 2
|
||||
{
|
||||
if (frame.channelMode == MPEGFrame::Mono)
|
||||
buffer += (9 + 4);//frame.HeaderSize());
|
||||
else
|
||||
buffer += (17 + 4);//frame.HeaderSize());
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "Info", 4))
|
||||
lameInfo->cbr=1;
|
||||
else if (memcmp(buffer, "Xing", 4) && memcmp(buffer, "Lame", 4))
|
||||
return 0;
|
||||
|
||||
buffer += 4; // skip Xing tag
|
||||
flags = lameInfo->flags = ExtractI4(buffer);
|
||||
buffer += 4; // skip flags
|
||||
|
||||
if (flags & FRAMES_FLAG)
|
||||
{
|
||||
lameInfo->frames = ExtractI4(buffer);
|
||||
buffer += 4; // skip frames
|
||||
}
|
||||
if (flags & BYTES_FLAG)
|
||||
{
|
||||
lameInfo->bytes = ExtractI4(buffer);
|
||||
buffer += 4;
|
||||
}
|
||||
if (flags & TOC_FLAG)
|
||||
{
|
||||
if (lameInfo->toc)
|
||||
{
|
||||
for (int i = 0;i < 100;i++)
|
||||
lameInfo->toc[i] = buffer[i];
|
||||
}
|
||||
buffer += 100;
|
||||
}
|
||||
|
||||
lameInfo->vbr_scale = -1;
|
||||
if (flags & VBR_SCALE_FLAG)
|
||||
{
|
||||
lameInfo->vbr_scale = ExtractI4(buffer);
|
||||
buffer += 4;
|
||||
}
|
||||
|
||||
if (!memcmp(buffer, "LAME", 4))
|
||||
{
|
||||
for (int i=0;i<9;i++)
|
||||
lameInfo->lameTag[i]=*buffer++;
|
||||
lameInfo->lameTag[9]=0; // null terminate in case tag used all 20 characters
|
||||
|
||||
lameInfo->encodingMethod = (*buffer++)&0xF; // we'll grab the VBR method
|
||||
lameInfo->lowpass = (*buffer++)*100; // lowpass value
|
||||
lameInfo->peak=*((float *)buffer); // read peak value
|
||||
buffer+=4; // skip peak value
|
||||
|
||||
// read track gain
|
||||
int16_t gain_word = ExtractI2(buffer);
|
||||
if ((gain_word & 0xFC00) == 0x2C00)
|
||||
{
|
||||
lameInfo->replaygain_track_gain = (float)(gain_word & 0x01FF);
|
||||
lameInfo->replaygain_track_gain /= 10;
|
||||
if (gain_word & 0x0200)
|
||||
lameInfo->replaygain_track_gain = -lameInfo->replaygain_track_gain;
|
||||
}
|
||||
buffer+=2;
|
||||
|
||||
// read album gain
|
||||
gain_word = ExtractI2(buffer);
|
||||
if ((gain_word & 0xFC00) == 0x4C00)
|
||||
{
|
||||
lameInfo->replaygain_album_gain = (float)(gain_word & 0x01FF);
|
||||
lameInfo->replaygain_album_gain /= 10;
|
||||
if (gain_word & 0x0200)
|
||||
lameInfo->replaygain_album_gain = -lameInfo->replaygain_album_gain;
|
||||
}
|
||||
buffer+=2;
|
||||
|
||||
buffer+=1; // skip encoding flags + ATH type
|
||||
buffer+=1; // skip bitrate
|
||||
|
||||
// get the encoder delay and padding, annoyingly as 12 bit values packed into 3 bytes
|
||||
lameInfo->encoderDelay = ((unsigned short)buffer[0] << 4) | (buffer[1] >> 4);
|
||||
lameInfo->padding = ((unsigned short)(buffer[1]&0x0F) << 8) | (buffer[2]);
|
||||
}
|
||||
return frame.FrameSize();
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef NULLSOFT_LAMEINFOH
|
||||
#define NULLSOFT_LAMEINFOH
|
||||
|
||||
|
||||
#define FRAMES_FLAG 0x0001
|
||||
#define BYTES_FLAG 0x0002
|
||||
#define TOC_FLAG 0x0004
|
||||
#define VBR_SCALE_FLAG 0x0008
|
||||
|
||||
#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
|
||||
#include <memory.h>
|
||||
#pragma intrinsic(memset)
|
||||
struct LAMEinfo
|
||||
{
|
||||
LAMEinfo()
|
||||
{
|
||||
memset(this, 0, sizeof(LAMEinfo));
|
||||
}
|
||||
|
||||
int cbr; // set to 1 if the file is actually just CBR
|
||||
// Xing
|
||||
int h_id;
|
||||
int samprate; // determined from MPEG header
|
||||
int flags; // from Xing header data
|
||||
int frames; // total bit stream frames from Xing header data
|
||||
int bytes; // total bit stream bytes from Xing header data
|
||||
int vbr_scale; // encoded vbr scale from Xing header data
|
||||
unsigned char *toc; // pointer to unsigned char toc_buffer[100]
|
||||
// may be NULL if toc not desired
|
||||
|
||||
// LAME
|
||||
char lameTag[10]; // 9 characters, but we'll add an extra NULL just in case
|
||||
float peak;
|
||||
float replaygain_album_gain;
|
||||
float replaygain_track_gain;
|
||||
unsigned short lowpass;
|
||||
unsigned short encoderDelay;
|
||||
unsigned short padding;
|
||||
int encodingMethod;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ENCODING_METHOD_LAME = 0,
|
||||
ENCODING_METHOD_CBR = 1,
|
||||
ENCODING_METHOD_ABR = 2,
|
||||
ENCODING_METHOD_VBR1 = 3,
|
||||
ENCODING_METHOD_VBR2 = 4,
|
||||
ENCODING_METHOD_VBR3 = 5,
|
||||
ENCODING_METHOD_VBR4 = 6,
|
||||
ENCODING_METHOD_CBR_2PASS = 8,
|
||||
ENCODING_METHOD_ABR_2PASS = 9,
|
||||
};
|
||||
|
||||
int ReadLAMEinfo(unsigned char *buffer, LAMEinfo *lameInfo);
|
||||
|
||||
class MPEGFrame
|
||||
{
|
||||
public:
|
||||
int GetNumChannels();
|
||||
|
||||
void ReadBuffer(const unsigned char *buffer);
|
||||
bool IsSync();
|
||||
int GetBitrate();
|
||||
int GetPadding();
|
||||
int HeaderSize();
|
||||
int GetSampleRate() const;
|
||||
int FrameSize();
|
||||
const char *GetMPEGVersionString();
|
||||
const char *GetChannelModeString();
|
||||
const char *GetEmphasisString();
|
||||
int GetLayer();
|
||||
bool IsCRC();
|
||||
bool IsCopyright();
|
||||
bool IsOriginal();
|
||||
int MPEGFrame::GetSamplesPerFrame() const;
|
||||
|
||||
enum
|
||||
{
|
||||
NotPadded=0,
|
||||
Padded=1,
|
||||
CRC = 0,
|
||||
NoProtection = 1,
|
||||
Stereo = 0,
|
||||
JointStereo = 1,
|
||||
DualChannel = 2,
|
||||
Mono = 3,
|
||||
MPEG1 = 3,
|
||||
MPEG2 = 2,
|
||||
MPEG_Error = 1,
|
||||
MPEG2_5 = 0,
|
||||
Layer1 = 3,
|
||||
Layer2 = 2,
|
||||
Layer3 = 1,
|
||||
LayerError = 0,
|
||||
Emphasis_None = 0,
|
||||
Emphasis_50_15_ms = 1,
|
||||
Emphasis_reserved = 2,
|
||||
Emphasis_CCIT_J_17 = 3,
|
||||
};
|
||||
|
||||
unsigned int sync:11,
|
||||
mpegVersion:2,
|
||||
layer:2,
|
||||
protection:1,
|
||||
bitrateIndex:4,
|
||||
paddingBit:1,
|
||||
privateBit:1,
|
||||
channelMode:2,
|
||||
modeExtension:2,
|
||||
sampleRateIndex:2,
|
||||
copyright:1,
|
||||
original:1,
|
||||
emphasis:2;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,193 @@
|
||||
#include <windows.h>
|
||||
#include "Lyrics3.h"
|
||||
#include "config.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
// http://www.id3.org/Lyrics3v2
|
||||
|
||||
Lyrics3::Lyrics3()
|
||||
{
|
||||
artist=0;
|
||||
title=0;
|
||||
album=0;
|
||||
hasData=false;
|
||||
dirty=false;
|
||||
}
|
||||
|
||||
Lyrics3::~Lyrics3()
|
||||
{
|
||||
free(artist);
|
||||
free(title);
|
||||
free(album);
|
||||
}
|
||||
|
||||
static wchar_t *CopyField(const uint8_t *buffer, uint32_t size)
|
||||
{
|
||||
int converted = MultiByteToWideChar(28591, 0,(LPCSTR)buffer, size, 0, 0);
|
||||
wchar_t *str = (wchar_t *)calloc((converted+1), sizeof(wchar_t));
|
||||
if (str)
|
||||
{
|
||||
converted = MultiByteToWideChar(28591, 0, (LPCSTR)buffer, size, str, converted);
|
||||
str[converted]=0;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int Lyrics3::Decode(const void *data, size_t datalen)
|
||||
{
|
||||
if (!config_parse_lyrics3)
|
||||
return 1;
|
||||
|
||||
if (memcmp(data, "LYRICSBEGIN", 11) == 0)
|
||||
{
|
||||
hasData = true;
|
||||
|
||||
datalen-=11;
|
||||
uint8_t *buffer = (uint8_t *)data+11;
|
||||
|
||||
while (datalen > 8)
|
||||
{
|
||||
uint8_t fid[4] = {0};
|
||||
uint8_t sizeT[6] = {0};
|
||||
uint32_t size;
|
||||
fid[3] = 0;
|
||||
sizeT[5] = 0;
|
||||
|
||||
memcpy(fid, buffer, 3);
|
||||
buffer+=3; datalen-=3;
|
||||
|
||||
memcpy(sizeT, buffer, 5);
|
||||
buffer+=5; datalen-=5;
|
||||
|
||||
size = strtoul((char *)sizeT, 0, 10);
|
||||
|
||||
if (datalen >= size)
|
||||
{
|
||||
/*if ( memcmp(fid, "IND", 3) == 0) // the IND field
|
||||
{
|
||||
if ( buff2[ posn + 8 + 1 ] == '1')
|
||||
stampsUsed = true;
|
||||
}
|
||||
else */
|
||||
if (memcmp(fid, "ETT", 3) == 0) // the TITLE field
|
||||
{
|
||||
title = CopyField(buffer, size);
|
||||
}
|
||||
else if (strcmp((char *) fid, "EAR") == 0) // the ARTIST field
|
||||
{
|
||||
artist = CopyField(buffer, size);
|
||||
}
|
||||
else if (strcmp((char *) fid, "EAL") == 0) // the ALBUM field
|
||||
{
|
||||
album = CopyField(buffer, size);
|
||||
}
|
||||
/*else if ( strcmp((char *) fid, "LYR") == 0) // the LYRICS field
|
||||
{
|
||||
char *text;
|
||||
luint newSize;
|
||||
|
||||
newSize = ID3_CRLFtoLF((char *) & buff2[ posn + 8 ], size);
|
||||
|
||||
if ( stampsUsed)
|
||||
newSize = ID3_StripTimeStamps((char *) & buff2[ posn + 8 ], newSize);
|
||||
|
||||
if ( text = (char*)malloc(newSize + 1))
|
||||
{
|
||||
text[ newSize ] = 0;
|
||||
|
||||
memcpy( text, &buff2[ posn + 8 ], newSize);
|
||||
|
||||
ID3_AddLyrics( this, text);
|
||||
|
||||
free(text);
|
||||
}
|
||||
else
|
||||
ID3_THROW( ID3E_NoMemory);
|
||||
}*/
|
||||
|
||||
datalen-=size;
|
||||
buffer+=size;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int Lyrics3::GetString(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!hasData)
|
||||
return 0;
|
||||
|
||||
if (!_stricmp(tag, "title"))
|
||||
{
|
||||
if (title && *title)
|
||||
{
|
||||
StringCchCopyW(data, dataLen, title);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "artist"))
|
||||
{
|
||||
if (artist && *artist)
|
||||
{
|
||||
StringCchCopyW(data, dataLen, artist);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "album"))
|
||||
{
|
||||
if (album && *album)
|
||||
{
|
||||
StringCchCopyW(data, dataLen, album);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Lyrics3::SetString(const char *tag, const wchar_t *data)
|
||||
{
|
||||
int ret=0;
|
||||
if (!_stricmp(tag, "title"))
|
||||
{
|
||||
if (title) free(title);
|
||||
title = _wcsdup(data);
|
||||
ret = 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "artist"))
|
||||
{
|
||||
if ( artist ) free(artist);
|
||||
artist = _wcsdup(data);
|
||||
ret = 1;
|
||||
}
|
||||
else if (!_stricmp(tag, "album"))
|
||||
{
|
||||
if ( album ) free(album);
|
||||
album = _wcsdup(data);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if(ret)
|
||||
{
|
||||
hasData=true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Lyrics3::Clear()
|
||||
{
|
||||
free(artist); artist=0;
|
||||
free(album); album=0;
|
||||
free(title); title=0;
|
||||
dirty=true;
|
||||
hasData=false;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef NULLSOFT_IN_MP3_LYRICS3_H
|
||||
#define NULLSOFT_IN_MP3_LYRICS3_H
|
||||
|
||||
#include <bfc/platform/types.h>
|
||||
class Lyrics3
|
||||
{
|
||||
public:
|
||||
Lyrics3();
|
||||
~Lyrics3();
|
||||
bool HasData() { return hasData; }
|
||||
bool IsDirty() { return dirty; }
|
||||
void Clear();
|
||||
void ResetDirty() { dirty=0; };
|
||||
int Decode(const void *data, size_t datalen);
|
||||
// return -1 for empty, 1 for OK, 0 for "don't understand tag name"
|
||||
int GetString(const char *tag, wchar_t *data, int dataLen);
|
||||
int SetString(const char *tag, const wchar_t *data);
|
||||
|
||||
private:
|
||||
bool hasData;
|
||||
bool dirty;
|
||||
wchar_t *title, *album, *artist;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,612 @@
|
||||
#include "main.h"
|
||||
#include "LAMEInfo.h"
|
||||
#include "AACFrame.h"
|
||||
#include "config.h"
|
||||
#include "../winamp/wa_ipc.h"
|
||||
#include <Richedit.h>
|
||||
#include "api__in_mp3.h"
|
||||
#include "FactoryHelper.h"
|
||||
#include <shlwapi.h>
|
||||
#include <strsafe.h>
|
||||
#include <foundation/error.h>
|
||||
|
||||
int fixAACCBRbitrate(int br);
|
||||
|
||||
#if 0
|
||||
/*
|
||||
|
||||
*/
|
||||
/*
|
||||
int MP3Info::remove_id3v1()
|
||||
{
|
||||
char temp[3] = {0, 0, 0};
|
||||
DWORD x;
|
||||
int err = 0;
|
||||
HANDLE hFile = CreateFile(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return 0; //1;
|
||||
}
|
||||
SetFilePointer(hFile, -128, NULL, FILE_END);
|
||||
ReadFile(hFile, temp, 3, &x, NULL);
|
||||
if (!memcmp(temp, "TAG", 3))
|
||||
{
|
||||
SetFilePointer(hFile, -128, NULL, FILE_END);
|
||||
if (!SetEndOfFile(hFile))
|
||||
{
|
||||
err = 1;
|
||||
}
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
if (!err) fbuf[0] = 0;
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
da_tag.SetUnsync(false);
|
||||
da_tag.SetExtendedHeader(true);
|
||||
da_tag.SetCompression(false);
|
||||
da_tag.SetPadding(true);
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
int FindAverageAACBitrate(unsigned __int8 *data, int sizeBytes)
|
||||
{
|
||||
AACFrame aacFrame;
|
||||
aacFrame.ReadBuffer(data);
|
||||
|
||||
if (aacFrame.OK())
|
||||
{
|
||||
int aac_frame_length = aacFrame.frameLength;
|
||||
int no_rawdb = aacFrame.numDataBlocks;
|
||||
|
||||
int fc_tot = aac_frame_length;
|
||||
int fc_cnt = no_rawdb + 1;
|
||||
|
||||
unsigned char *aa = data + aac_frame_length;
|
||||
int tt = sizeBytes - aac_frame_length;
|
||||
while (tt >= 8)
|
||||
{
|
||||
AACFrame nextFrame;
|
||||
nextFrame.ReadBuffer(aa);
|
||||
if (!nextFrame.OK()) break; // error
|
||||
int fcaac_frame_length = nextFrame.frameLength;
|
||||
int fcno_rawdb = nextFrame.numDataBlocks;
|
||||
|
||||
fc_cnt += fcno_rawdb + 1;
|
||||
fc_tot += fcaac_frame_length;
|
||||
|
||||
aa += fcaac_frame_length;
|
||||
tt -= fcaac_frame_length;
|
||||
}
|
||||
|
||||
|
||||
int avg_framesize = fc_tot / (fc_cnt ? fc_cnt : 1);
|
||||
return fixAACCBRbitrate(MulDiv(avg_framesize * 8, aacFrame.GetSampleRate(), 1024 * 1000));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ScanForFrame(CGioFile *file, int *bytesRead)
|
||||
{
|
||||
unsigned char buffer[512] = {0}; /* don't want to read too much, since most MP3's start at 0 */
|
||||
int buflen = 0;
|
||||
|
||||
int checked=0;
|
||||
if (file->Peek(buffer, sizeof(buffer), &buflen) != NErr_Success)
|
||||
return false;
|
||||
unsigned char *b = buffer;
|
||||
while (buflen >= 4)
|
||||
{
|
||||
MPEGFrame frame1;
|
||||
frame1.ReadBuffer(b);
|
||||
if (frame1.IsSync())
|
||||
{
|
||||
if (checked)
|
||||
file->Read(buffer, checked, &buflen);
|
||||
*bytesRead=checked;
|
||||
return true;
|
||||
}
|
||||
|
||||
checked++;
|
||||
buflen--;
|
||||
b++;
|
||||
}
|
||||
if (checked)
|
||||
file->Read(buffer, checked, &buflen);
|
||||
*bytesRead=checked;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mp3sync(CGioFile *file)
|
||||
{
|
||||
unsigned char buffer[1448 + 4] = {0}; /* large enough for one max-size frame and the header of the second */
|
||||
int buflen = 0;
|
||||
|
||||
static const unsigned long gdwHeaderSyncMask = 0xfffe0c00L;
|
||||
unsigned long ulHdr1=0;
|
||||
unsigned long ulHdr2=0;
|
||||
|
||||
int bytesChecked=0;
|
||||
while (bytesChecked<32768)
|
||||
{
|
||||
int bytesRead=0;
|
||||
if (ScanForFrame(file, &bytesRead))
|
||||
{
|
||||
if (file->Peek(buffer, sizeof(buffer), &buflen) != NErr_Success)
|
||||
return false;
|
||||
|
||||
if (buflen >= 4)
|
||||
{
|
||||
MPEGFrame frame1;
|
||||
frame1.ReadBuffer(buffer);
|
||||
if (frame1.IsSync())
|
||||
{
|
||||
ulHdr1 = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
|
||||
int framelength= frame1.FrameSize();
|
||||
if (buflen >= (framelength+4))
|
||||
{
|
||||
unsigned char *b = buffer + framelength;
|
||||
buflen -= frame1.FrameSize();
|
||||
MPEGFrame frame2;
|
||||
frame2.ReadBuffer(b);
|
||||
ulHdr2 = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
|
||||
if (!((ulHdr1 ^ ulHdr2) & gdwHeaderSyncMask) && frame2.IsSync())
|
||||
return true;
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
file->Read(buffer, 1, &buflen);
|
||||
bytesChecked++;
|
||||
}
|
||||
}
|
||||
else if (file->EndOf())
|
||||
return 0;
|
||||
|
||||
bytesChecked+=bytesRead;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const wchar_t *GetMPEGVersionString(int mpegVersion)
|
||||
{
|
||||
switch (mpegVersion)
|
||||
{
|
||||
case MPEGFrame::MPEG1:
|
||||
return L"MPEG-1";
|
||||
case MPEGFrame::MPEG2:
|
||||
return L"MPEG-2";
|
||||
case MPEGFrame::MPEG2_5:
|
||||
return L"MPEG-2.5";
|
||||
default:
|
||||
static wchar_t temp[64];
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,temp,64);
|
||||
}
|
||||
}
|
||||
|
||||
static const wchar_t *GetEmphasisString(int emphasis)
|
||||
{
|
||||
static wchar_t tempE[32];
|
||||
switch (emphasis)
|
||||
{
|
||||
case 0:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_NONE,tempE,32);
|
||||
case 1:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_50_15_MICROSEC,tempE,32);
|
||||
case 2:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_INVALID,tempE,32);
|
||||
case 3:
|
||||
return L"CITT j.17";
|
||||
default:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,tempE,32);
|
||||
}
|
||||
}
|
||||
|
||||
static const wchar_t *GetChannelModeString(int channelMode)
|
||||
{
|
||||
static wchar_t tempM[32];
|
||||
switch (channelMode)
|
||||
{
|
||||
case 0:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_STEREO,tempM,32);
|
||||
case 1:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_JOINT_STEREO,tempM,32);
|
||||
case 2:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_2_CHANNEL,tempM,32);
|
||||
case 3:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_MONO,tempM,32);
|
||||
default:
|
||||
return WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,tempM,32);
|
||||
}
|
||||
}
|
||||
#define INFO_READ_SIZE 32768
|
||||
void GetFileDescription(const wchar_t *file, CGioFile &_file, wchar_t *data, size_t datalen)
|
||||
{
|
||||
int hdroffs = 0;
|
||||
size_t size = datalen;
|
||||
wchar_t *mt = data;
|
||||
wchar_t *ext = PathFindExtension(file);
|
||||
|
||||
wchar_t langbuf[256] = {0};
|
||||
int flen = _file.GetContentLength();
|
||||
StringCchPrintfExW(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_PAYLOAD_SIZE, langbuf, 256), _file.GetContentLength());
|
||||
if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".vlb"))
|
||||
{
|
||||
#if 0 // TODO!
|
||||
if (_wcsicmp(ext, L".vlb")) // aacplus can't do VLB
|
||||
{
|
||||
if (aacPlus)
|
||||
{
|
||||
aacPlus->EasyOpen(AACPLUSDEC_OUTPUTFORMAT_INT16_HOSTENDIAN, 6);
|
||||
AACPLUSDEC_EXPERTSETTINGS *pConf = aacPlus->GetDecoderSettingsHandle();
|
||||
pConf->bEnableOutputLimiter = 1;
|
||||
pConf->bDoUpsampling = 1;
|
||||
aacPlus->SetDecoderSettings();
|
||||
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW_BUF(IDS_FORMAT_AAC, langbuf, 256), &mt, &size, 0);
|
||||
|
||||
char buffer[INFO_READ_SIZE] = {0};
|
||||
int inputRead;
|
||||
_file.Read(buffer, INFO_READ_SIZE, &inputRead);
|
||||
AACPLUSDEC_BITSTREAMBUFFERINFO bitbufInfo = { inputRead, 0, 0};
|
||||
aacPlus->StreamFeed((unsigned char *)buffer, &bitbufInfo);
|
||||
|
||||
unsigned char tempBuf[65536] = {0}; // grr, can't we find a better way to do this?
|
||||
AACPLUSDEC_AUDIOBUFFERINFO audioBufInfo = {65536, 0, 0};
|
||||
aacPlus->StreamDecode(tempBuf, &audioBufInfo, 0, 0);
|
||||
audioBufInfo.nBytesBufferSizeIn -= audioBufInfo.nBytesWrittenOut;
|
||||
aacPlus->StreamDecode(tempBuf + audioBufInfo.nBytesWrittenOut, &audioBufInfo, 0, 0);
|
||||
AACPLUSDEC_STREAMPROPERTIES *streamProperties = aacPlus->GetStreamPropertiesHandle();
|
||||
if (streamProperties->nDecodingState == AACPLUSDEC_DECODINGSTATE_STREAMVERIFIED)
|
||||
{
|
||||
AACPLUSDEC_PROGRAMPROPERTIES *currentProgram = &(streamProperties->programProperties[streamProperties->nCurrentProgram]);
|
||||
|
||||
switch (currentProgram->nStreamType)
|
||||
{
|
||||
case AACPLUSDEC_MPEG2_PROFILE_AACMAIN:
|
||||
StringCchCatEx(mt, size, L"\r\nMPEG-2 AAC", &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_MPEG2_PROFILE_AACLC:
|
||||
if (currentProgram->bProgramSbrEnabled)
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW_BUF(IDS_MPEG2_HE_AAC_IS, langbuf, 256), &mt, &size, 0);
|
||||
else
|
||||
StringCchCatEx(mt, size, L"\r\nMPEG-2 AAC LC", &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_MPEG4_AOT_AACMAIN:
|
||||
StringCchCatEx(mt, size, L"\r\nMPEG-4 AAC", &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_MPEG4_AOT_AACLC:
|
||||
if (currentProgram->bProgramSbrEnabled)
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW_BUF(IDS_MPEG4_HE_AAC_IS, langbuf, 256), &mt, &size, 0);
|
||||
else
|
||||
StringCchCatEx(mt, size, L"\r\nMPEG-4 AAC LC", &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_MPEG4_AOT_SBR:
|
||||
StringCchCatEx(mt, size, L"\r\nMPEG-4 HE-AAC", &mt, &size, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentProgram->nAacSamplingRate != currentProgram->nOutputSamplingRate)
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0,
|
||||
WASABI_API_LNGSTRINGW(IDS_SAMPLE_RATE_OUTPUT),
|
||||
currentProgram->nAacSamplingRate, currentProgram->nOutputSamplingRate);
|
||||
else
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0,
|
||||
WASABI_API_LNGSTRINGW(IDS_SAMPLE_RATE),
|
||||
currentProgram->nAacSamplingRate);
|
||||
int srate = currentProgram->nOutputSamplingRate;
|
||||
|
||||
if (currentProgram->bProgramSbrEnabled)
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_SBR_PRESENT), &mt, &size, 0);
|
||||
else
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_SBR_NOT_PRESENT), &mt, &size, 0);
|
||||
|
||||
if (currentProgram->nAacChannels != currentProgram->nOutputChannels)
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0,
|
||||
WASABI_API_LNGSTRINGW(IDS_CHANNELS_OUTPUT),
|
||||
currentProgram->nAacChannels, currentProgram->nOutputChannels);
|
||||
else
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0,
|
||||
WASABI_API_LNGSTRINGW(IDS_CHANNELS),
|
||||
currentProgram->nAacChannels);
|
||||
switch (currentProgram->nChannelMode)
|
||||
{
|
||||
case AACPLUSDEC_CHANNELMODE_MONO:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_MONO), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_STEREO:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_STEREO), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_PARAMETRIC_STEREO:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_PARAMETRIC_STEREO), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_DUAL_CHANNEL:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_DUAL_CHANNEL), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_4_CHANNEL_2CPE:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_4_CHANNEL_2_CPE), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_4_CHANNEL_MPEG:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_4_CHANNEL_MPEG), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_5_CHANNEL:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_5_CHANNEL), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_5_1_CHANNEL:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_5_1), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_6_1_CHANNEL:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_6_1), &mt, &size, 0);
|
||||
break;
|
||||
case AACPLUSDEC_CHANNELMODE_7_1_CHANNEL:
|
||||
StringCchCatEx(mt, size, WASABI_API_LNGSTRINGW(IDS_MODE_7_1), &mt, &size, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (streamProperties->nBitrate)
|
||||
{
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0,
|
||||
WASABI_API_LNGSTRINGW(IDS_BITRATE),
|
||||
streamProperties->nBitrate);
|
||||
}
|
||||
else
|
||||
{
|
||||
int avg_bitrate = FindAverageAACBitrate((unsigned char *)buffer, inputRead);
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0,
|
||||
WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE),
|
||||
avg_bitrate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!aacPlus)
|
||||
#endif
|
||||
{
|
||||
char buffer[INFO_READ_SIZE] = {0};
|
||||
int inputRead = 0;
|
||||
_file.Read(buffer, INFO_READ_SIZE, &inputRead);
|
||||
|
||||
StringCchCopyEx(mt, size, WASABI_API_LNGSTRINGW(IDS_FORMAT_AAC), &mt, &size, 0);
|
||||
unsigned char *a = (unsigned char *)buffer;
|
||||
while (inputRead-- >= 8)
|
||||
{
|
||||
AACFrame aacFrame;
|
||||
aacFrame.ReadBuffer(a);
|
||||
|
||||
if (aacFrame.OK())
|
||||
{
|
||||
int aac_frame_length = aacFrame.frameLength;
|
||||
int no_rawdb = aacFrame.numDataBlocks;
|
||||
|
||||
/*size_t size = 1024;
|
||||
char *mt = mpeg_description;*/
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW(IDS_HEADER_FOUND_AT_X_BYTES), hdroffs);
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nMPEG-%d AAC", aacFrame.GetMPEGVersion());
|
||||
|
||||
int fc_tot = aac_frame_length;
|
||||
int fc_cnt = no_rawdb + 1;
|
||||
|
||||
unsigned char *aa = a + aac_frame_length;
|
||||
int tt = inputRead - aac_frame_length;
|
||||
while (tt >= 8)
|
||||
{
|
||||
AACFrame nextFrame;
|
||||
nextFrame.ReadBuffer(aa);
|
||||
if (!nextFrame.OK()) break; // error
|
||||
int fcaac_frame_length = nextFrame.frameLength;
|
||||
int fcno_rawdb = nextFrame.numDataBlocks;
|
||||
|
||||
fc_cnt += fcno_rawdb + 1;
|
||||
fc_tot += fcaac_frame_length;
|
||||
|
||||
aa += fcaac_frame_length;
|
||||
tt -= fcaac_frame_length;
|
||||
}
|
||||
|
||||
{
|
||||
int avg_framesize = fc_tot / (fc_cnt ? fc_cnt : 1);
|
||||
int srate = aacFrame.GetSampleRate();
|
||||
int avg_bitrate = fixAACCBRbitrate(MulDiv(avg_framesize * 8, srate, 1024 * 1000));
|
||||
|
||||
int len_s = MulDiv(flen, 1024, avg_framesize * srate);
|
||||
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW(IDS_LENGTH_X_SECONDS), len_s);
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nCBR %d kbps", avg_bitrate);
|
||||
}
|
||||
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW(IDS_PROFILE), aacFrame.GetProfileName());
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\n%dHz %s", aacFrame.GetSampleRate(), aacFrame.GetChannelConfigurationName());
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nCRC: %s", WASABI_API_LNGSTRINGW((aacFrame.protection == 0 ? IDS_YES : IDS_NO)));
|
||||
break;
|
||||
}
|
||||
|
||||
a++;
|
||||
hdroffs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char mp3syncbuf[INFO_READ_SIZE] = {0};
|
||||
int inputRead = 0;
|
||||
|
||||
DWORD start = _file.GetCurrentPosition();
|
||||
// find position of first sync
|
||||
if (!mp3sync(&_file))
|
||||
return;
|
||||
int syncposition = _file.GetCurrentPosition()-start;
|
||||
|
||||
// advance to first sync
|
||||
_file.Peek(mp3syncbuf, INFO_READ_SIZE, &inputRead);
|
||||
|
||||
unsigned int padding = 0;
|
||||
unsigned int encoderDelay = 0;
|
||||
|
||||
MPEGFrame frame;
|
||||
frame.ReadBuffer(mp3syncbuf);
|
||||
|
||||
|
||||
int framelen = frame.FrameSize();
|
||||
|
||||
//const CMp3StreamInfo *info = decoder.GetStreamInfo();
|
||||
//const CMpegHeader *header = decoder.m_Mbs.GetHdr();
|
||||
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_HEADER_FOUND_AT_X_BYTES, langbuf, 256), _file.GetHeaderOffset() + syncposition);
|
||||
if (!padding) padding = _file.postpad;
|
||||
if (!encoderDelay) encoderDelay = _file.prepad;
|
||||
|
||||
if (padding || encoderDelay)
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ENC_DELAY_ZERO_PADDING, langbuf, 256), encoderDelay, padding);
|
||||
|
||||
int is_vbr_lens = _file.m_vbr_ms;
|
||||
int iLen = (is_vbr_lens ? is_vbr_lens/1000 : ((flen * 8) / frame.GetBitrate()));
|
||||
if(iLen > 0)
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_LENGTH_X_SECONDS, langbuf, 256), iLen);
|
||||
else
|
||||
{
|
||||
float fLen = (is_vbr_lens ? is_vbr_lens/1000.0f : ((flen * 8.0f) / frame.GetBitrate()));
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_LENGTH_X_PART_SECONDS, langbuf, 256), fLen);
|
||||
}
|
||||
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_S_LAYER_X, langbuf, 256), GetMPEGVersionString(frame.mpegVersion), frame.GetLayer());
|
||||
|
||||
int frames = _file.m_vbr_frames;
|
||||
|
||||
int is_vbr = _file.m_vbr_flag || _file.m_vbr_hdr;
|
||||
if (!is_vbr || _file.encodingMethod == ENCODING_METHOD_CBR)
|
||||
{
|
||||
if (frames)
|
||||
{
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT, langbuf, 256), frame.GetBitrate() / 1000, frames);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT_APPROX, langbuf, 256), frame.GetBitrate() / 1000, MulDiv(flen, 8, framelen));
|
||||
}
|
||||
}
|
||||
else if (is_vbr && _file.encodingMethod == ENCODING_METHOD_ABR)
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT_ABR, langbuf, 256), _file.GetAvgVBRBitrate(), frames);
|
||||
else
|
||||
{
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_KBIT_VBR, langbuf, 256), _file.GetAvgVBRBitrate(), _file.m_vbr_hdr?L"I":L"", frames);
|
||||
}
|
||||
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_X_HZ_S, langbuf, 256), frame.GetSampleRate(), GetChannelModeString(frame.channelMode));
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, L"\r\nCRC: %s", WASABI_API_LNGSTRINGW_BUF((frame.CRC ? IDS_YES : IDS_NO), langbuf, 256));
|
||||
wchar_t tmp[16] = {0};
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_COPYRIGHTED, langbuf, 256), WASABI_API_LNGSTRINGW_BUF((frame.copyright ? IDS_YES : IDS_NO),tmp,16));
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ORIGINAL, langbuf, 256), WASABI_API_LNGSTRINGW_BUF((frame.original ? IDS_YES : IDS_NO),tmp,16));
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_EMPHASIS, langbuf, 256), GetEmphasisString(frame.emphasis));
|
||||
|
||||
if (_file.m_vbr_frame_len && !_file.lengthVerified)
|
||||
StringCchPrintfEx(mt, size, &mt, &size, 0, WASABI_API_LNGSTRINGW_BUF(IDS_MP3_HAS_BEEN_MODIFIED_NOT_ALL_MAY_BE_CORRECT, langbuf, 256));
|
||||
}
|
||||
}
|
||||
|
||||
void GetAudioInfo(const wchar_t *filename, CGioFile *file, int *len, int *channels, int *bitrate, int *vbr, int *sr)
|
||||
{
|
||||
*bitrate=0;
|
||||
*len=0;
|
||||
*vbr=0;
|
||||
*channels=0;
|
||||
if (file)
|
||||
{
|
||||
wchar_t *ext = PathFindExtension(filename);
|
||||
if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".vlb"))
|
||||
{
|
||||
unsigned char t[INFO_READ_SIZE*2] = {0};
|
||||
unsigned char *a = t;
|
||||
int n = 0;
|
||||
|
||||
if (file->Read(t, sizeof(t), &n) != NErr_Success)
|
||||
return;
|
||||
|
||||
while (n-- >= 8)
|
||||
{
|
||||
AACFrame aacFrame;
|
||||
aacFrame.ReadBuffer(a);
|
||||
|
||||
if (aacFrame.OK())
|
||||
{
|
||||
int aac_frame_length = aacFrame.frameLength;
|
||||
|
||||
int fc_tot = aac_frame_length;
|
||||
int fc_cnt = aacFrame.numDataBlocks + 1;
|
||||
|
||||
unsigned char *aa = a + aac_frame_length;
|
||||
int tt = n - aac_frame_length;
|
||||
while (tt >= 8 && aac_frame_length)
|
||||
{
|
||||
AACFrame nextFrame;
|
||||
nextFrame.ReadBuffer(aa);
|
||||
if (!nextFrame.OK()) break; // error
|
||||
int fcaac_frame_length = nextFrame.frameLength;
|
||||
int fcno_rawdb = nextFrame.numDataBlocks;
|
||||
|
||||
fc_cnt += fcno_rawdb + 1;
|
||||
fc_tot += fcaac_frame_length;
|
||||
|
||||
aa += fcaac_frame_length;
|
||||
tt -= fcaac_frame_length;
|
||||
}
|
||||
|
||||
int avg_framesize = fc_tot / (fc_cnt ? fc_cnt : 1);
|
||||
|
||||
int br = MulDiv(avg_framesize * 8, aacFrame.GetSampleRate(), 1024);
|
||||
*len = MulDiv(file->GetContentLength(), 1024*8, br);
|
||||
*bitrate = fixAACCBRbitrate(br/1000)*1000;
|
||||
|
||||
*sr = aacFrame.GetSampleRate();
|
||||
*channels = aacFrame.GetNumChannels();
|
||||
|
||||
break;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*bitrate = file->GetAvgVBRBitrate()*1000)
|
||||
{
|
||||
*len = file->m_vbr_ms;
|
||||
*vbr = file->m_vbr_flag || file->m_vbr_hdr;
|
||||
}
|
||||
|
||||
if (!mp3sync(file))
|
||||
return;
|
||||
|
||||
unsigned char t[4] = {0};
|
||||
int n = 0;
|
||||
|
||||
if (file->Peek(t, sizeof(t), &n) != NErr_Success)
|
||||
return;
|
||||
|
||||
MPEGFrame frame;
|
||||
frame.ReadBuffer(t);
|
||||
if (frame.IsSync())
|
||||
{
|
||||
if (!*bitrate)
|
||||
{
|
||||
*bitrate = frame.GetBitrate();
|
||||
*len = MulDiv(file->GetContentLength(), 1000*8, *bitrate);
|
||||
}
|
||||
*channels = frame.GetNumChannels();
|
||||
*sr = frame.GetSampleRate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef NULLSOFT_IN_MP3_MP3_INFO_H
|
||||
#define NULLSOFT_IN_MP3_MP3_INFO_H
|
||||
|
||||
#include "Metadata.h"
|
||||
#include <windows.h>
|
||||
|
||||
class MP3Info
|
||||
{
|
||||
public:
|
||||
MP3Info(const wchar_t *fn);
|
||||
|
||||
char mpeg_description[1024];
|
||||
|
||||
bool isOld();
|
||||
|
||||
void get_file_info();
|
||||
int write_id3v1();
|
||||
int remove_id3v1();
|
||||
void display_id3v1(HWND hwndDlg);
|
||||
void get_id3v1_values(HWND hwndDlg);
|
||||
void display_id3v2(HWND hwndDlg);
|
||||
void get_id3v2_values(HWND hwndDlg);
|
||||
void write_id3v2(HWND hwndDlg);
|
||||
void do_enable_id3v1(HWND hwndDlg, int en);
|
||||
void do_enable_id3v2(HWND hwndDlg, int en);
|
||||
|
||||
BOOL CALLBACK id3Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
|
||||
|
||||
int setExtendedFileInfoW(const char *data, wchar_t *val);
|
||||
int writeExtendedFileInfo();
|
||||
|
||||
bool IsMe(const wchar_t *fn)
|
||||
{
|
||||
return !lstrcmpW(file, fn);
|
||||
}
|
||||
protected:
|
||||
// Keep track of file timestamp for file system change notification handling
|
||||
FILETIME last_write_time;
|
||||
|
||||
private:
|
||||
void SetField(const wchar_t *value, wchar_t *&v2, char *v1, size_t v1size);
|
||||
Metadata metadata;
|
||||
wchar_t file[MAX_PATH];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,616 @@
|
||||
#include "Metadata.h"
|
||||
#include "main.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "LAMEInfo.h"
|
||||
#include "AACFrame.h"
|
||||
#include "config.h"
|
||||
#include "LAMEInfo.h"
|
||||
#include <shlwapi.h>
|
||||
#include <assert.h>
|
||||
#include <foundation/error.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#define INFO_READ_SIZE 32768
|
||||
|
||||
Metadata::Metadata( CGioFile *_file, const wchar_t *_filename )
|
||||
{
|
||||
if ( !PathIsURL( _filename ) )
|
||||
filename = _wcsdup( _filename );
|
||||
|
||||
ReadTags( _file );
|
||||
if ( bitrate = _file->GetAvgVBRBitrate() * 1000 )
|
||||
{
|
||||
length_ms = _file->m_vbr_ms;
|
||||
vbr = _file->m_vbr_flag || _file->m_vbr_hdr;
|
||||
}
|
||||
}
|
||||
|
||||
void GetFileDescription(const wchar_t *file, CGioFile &_file, wchar_t *data, size_t datalen);
|
||||
void GetAudioInfo(const wchar_t *filename, CGioFile *file, int *len, int *channels, int *bitrate, int *vbr, int *sr);
|
||||
|
||||
int Metadata::Open(const wchar_t *_filename)
|
||||
{
|
||||
if ( filename && *filename )
|
||||
free( filename );
|
||||
|
||||
filename = _wcsdup(_filename);
|
||||
if (file.Open(filename, INFO_READ_SIZE/1024) != NErr_Success)
|
||||
return 1;
|
||||
|
||||
GetAudioInfo(filename, &file, &length_ms, &channels, &bitrate, &vbr, &sampleRate);
|
||||
ReadTags(&file);
|
||||
file.Close();
|
||||
return METADATA_SUCCESS;
|
||||
}
|
||||
|
||||
Metadata::~Metadata()
|
||||
{
|
||||
if (filename)
|
||||
{
|
||||
free(filename);
|
||||
filename=0;
|
||||
}
|
||||
}
|
||||
|
||||
void Metadata::ReadTags(CGioFile *_file)
|
||||
{
|
||||
// Process ID3v1
|
||||
if (config_parse_id3v1)
|
||||
{
|
||||
void *id3v1_data = _file->GetID3v1();
|
||||
if (id3v1_data)
|
||||
id3v1.Decode(id3v1_data);
|
||||
}
|
||||
|
||||
if (config_parse_id3v2)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
void *id3v2_data = _file->GetID3v2(&len);
|
||||
if (id3v2_data)
|
||||
id3v2.Decode(id3v2_data, len);
|
||||
}
|
||||
|
||||
if (config_parse_lyrics3)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
void *lyrics3_data = _file->GetLyrics3(&len);
|
||||
if (lyrics3_data)
|
||||
lyrics3.Decode(lyrics3_data, len);
|
||||
}
|
||||
|
||||
if (config_parse_apev2)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
void *apev2_data = _file->GetAPEv2(&len);
|
||||
if (apev2_data)
|
||||
apev2.Decode(apev2_data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static int ID3Write(const wchar_t *filename, HANDLE infile, DWORD offset, void *data, DWORD len)
|
||||
{
|
||||
wchar_t tempFile[MAX_PATH] = {0};
|
||||
StringCchCopyW(tempFile, MAX_PATH, filename);
|
||||
PathRemoveExtension(tempFile);
|
||||
StringCchCatW(tempFile, MAX_PATH, L".tmp");
|
||||
|
||||
// check to make sure the filename was actually different!
|
||||
// benski> TODO: we should just try to mangle the filename more rather than totally bail out
|
||||
if (!_wcsicmp(tempFile, filename))
|
||||
return SAVE_ERROR_CANT_OPEN_TEMPFILE;
|
||||
|
||||
// TODO: overlapped I/O
|
||||
HANDLE outfile = CreateFile(tempFile, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
if (outfile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD written=0;
|
||||
if (data && len)
|
||||
WriteFile(outfile, data, len, &written, NULL);
|
||||
SetFilePointer(infile, offset, 0, FILE_BEGIN);
|
||||
|
||||
DWORD read=0;
|
||||
do
|
||||
{
|
||||
char data[4096] = {0};
|
||||
written = read = 0;
|
||||
ReadFile(infile, data, 4096, &read, NULL);
|
||||
if (read) WriteFile(outfile, data, read, &written, NULL);
|
||||
}
|
||||
while (read != 0);
|
||||
CloseHandle(outfile);
|
||||
CloseHandle(infile);
|
||||
if (!MoveFile(tempFile, filename))
|
||||
{
|
||||
if (!CopyFile(tempFile, filename, FALSE))
|
||||
{
|
||||
DeleteFile(tempFile);
|
||||
return SAVE_ERROR_ERROR_OVERWRITING;
|
||||
}
|
||||
DeleteFile(tempFile);
|
||||
}
|
||||
return SAVE_SUCCESS;
|
||||
}
|
||||
return SAVE_ERROR_CANT_OPEN_TEMPFILE;
|
||||
|
||||
}
|
||||
|
||||
bool Metadata::IsDirty()
|
||||
{
|
||||
return id3v1.IsDirty() || id3v2.IsDirty() || lyrics3.IsDirty() || apev2.IsDirty();
|
||||
}
|
||||
|
||||
int Metadata::Save()
|
||||
{
|
||||
if (!IsDirty())
|
||||
return SAVE_SUCCESS;
|
||||
|
||||
int err=SAVE_SUCCESS;
|
||||
if (GetFileAttributes(filename)&FILE_ATTRIBUTE_READONLY)
|
||||
return SAVE_ERROR_READONLY;
|
||||
|
||||
HANDLE metadataFile = CreateFile(filename, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
|
||||
if (metadataFile == INVALID_HANDLE_VALUE)
|
||||
return SAVE_ERROR_OPENING_FILE;
|
||||
|
||||
if (file.Open(filename, INFO_READ_SIZE/1024) != NErr_Success)
|
||||
{
|
||||
CloseHandle(metadataFile);
|
||||
return SAVE_ERROR_OPENING_FILE;
|
||||
}
|
||||
|
||||
bool strippedID3v1=false; // this flag will get set to true when we remove ID3v1 as a side effect of removing APEv2 or Lyrics3 (or ID3v2.4 end-tag if/when we implement)
|
||||
bool strippedLyrics3=false;
|
||||
|
||||
/* Strip APEv2 */
|
||||
if (config_parse_apev2 && config_write_apev2 && apev2.IsDirty())
|
||||
{
|
||||
uint32_t len = 0;
|
||||
void *apev2_data = file.GetAPEv2(&len);
|
||||
if (apev2_data)
|
||||
{
|
||||
uint32_t lyrics3_len = 0;
|
||||
void *lyrics3_data = file.GetLyrics3(&lyrics3_len);
|
||||
if (lyrics3_data)
|
||||
SetFilePointer(metadataFile, -(LONG)(len + 15 + lyrics3_len + (file.GetID3v1()?128:0)), NULL, FILE_END);
|
||||
else
|
||||
SetFilePointer(metadataFile, -(LONG)(len + (file.GetID3v1()?128:0)), NULL, FILE_END);
|
||||
SetEndOfFile(metadataFile);
|
||||
strippedLyrics3=true;
|
||||
strippedID3v1=true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip Lyrics3 tag */
|
||||
if (!strippedLyrics3 && config_parse_lyrics3 && lyrics3.IsDirty())
|
||||
{
|
||||
uint32_t len = 0;
|
||||
void *lyrics3_data = file.GetLyrics3(&len);
|
||||
if (lyrics3_data)
|
||||
{
|
||||
SetFilePointer(metadataFile, -(LONG)(len + 15 + (file.GetID3v1()?128:0)), NULL, FILE_END);
|
||||
SetEndOfFile(metadataFile);
|
||||
strippedID3v1=true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip ID3v1(.1) tag */
|
||||
if (!strippedID3v1 /* if we stripped lyrics3 tag, then we ended up stripping id3v1 also */
|
||||
&& config_parse_id3v1 && config_write_id3v1 && id3v1.IsDirty())
|
||||
{
|
||||
if (file.GetID3v1()) // see if we have ID3v1
|
||||
{
|
||||
SetFilePointer(metadataFile, -128, NULL, FILE_END);
|
||||
SetEndOfFile(metadataFile);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write APEv2 */
|
||||
if (config_parse_apev2 && config_write_apev2 && apev2.IsDirty() && apev2.HasData())
|
||||
{
|
||||
switch(config_apev2_header)
|
||||
{
|
||||
case ADD_HEADER:
|
||||
apev2.SetFlags(APEv2::FLAG_HEADER_HAS_HEADER, APEv2::FLAG_HEADER_HAS_HEADER);
|
||||
break;
|
||||
case REMOVE_HEADER:
|
||||
apev2.SetFlags(0, APEv2::FLAG_HEADER_HAS_HEADER);
|
||||
break;
|
||||
}
|
||||
|
||||
size_t apev2_len = apev2.EncodeSize();
|
||||
void *apev2_data = malloc(apev2_len);
|
||||
if (apev2_data && apev2.Encode(apev2_data, apev2_len) == APEv2::APEV2_SUCCESS)
|
||||
{
|
||||
SetFilePointer(metadataFile, 0, NULL, FILE_END);
|
||||
DWORD bytesWritten=0;
|
||||
WriteFile(metadataFile, apev2_data, (DWORD)apev2_len, &bytesWritten, 0);
|
||||
free(apev2_data);
|
||||
apev2_data = 0;
|
||||
if (bytesWritten != apev2_len)
|
||||
{
|
||||
err=SAVE_APEV2_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(apev2_data);
|
||||
apev2_data = 0;
|
||||
err=SAVE_APEV2_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write Lyrics3 */
|
||||
if (strippedLyrics3) /* if we need to rewrite it because we stripped it (e.g. removing an APEv2 tag)*/
|
||||
{
|
||||
/* since we don't modify lyrics3 (yet) we'll just rewrite the original binary data */
|
||||
uint32_t len = 0;
|
||||
void *lyrics3_data = file.GetLyrics3(&len);
|
||||
if (lyrics3_data)
|
||||
{
|
||||
SetFilePointer(metadataFile, 0, NULL, FILE_END);
|
||||
DWORD bytesWritten=0;
|
||||
WriteFile(metadataFile, lyrics3_data, len, &bytesWritten, NULL);
|
||||
if (bytesWritten != len)
|
||||
{
|
||||
err=SAVE_LYRICS3_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
char temp[7] = {0};
|
||||
StringCchPrintfA(temp, 7, "%06u", len);
|
||||
bytesWritten = 0;
|
||||
WriteFile(metadataFile, temp, 6, &bytesWritten, NULL);
|
||||
if (bytesWritten != 6)
|
||||
{
|
||||
err=SAVE_LYRICS3_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
bytesWritten = 0;
|
||||
WriteFile(metadataFile, "LYRICS200", 9, &bytesWritten, NULL);
|
||||
if (bytesWritten != 9)
|
||||
{
|
||||
err=SAVE_LYRICS3_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write ID3v1 */
|
||||
if (config_parse_id3v1 && config_write_id3v1 && id3v1.IsDirty())
|
||||
{
|
||||
uint8_t id3v1_data[128] = {0};
|
||||
if (id3v1.Encode(id3v1_data) == METADATA_SUCCESS)
|
||||
{
|
||||
SetFilePointer(metadataFile, 0, NULL, FILE_END);
|
||||
DWORD bytesWritten=0;
|
||||
WriteFile(metadataFile, id3v1_data, 128, &bytesWritten, NULL);
|
||||
if (bytesWritten != 128)
|
||||
{
|
||||
err=SAVE_ID3V1_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strippedID3v1)
|
||||
{
|
||||
/** if we stripped lyrics3 or apev2 but didn't modify id3v1 (or are configured not to use it),
|
||||
** we need to rewrite it back to the original data
|
||||
**/
|
||||
void *id3v1_data=file.GetID3v1();
|
||||
if (id3v1_data)
|
||||
{
|
||||
SetFilePointer(metadataFile, 0, NULL, FILE_END);
|
||||
DWORD bytesWritten=0;
|
||||
WriteFile(metadataFile, id3v1_data, 128, &bytesWritten, NULL);
|
||||
if (bytesWritten != 128)
|
||||
{
|
||||
err=SAVE_ID3V1_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write ID3v2 */
|
||||
if (config_parse_id3v2 && config_write_id3v2 && id3v2.IsDirty())
|
||||
{
|
||||
uint32_t oldlen=0;
|
||||
void *old_id3v2_data = file.GetID3v2(&oldlen);
|
||||
id3v2.id3v2.SetPadding(false); // turn off padding to see if we can get away with non re-writing the file
|
||||
uint32_t newlen = id3v2.EncodeSize();
|
||||
if (old_id3v2_data && !newlen) // there's an old tag, but no new tag
|
||||
{
|
||||
err = ID3Write(filename, metadataFile, oldlen, 0, 0);
|
||||
if (err == SAVE_SUCCESS)
|
||||
metadataFile = INVALID_HANDLE_VALUE; // ID3Write returns true if it closed the handle
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
else if (!old_id3v2_data && !newlen) // no old tag, no new tag.. easy :)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
id3v2.id3v2.SetPadding(true);
|
||||
if (newlen <= oldlen) // if we can fit in the old tag
|
||||
{
|
||||
if (oldlen != newlen)
|
||||
id3v2.id3v2.ForcePading(oldlen-newlen); // pad out the rest of the tag
|
||||
else
|
||||
id3v2.id3v2.SetPadding(false);
|
||||
assert(id3v2.EncodeSize() == oldlen);
|
||||
newlen = oldlen;
|
||||
uint8_t *new_id3v2_data = (uint8_t *)calloc(newlen, sizeof(uint8_t));
|
||||
if (new_id3v2_data && id3v2.Encode(new_id3v2_data, newlen) == METADATA_SUCCESS)
|
||||
{
|
||||
// TODO: deal with files with multiple starting id3v2 tags
|
||||
SetFilePointer(metadataFile, 0, NULL, FILE_BEGIN);
|
||||
DWORD bytesWritten=0;
|
||||
WriteFile(metadataFile, new_id3v2_data, newlen, &bytesWritten, NULL);
|
||||
free(new_id3v2_data);
|
||||
new_id3v2_data = 0;
|
||||
if (bytesWritten != newlen)
|
||||
{
|
||||
err = SAVE_ID3V2_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(new_id3v2_data);
|
||||
new_id3v2_data = 0;
|
||||
err = SAVE_ID3V2_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else // otherwise we have to pad out the start
|
||||
{
|
||||
newlen = id3v2.EncodeSize();
|
||||
uint8_t *new_id3v2_data = (uint8_t *)calloc(newlen, sizeof(uint8_t));
|
||||
if (new_id3v2_data && id3v2.Encode(new_id3v2_data, newlen) == METADATA_SUCCESS)
|
||||
{
|
||||
// TODO: deal with files with multiple starting id3v2 tags
|
||||
SetFilePointer(metadataFile, 0, NULL, FILE_BEGIN);
|
||||
DWORD bytesWritten=0;
|
||||
err = ID3Write(filename, metadataFile, oldlen, new_id3v2_data, newlen);
|
||||
free(new_id3v2_data);
|
||||
new_id3v2_data = 0;
|
||||
if (err == SAVE_SUCCESS)
|
||||
metadataFile = INVALID_HANDLE_VALUE; // ID3Write returns true if it closed the handle
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(new_id3v2_data);
|
||||
new_id3v2_data = 0;
|
||||
err = SAVE_ID3V2_WRITE_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
file.Close();
|
||||
if (metadataFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(metadataFile);
|
||||
return err;
|
||||
}
|
||||
|
||||
int Metadata::GetExtendedData(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
int understood=0;
|
||||
switch (id3v2.GetString(tag, data, dataLen))
|
||||
{
|
||||
case -1:
|
||||
data[0]=0;
|
||||
understood=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (apev2.GetString(tag, data, dataLen))
|
||||
{
|
||||
case -1:
|
||||
data[0]=0;
|
||||
understood=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (lyrics3.GetString(tag, data, dataLen))
|
||||
{
|
||||
case -1:
|
||||
data[0]=0;
|
||||
understood=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (id3v1.GetString(tag, data, dataLen))
|
||||
{
|
||||
case -1:
|
||||
data[0]=0;
|
||||
understood=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (GetString(tag, data, dataLen))
|
||||
{
|
||||
case -1:
|
||||
data[0]=0;
|
||||
understood=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return understood;
|
||||
}
|
||||
|
||||
int Metadata::SetExtendedData(const char *tag, const wchar_t *data)
|
||||
{
|
||||
int understood=0;
|
||||
if (config_create_id3v2 || id3v2.HasData())
|
||||
understood |= id3v2.SetString(tag, data);
|
||||
if (config_create_apev2 || apev2.HasData())
|
||||
understood |= apev2.SetString(tag, data);
|
||||
if (config_create_id3v1 || id3v1.HasData())
|
||||
understood |= id3v1.SetString(tag, data);
|
||||
return understood;
|
||||
}
|
||||
|
||||
int Metadata::GetString(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!_stricmp(tag, "formatinformation"))
|
||||
{
|
||||
data[0]=0;
|
||||
if (filename)
|
||||
{
|
||||
if (file.Open(filename, INFO_READ_SIZE/1024) == NErr_Success)
|
||||
GetFileDescription(filename, file, data, dataLen);
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
else if (!_stricmp(tag, "length"))
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%d", length_ms);
|
||||
}
|
||||
else if (!_stricmp(tag, "stereo"))
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%d", channels==2);
|
||||
}
|
||||
else if (!_stricmp(tag, "vbr"))
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%d", vbr);
|
||||
}
|
||||
else if (!_stricmp(tag, "bitrate"))
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%d", bitrate/1000);
|
||||
}
|
||||
else if (!_stricmp(tag, "gain"))
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%-+.2f dB", file.GetGain());
|
||||
}
|
||||
else if (!_stricmp(tag, "pregap"))
|
||||
{
|
||||
if (file.prepad)
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%u", file.prepad);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "postgap"))
|
||||
{
|
||||
if (file.prepad) // yes, we check for this because postpad could legitimately be 0
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%u", file.postpad);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "numsamples"))
|
||||
{
|
||||
if (file.m_vbr_samples)
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%I64u", file.m_vbr_samples);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if (!_stricmp(tag, "endoffset"))
|
||||
{
|
||||
if (file.m_vbr_frames)
|
||||
{
|
||||
int totalFrames = file.m_vbr_frames;
|
||||
if (totalFrames > 8)
|
||||
{
|
||||
int seekPoint = 0;
|
||||
// we're using m_vbr_bytes here instead of file.ContentLength(), because we're already trusting the other LAME header info
|
||||
#define MAX_SIZE_8_FRAMES (1448 * 8) // mp3 frames won't be ever be any bigger than this (320kbps 32000Hz + padding)
|
||||
if (file.m_vbr_bytes > MAX_SIZE_8_FRAMES)
|
||||
seekPoint = (int)(file.m_vbr_bytes - MAX_SIZE_8_FRAMES);
|
||||
else
|
||||
seekPoint = 0;
|
||||
|
||||
size_t offsets[8] = {0};
|
||||
size_t offsetsRead = 0;
|
||||
size_t offsetPosition = 0;
|
||||
|
||||
unsigned char header[6] = {0};
|
||||
MPEGFrame frame;
|
||||
|
||||
if (file.Open(filename, INFO_READ_SIZE/1024) != NErr_Success)
|
||||
return -1;
|
||||
|
||||
// first we need to sync
|
||||
while (1)
|
||||
{
|
||||
file.SetCurrentPosition(seekPoint, CGioFile::GIO_FILE_BEGIN);
|
||||
int read = 0;
|
||||
file.Read(header, 6, &read);
|
||||
if (read != 6)
|
||||
break;
|
||||
frame.ReadBuffer(header);
|
||||
if (frame.IsSync() && frame.GetLayer() == 3)
|
||||
{
|
||||
// make sure this isn't false sync - see if we can get another sync...
|
||||
int nextPoint = seekPoint + frame.FrameSize();
|
||||
file.SetCurrentPosition(nextPoint, CGioFile::GIO_FILE_BEGIN);
|
||||
file.Read(header, 6, &read);
|
||||
if (read != 6) // must be EOF
|
||||
break;
|
||||
frame.ReadBuffer(header);
|
||||
if (frame.IsSync() && frame.GetLayer() == 3)
|
||||
break;
|
||||
}
|
||||
seekPoint++;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
file.SetCurrentPosition(seekPoint, CGioFile::GIO_FILE_BEGIN);
|
||||
int read = 0;
|
||||
file.Read(header, 6, &read);
|
||||
if (read != 6)
|
||||
break;
|
||||
frame.ReadBuffer(header);
|
||||
if (frame.IsSync() && frame.GetLayer() == 3)
|
||||
{
|
||||
offsets[offsetPosition] = seekPoint;
|
||||
offsetPosition = (offsetPosition + 1) % 8;
|
||||
offsetsRead++;
|
||||
seekPoint += frame.FrameSize();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (offsetsRead >= 8)
|
||||
{
|
||||
StringCchPrintfW(data, dataLen, L"%I32d", offsets[offsetPosition] + file.m_vbr_frame_len);
|
||||
file.Close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fixAACCBRbitrate(int br);
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef NULLSOFT_IN_MP3_METADATA
|
||||
#define NULLSOFT_IN_MP3_METADATA
|
||||
|
||||
#include "giofile.h"
|
||||
#include "ID3v1.h"
|
||||
#include "ID3v2.h"
|
||||
#include "Lyrics3.h"
|
||||
#include "apev2.h"
|
||||
|
||||
enum
|
||||
{
|
||||
METADATA_SUCCESS = 0,
|
||||
SAVE_SUCCESS = 0,
|
||||
SAVE_ERROR_OPENING_FILE = 1,
|
||||
SAVE_ID3V1_WRITE_ERROR = 2,
|
||||
SAVE_ID3V2_WRITE_ERROR = 3,
|
||||
SAVE_ERROR_READONLY = 4,
|
||||
SAVE_ERROR_CANT_OPEN_TEMPFILE = 5,
|
||||
SAVE_ERROR_ERROR_OVERWRITING = 6,
|
||||
SAVE_LYRICS3_WRITE_ERROR = 7,
|
||||
SAVE_APEV2_WRITE_ERROR = 8,
|
||||
};
|
||||
|
||||
|
||||
class Metadata
|
||||
{
|
||||
public:
|
||||
Metadata() {}
|
||||
Metadata(CGioFile *_file, const wchar_t *_filename);
|
||||
~Metadata();
|
||||
|
||||
int Open(const wchar_t *filename);
|
||||
int GetExtendedData(const char *tag, wchar_t *data, int dataLen);
|
||||
int SetExtendedData(const char *tag, const wchar_t *data);
|
||||
int Save();
|
||||
bool IsMe(const wchar_t *fn) { return filename && !_wcsicmp(filename, fn); }
|
||||
|
||||
void AddRef() { InterlockedIncrement(&refs); }
|
||||
void Release() { if(!InterlockedDecrement(&refs)) delete this; }
|
||||
|
||||
private:
|
||||
bool IsDirty();
|
||||
void ReadTags(CGioFile *_file);
|
||||
int GetString(const char *tag, wchar_t *data, int dataLen);
|
||||
|
||||
int sampleRate = 0;
|
||||
int bitrate = 0;
|
||||
int vbr = 0;
|
||||
int channels = 0;
|
||||
int length_ms = 0;
|
||||
CGioFile file;
|
||||
|
||||
public:
|
||||
ID3v1 id3v1;
|
||||
ID3v2 id3v2;
|
||||
Lyrics3 lyrics3;
|
||||
APE apev2;
|
||||
|
||||
wchar_t *filename = 0;
|
||||
protected:
|
||||
volatile LONG refs = 1;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "MetadataFactory.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "WasabiMetadata.h"
|
||||
|
||||
static const char serviceName[] = "MP3 Stream Metadata Provider";
|
||||
|
||||
FOURCC MetadataFactory::GetServiceType()
|
||||
{
|
||||
return MP3StreamMetadata::getServiceType();
|
||||
}
|
||||
|
||||
const char *MetadataFactory::GetServiceName()
|
||||
{
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
GUID MetadataFactory::GetGUID()
|
||||
{
|
||||
return MP3StreamMetadataGUID;
|
||||
}
|
||||
|
||||
void *MetadataFactory::GetInterface(int global_lock)
|
||||
{
|
||||
return new MP3StreamMetadata;
|
||||
}
|
||||
|
||||
int MetadataFactory::SupportNonLockingInterface()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int MetadataFactory::ReleaseInterface(void *ifc)
|
||||
{
|
||||
//plugin.service->service_unlock(ifc);
|
||||
svc_metaTag *metadata = static_cast<svc_metaTag *>(ifc);
|
||||
MP3StreamMetadata *mp3metadata = static_cast<MP3StreamMetadata *>(metadata);
|
||||
delete mp3metadata;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *MetadataFactory::GetTestString()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MetadataFactory::ServiceNotify(int msg, int param1, int param2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define CBCLASS MetadataFactory
|
||||
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
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef NULLSOFT_MP3_METADATAFACTORY_H
|
||||
#define NULLSOFT_MP3_METADATAFACTORY_H
|
||||
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include <api/service/services.h>
|
||||
|
||||
class MetadataFactory : 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
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "OFL.h"
|
||||
#include "foundation/error.h"
|
||||
|
||||
static void crcofl(unsigned short crcPoly, unsigned short crcMask, unsigned long *crc, unsigned char byte)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
unsigned short flag = (*crc) & crcMask ? 1:0;
|
||||
flag ^= (byte & 0x80 ? 1 : 0);
|
||||
(*crc)<<=1;
|
||||
byte <<= 1;
|
||||
if(flag)
|
||||
(*crc) ^= crcPoly;
|
||||
}
|
||||
}
|
||||
|
||||
int OFL::GetGaps(size_t *pregap, size_t *postgap)
|
||||
{
|
||||
/* TODO: verify the postgap calculation */
|
||||
if (codec_delay >= 529)
|
||||
{
|
||||
*pregap = codec_delay;
|
||||
size_t endcut;
|
||||
endcut = samples_per_frame - ((total_length + codec_delay) % samples_per_frame); // how many 0 samples had to be added?
|
||||
*postgap = endcut;
|
||||
return NErr_Success;
|
||||
}
|
||||
return NErr_Empty;
|
||||
}
|
||||
|
||||
|
||||
double OFL::GetLengthSeconds() const
|
||||
{
|
||||
return (double)GetSamples() / (double)sample_rate;
|
||||
}
|
||||
|
||||
uint64_t OFL::GetSamples() const
|
||||
{
|
||||
return total_length;
|
||||
}
|
||||
|
||||
uint32_t OFL::GetFrames() const
|
||||
{
|
||||
uint64_t real_samples = (total_length+codec_delay)*samples_per_frame;
|
||||
return (uint32_t) (real_samples/samples_per_frame);
|
||||
}
|
||||
|
||||
int OFL::Read(const MPEGFrame &header, const uint8_t *buffer, size_t buffer_len)
|
||||
{
|
||||
if (header.layer != MPEGFrame::Layer3)
|
||||
return NErr_False;
|
||||
|
||||
sample_rate = header.GetSampleRate();
|
||||
samples_per_frame = header.GetSamplesPerFrame();
|
||||
|
||||
if (header.channelMode == MPEGFrame::Mono)
|
||||
{
|
||||
if (header.mpegVersion == MPEGFrame::MPEG1)
|
||||
{
|
||||
// 0-9 : main_data_end
|
||||
int16_t main_data_end = (buffer[0] << 1) | (buffer[1] >> 7);
|
||||
|
||||
// read the 2 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[2] & 0x3F) << 6) | (buffer[3]>>2); // bits 18-30
|
||||
part2_3_length += ((buffer[9] & 0x7) << 9) | (buffer[10] << 1) | (buffer[11] >> 7) ; // bits 77-89
|
||||
|
||||
size_t offset = 17 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // MPEG2 and 2.5
|
||||
// 0-8 : main_data_end
|
||||
uint16_t main_data_end = buffer[0];
|
||||
|
||||
// read the 2 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[1] & 0x7F) << 5) | (buffer[2]>>3); // bits 9-21
|
||||
|
||||
size_t offset = 9 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (header.mpegVersion == MPEGFrame::MPEG1)
|
||||
{
|
||||
// 0-9 : main_data_end
|
||||
uint16_t main_data_end = (buffer[0] << 1) | (buffer[1] >> 7);
|
||||
|
||||
// read the 4 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[2] & 0xF) << 8) | buffer[3]; // bits 20-32
|
||||
part2_3_length += ((buffer[9] & 0x1) << 11) | (buffer[10] << 3) | (buffer[11] >> 5) ; // bits 79-91
|
||||
part2_3_length += ((buffer[17] & 0x3F) << 6) | (buffer[18] >> 2); // bits 138-150
|
||||
part2_3_length += ((buffer[24] & 0x7) << 9) | (buffer[25] << 1) | (buffer[26] >> 7); // bits 197-209
|
||||
|
||||
size_t offset = 32 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // MPEG2 and 2.5
|
||||
// 0-8 : main_data_end
|
||||
uint16_t main_data_end = buffer[0];
|
||||
|
||||
// read the 4 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[1] & 0x3F) << 6) | (buffer[2] >> 2); // bits 10-22
|
||||
part2_3_length += ((buffer[8] & 0x7) << 9) | (buffer[9] << 1) | (buffer[10] >> 7) ; // bits 69-81
|
||||
|
||||
size_t offset = 17 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NErr_False;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "LAMEInfo.h"
|
||||
#include <bfc/platform/types.h>
|
||||
class OFL
|
||||
{
|
||||
public:
|
||||
int Read(const MPEGFrame &header, const uint8_t *buffer, size_t buffer_len);
|
||||
double GetLengthSeconds() const;
|
||||
uint64_t GetSamples() const;
|
||||
uint32_t GetFrames() const;
|
||||
int GetGaps(size_t *pregap, size_t *postgap);
|
||||
|
||||
private:
|
||||
int samples_per_frame;
|
||||
uint32_t total_length;
|
||||
uint16_t codec_delay;
|
||||
uint16_t additional_delay;
|
||||
|
||||
unsigned int sample_rate;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "RawMediaReader.h"
|
||||
#include <limits.h>
|
||||
|
||||
bool IsMyExtension(const wchar_t *filename);
|
||||
|
||||
int RawMediaReaderService::CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **out_reader)
|
||||
{
|
||||
if (IsMyExtension(filename))
|
||||
{
|
||||
CGioFile *file = new CGioFile();
|
||||
if (!file)
|
||||
return NErr_OutOfMemory;
|
||||
|
||||
if (file->Open(filename, 0) != NErr_Success)
|
||||
{
|
||||
delete file;
|
||||
return NErr_FileNotFound;
|
||||
}
|
||||
|
||||
RawMediaReader *reader = new RawMediaReader(file);
|
||||
if (!reader)
|
||||
{
|
||||
file->Close();
|
||||
delete file;
|
||||
return NErr_OutOfMemory;
|
||||
}
|
||||
|
||||
*out_reader = reader;
|
||||
return NErr_Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NErr_False;
|
||||
}
|
||||
}
|
||||
|
||||
#define CBCLASS RawMediaReaderService
|
||||
START_DISPATCH;
|
||||
CB(CREATERAWMEDIAREADER, CreateRawMediaReader);
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
|
||||
RawMediaReader::RawMediaReader(CGioFile *file) : file(file)
|
||||
{}
|
||||
|
||||
int RawMediaReader::Read( void *buffer, size_t buffer_size, size_t *bytes_read )
|
||||
{
|
||||
if ( buffer_size > INT_MAX )
|
||||
return NErr_BadParameter;
|
||||
|
||||
int file_bytes_read = 0;
|
||||
int ret = file->Read( buffer, (int)buffer_size, &file_bytes_read );
|
||||
|
||||
if ( ret == NErr_Success )
|
||||
{
|
||||
*bytes_read = (size_t)file_bytes_read;
|
||||
if ( !file_bytes_read && file->IsEof() )
|
||||
return NErr_EndOfFile;
|
||||
|
||||
return NErr_Success;
|
||||
}
|
||||
else
|
||||
return NErr_Error;
|
||||
}
|
||||
|
||||
size_t RawMediaReader::Release()
|
||||
{
|
||||
file->Close();
|
||||
|
||||
delete file;
|
||||
file = NULL;
|
||||
|
||||
delete this;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define CBCLASS RawMediaReader
|
||||
START_DISPATCH;
|
||||
CB( RELEASE, Release );
|
||||
CB( RAW_READ, Read );
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "../Agave/DecodeFile/svc_raw_media_reader.h"
|
||||
#include "../Agave/DecodeFile/ifc_raw_media_reader.h"
|
||||
#include "giofile.h"
|
||||
|
||||
// {5EC19CF3-E1ED-4AA5-AFD3-8E93149692BD}
|
||||
static const GUID mpeg_audio_raw_reader_guid =
|
||||
{ 0x5ec19cf3, 0xe1ed, 0x4aa5, { 0xaf, 0xd3, 0x8e, 0x93, 0x14, 0x96, 0x92, 0xbd } };
|
||||
|
||||
class RawMediaReaderService : public svc_raw_media_reader
|
||||
{
|
||||
public:
|
||||
static const char *getServiceName() { return "MPEG-1/2 Audio Raw Reader"; }
|
||||
static GUID getServiceGuid() { return mpeg_audio_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(CGioFile *file);
|
||||
int Read(void *buffer, size_t buffer_size, size_t *bytes_read);
|
||||
size_t Release();
|
||||
protected:
|
||||
RECVS_DISPATCH;
|
||||
private:
|
||||
CGioFile *file;
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "Stopper.h"
|
||||
#include "main.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
|
||||
Stopper::Stopper() : isplaying(0), timems(0)
|
||||
{
|
||||
}
|
||||
|
||||
void Stopper::ChangeTracking(bool mode)
|
||||
{
|
||||
SendMessage(mod.hMainWindow, WM_USER, mode, IPC_ALLOW_PLAYTRACKING); // enable / disable stats updating
|
||||
}
|
||||
|
||||
void Stopper::Stop()
|
||||
{
|
||||
isplaying = (int)SendMessage(mod.hMainWindow, WM_USER, 0, IPC_ISPLAYING);
|
||||
if (isplaying)
|
||||
{
|
||||
ChangeTracking(0); // disable stats updating
|
||||
timems = (int)SendMessage(mod.hMainWindow, WM_USER, 0, IPC_GETOUTPUTTIME);
|
||||
SendMessage(mod.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(mod.hMainWindow, WM_COMMAND, 40045, 0); // Play
|
||||
m_force_seek = -1;
|
||||
if (isplaying & 2)
|
||||
{
|
||||
SendMessage(mod.hMainWindow, WM_COMMAND, 40046, 0); // Pause
|
||||
}
|
||||
ChangeTracking(1); // enable stats updating
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
class Stopper
|
||||
{
|
||||
public:
|
||||
Stopper();
|
||||
void ChangeTracking(bool);
|
||||
void Stop();
|
||||
void Play();
|
||||
int isplaying, timems;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "main.h"
|
||||
#include "MP3Info.h"
|
||||
|
||||
StreamInfo::StreamInfo(void *buffer) : ID3Info()
|
||||
{
|
||||
unsigned __int8 *header = (unsigned __int8 *)buffer;
|
||||
da_tag.Parse(header, &header[ID3_TAGHEADERSIZE]);
|
||||
GetID3V2Values();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "WasabiMetadata.h"
|
||||
#include <shlwapi.h>
|
||||
#include "../nu/AutoChar.h"
|
||||
|
||||
const wchar_t *MP3StreamMetadata::GetName()
|
||||
{
|
||||
return L"MP3 Stream Metadata";
|
||||
}
|
||||
|
||||
GUID MP3StreamMetadata::getGUID()
|
||||
{
|
||||
return MP3StreamMetadataGUID;
|
||||
}
|
||||
|
||||
int MP3StreamMetadata::getFlags()
|
||||
{
|
||||
return METATAG_FILE_INFO;
|
||||
}
|
||||
|
||||
int MP3StreamMetadata::isOurFile(const wchar_t *filename)
|
||||
{
|
||||
if (PathIsURL(filename) && !_wcsicmp(PathFindExtension(filename), L".mp3"))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MP3StreamMetadata::metaTag_open(const wchar_t *filename)
|
||||
{
|
||||
if (metadata.Open(filename) == METADATA_SUCCESS)
|
||||
return METATAG_SUCCESS;
|
||||
else
|
||||
return METATAG_FAILED;
|
||||
}
|
||||
|
||||
void MP3StreamMetadata::metaTag_close()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
int MP3StreamMetadata::getMetaData(const wchar_t *tag, __int8 *buf, int buflenBytes, int datatype)
|
||||
{
|
||||
if (datatype == METATYPE_STRING)
|
||||
{
|
||||
if (metadata.GetExtendedData(AutoChar(tag), (wchar_t *)buf, buflenBytes/sizeof(wchar_t)))
|
||||
return METATAG_SUCCESS;
|
||||
else
|
||||
return METATAG_UNKNOWN_TAG;
|
||||
}
|
||||
else
|
||||
return METATAG_FAILED;
|
||||
}
|
||||
|
||||
#define CBCLASS MP3StreamMetadata
|
||||
START_DISPATCH;
|
||||
CB(SVC_METATAG_GETNAME,getName)
|
||||
CB(SVC_METATAG_GETGUID,getGUID)
|
||||
CB(SVC_METATAG_GETFLAGS,getFlags)
|
||||
CB(SVC_METATAG_ISOURFILE,isOurFile)
|
||||
CB(SVC_METATAG_OPEN,metaTag_open)
|
||||
VCB(SVC_METATAG_CLOSE,metaTag_close)
|
||||
//CB(SVC_METATAG_ENUMTAGS,enumSupportedTag)
|
||||
//CB(SVC_METATAG_GETTAGSIZE,getTagSize)
|
||||
CB(SVC_METATAG_GETMETADATA,getMetaData)
|
||||
//CB(SVC_METATAG_SETMETADATA,setMetaData)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "../Agave/Metadata/svc_metatag.h"
|
||||
#include "Metadata.h"
|
||||
|
||||
// {9937E02D-205B-4964-86A9-F784D9C05F5D}
|
||||
static const GUID MP3StreamMetadataGUID =
|
||||
{ 0x9937e02d, 0x205b, 0x4964, { 0x86, 0xa9, 0xf7, 0x84, 0xd9, 0xc0, 0x5f, 0x5d } };
|
||||
|
||||
class MP3StreamMetadata : public svc_metaTag
|
||||
{
|
||||
private:
|
||||
/* These methods are to be used by api_metadata */
|
||||
const wchar_t *GetName();
|
||||
GUID getGUID(); // this needs to be the same GUID that you use when registering your service factory
|
||||
int getFlags(); // how this service gets its info
|
||||
int isOurFile(const wchar_t *filename);
|
||||
int metaTag_open(const wchar_t *filename);
|
||||
void metaTag_close(); // self-destructs when this is called (you don't need to call serviceFactory->releaseInterface)
|
||||
|
||||
/* user API starts here */
|
||||
const wchar_t *enumSupportedTag(int n, int *datatype = NULL); // returns a list of understood tags. might not be complete (see note [1])
|
||||
int getTagSize(const wchar_t *tag, size_t *sizeBytes); // always gives you BYTES, not characters (be careful with your strings)
|
||||
int getMetaData(const wchar_t *tag, __int8 *buf, int buflenBytes, int datatype = METATYPE_STRING); // buflen is BYTES, not characters (be careful with your strings)
|
||||
int setMetaData(const wchar_t *tag, const __int8 *buf, int buflenBytes, int datatype = METATYPE_STRING);
|
||||
private:
|
||||
Metadata metadata;
|
||||
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,576 @@
|
||||
#include <windows.h>
|
||||
#include "main.h"
|
||||
#if 1
|
||||
BOOL CALLBACK AboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
if (uMsg == WM_COMMAND && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) EndDialog(hwndDlg,0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <commctrl.h>
|
||||
|
||||
#include ".\graphics\image.h"
|
||||
#include ".\graphics\imagefilters.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
|
||||
|
||||
#define random( min, max ) (( rand() % (int)((( max ) + 1 ) - ( min ))) + ( min ))
|
||||
|
||||
HBITMAP LoadImageFromResource(INT_PTR handle);
|
||||
|
||||
#define LEGAL_COUNT 4
|
||||
wchar_t *legal[] = {L"Copyright (C) 1998-2006 - Nullsoft, Inc.",
|
||||
L"MPEG Layer-3 audio compression technology licensed by Fraunhofer IIS and THOMSON multimedia.",
|
||||
L"VLB decoding copyright 1998-2002 by Dolby Laboratories, Inc. All rights reserved.",
|
||||
L"AAC && aacPlus decoding copyright 1998-2006 by Coding Technologies, Inc. All rights reserved."};
|
||||
|
||||
HFONT fntPlugin, fntC, fntLegal;
|
||||
HBRUSH brhDlgBG;
|
||||
|
||||
#define IMAGES_COUNT 6
|
||||
#define IMAGES_INDEX_MY 0
|
||||
#define IMAGES_INDEX_WA 1
|
||||
#define IMAGES_INDEX_CT 2
|
||||
#define IMAGES_INDEX_IIS 3
|
||||
#define IMAGES_INDEX_ID3V2 4
|
||||
#define IMAGES_INDEX_MP3S 5
|
||||
|
||||
RECT rectLogo[IMAGES_COUNT];
|
||||
MLImage *imgLogo[IMAGES_COUNT] = {NULL, NULL, NULL, NULL, NULL, NULL};
|
||||
MLImage *imgLlama = NULL, *imgLlamaOrig = NULL, *imgMy = NULL;
|
||||
RECT rcLlama;
|
||||
|
||||
int idxSelected;
|
||||
|
||||
wchar_t *url[IMAGES_COUNT - 1] = { L"http://winamp.com/",
|
||||
L"http://www.codingtechnologies.com/index.htm",
|
||||
L"http://www.iis.fraunhofer.de/index.html",
|
||||
L"http://www.id3.org/",
|
||||
L"http://www.iis.fraunhofer.de/amm/download/mp3surround/index.html"};
|
||||
|
||||
HWND hwndTT;
|
||||
wchar_t strTT[] = L"click here to visit this website";
|
||||
|
||||
#define TIMER_ID_WATERRENDER 1980
|
||||
#define TIMER_ID_WATERPULSE 1978
|
||||
#define TIMER_DELAY_WATERRENDER 48
|
||||
#define TIMER_DELAY_WATERPULSE 12000
|
||||
#define TIMER_ID_LLAMAFADE 1987
|
||||
#define TIMER_DELAY_LLAMAFADE 200
|
||||
|
||||
#define TIMER_ID_LOADDATA 1959
|
||||
#define TIMER_DELAY_LOADDATA 10
|
||||
MLImage *tmpImage = NULL;
|
||||
|
||||
MLImageFilterWater *fltrWater = NULL;
|
||||
HCURSOR curHand = NULL;
|
||||
|
||||
void about_OnInit(HWND hwndDlg);
|
||||
void about_OnDestroy(HWND hwndDlg);
|
||||
void about_OnMouseDown(HWND hwndDlg, int cx, int cy);
|
||||
void about_OnMouseMove(HWND hwndDlg, int cx, int cy);
|
||||
void about_OnDraw(HWND hwndDlg);
|
||||
void timer_OnWaterPulse(HWND hwndDlg);
|
||||
void timer_OnLlamaFade(HWND hwndDlg);
|
||||
void timer_OnLoadData(HWND hwndDlg);
|
||||
|
||||
|
||||
void SetRects(int cx, int cy)
|
||||
{
|
||||
int i, xl, yl;
|
||||
|
||||
i = IMAGES_INDEX_MY; xl = (cx - imgLogo[i]->GetWidth())/2; yl = 64;
|
||||
if (imgLogo[i]) SetRect(&rectLogo[i], xl, yl, xl + imgLogo[i]->GetWidth(), yl + imgLogo[i]->GetHeight());
|
||||
xl = 2; yl = 2; i = IMAGES_INDEX_WA;
|
||||
if (imgLogo[i]) SetRect(&rectLogo[i], xl, yl, xl + imgLogo[i]->GetWidth(), yl + imgLogo[i]->GetHeight());
|
||||
xl = 2; yl = cy - 88; i = IMAGES_INDEX_CT;
|
||||
if (imgLogo[i]) SetRect(&rectLogo[i], xl, yl, xl + imgLogo[i]->GetWidth(), yl + imgLogo[i]->GetHeight());
|
||||
xl = rectLogo[i].right; i = IMAGES_INDEX_IIS;
|
||||
if (imgLogo[i]) SetRect(&rectLogo[i], xl, yl, xl + imgLogo[i]->GetWidth(), yl + imgLogo[i]->GetHeight());
|
||||
xl = rectLogo[i].right; i = IMAGES_INDEX_ID3V2;
|
||||
if (imgLogo[i]) SetRect(&rectLogo[i], xl, yl, xl + imgLogo[i]->GetWidth(), yl + imgLogo[i]->GetHeight());
|
||||
xl = rectLogo[i].right; i = IMAGES_INDEX_MP3S;
|
||||
if (imgLogo[i]) SetRect(&rectLogo[i], xl, yl, xl + imgLogo[i]->GetWidth(), yl + imgLogo[i]->GetHeight());
|
||||
|
||||
if(imgLlama) SetRect(&rcLlama, 2, 1, imgLlama->GetWidth() + 2, imgLlama->GetHeight() + 1);
|
||||
}
|
||||
void CreateToolTipWnd(HWND hwndDlg)
|
||||
{
|
||||
INITCOMMONCONTROLSEX iccex;
|
||||
iccex.dwICC = ICC_WIN95_CLASSES;
|
||||
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
||||
InitCommonControlsEx(&iccex);
|
||||
|
||||
hwndTT = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL, mod.hDllInstance, NULL);
|
||||
|
||||
SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
void UpdateToolTips(HWND hwndDlg)
|
||||
{
|
||||
TOOLINFOW ti;
|
||||
unsigned int uid = 0;
|
||||
|
||||
// delete tools
|
||||
int ttCount = SendMessage(hwndTT, TTM_GETTOOLCOUNT, 0, 0);
|
||||
for(int i = 0; i < ttCount; i++)
|
||||
{
|
||||
if (SendMessageW(hwndTT, TTM_ENUMTOOLSW, (WPARAM)i, (LPARAM)&ti)) SendMessageW(hwndTT, TTM_DELTOOLW, 0, (LPARAM)&ti);
|
||||
}
|
||||
|
||||
/// add tools
|
||||
|
||||
for (int i = 1; i < IMAGES_COUNT -1; i++)
|
||||
{
|
||||
ti.cbSize = sizeof(TOOLINFO);
|
||||
ti.uFlags = TTF_SUBCLASS;
|
||||
ti.hwnd = hwndDlg;
|
||||
ti.hinst = mod.hDllInstance;
|
||||
ti.uId = uid;
|
||||
ti.lpszText = strTT;
|
||||
ti.rect = rectLogo[i];
|
||||
SendMessageW(hwndTT, TTM_ADDTOOLW, 0, (LPARAM) (LPTOOLINFO) &ti);
|
||||
}
|
||||
}
|
||||
BOOL CALLBACK AboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
about_OnInit(hwndDlg);
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
about_OnDestroy(hwndDlg);
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
|
||||
{
|
||||
EndDialog(hwndDlg,0);
|
||||
}
|
||||
break;
|
||||
case WM_SIZE:
|
||||
if (wParam != SIZE_MINIMIZED)
|
||||
{
|
||||
SetRects(LOWORD(lParam), HIWORD(lParam));
|
||||
UpdateToolTips(hwndDlg);
|
||||
}
|
||||
case WM_MOUSEMOVE:
|
||||
about_OnMouseMove(hwndDlg, LOWORD(lParam), HIWORD(lParam));
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
about_OnMouseDown(hwndDlg, LOWORD(lParam), HIWORD(lParam));
|
||||
break;
|
||||
case WM_PAINT:
|
||||
about_OnDraw(hwndDlg);
|
||||
break;
|
||||
case WM_TIMER:
|
||||
switch(wParam)
|
||||
{
|
||||
case TIMER_ID_WATERRENDER:
|
||||
if (idxSelected != -1 && fltrWater)
|
||||
{
|
||||
fltrWater->Render(tmpImage, imgLogo[idxSelected]);
|
||||
InvalidateRect(hwndDlg, &rectLogo[idxSelected], FALSE);
|
||||
}
|
||||
break;
|
||||
case TIMER_ID_WATERPULSE:
|
||||
timer_OnWaterPulse(hwndDlg);
|
||||
break;
|
||||
case TIMER_ID_LLAMAFADE:
|
||||
timer_OnLlamaFade(hwndDlg);
|
||||
break;
|
||||
case TIMER_ID_LOADDATA:
|
||||
timer_OnLoadData(hwndDlg);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_CTLCOLORDLG:
|
||||
return (BOOL)brhDlgBG;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void about_OnInit(HWND hwndDlg)
|
||||
{
|
||||
HDC hdc = GetDC(hwndDlg);
|
||||
fntPlugin = CreateFontW(-MulDiv(20, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Microsoft Sans Serif");
|
||||
fntC = CreateFontW(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, 0, L"Arial");
|
||||
fntLegal = CreateFontW(-MulDiv(6, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, 0, L"Microsoft Sans Serif");
|
||||
ReleaseDC(hwndDlg, hdc);
|
||||
|
||||
brhDlgBG = CreateSolidBrush(RGB(255,255,255));
|
||||
|
||||
SetTimer(hwndDlg, TIMER_ID_LOADDATA, TIMER_DELAY_LOADDATA, NULL);
|
||||
}
|
||||
|
||||
void about_OnDestroy(HWND hwndDlg)
|
||||
{
|
||||
for (int i = 0; i < IMAGES_COUNT; i++)
|
||||
{
|
||||
if (imgLogo[i]) delete(imgLogo[i]);
|
||||
imgLogo[i] = NULL;
|
||||
}
|
||||
|
||||
if (imgLlama) delete(imgLlama);
|
||||
imgLlama = NULL;
|
||||
|
||||
if (imgLlamaOrig) delete(imgLlamaOrig);
|
||||
imgLlamaOrig = NULL;
|
||||
|
||||
if (imgMy) delete(imgMy);
|
||||
imgMy = NULL;
|
||||
|
||||
if (fntPlugin) DeleteObject(fntPlugin);
|
||||
if (fntC) DeleteObject(fntC);
|
||||
if (fntLegal) DeleteObject(fntLegal);
|
||||
fntPlugin = NULL;
|
||||
fntC = NULL;
|
||||
fntLegal = NULL;
|
||||
|
||||
if (brhDlgBG) DeleteObject(brhDlgBG);
|
||||
brhDlgBG = NULL;
|
||||
|
||||
if (fltrWater) delete (fltrWater);
|
||||
fltrWater = NULL;
|
||||
|
||||
if (tmpImage) delete(tmpImage);
|
||||
tmpImage = NULL;
|
||||
|
||||
if (curHand) DestroyCursor(curHand);
|
||||
curHand = NULL;
|
||||
}
|
||||
|
||||
void about_OnMouseDown(HWND hwndDlg, int cx, int cy)
|
||||
{
|
||||
|
||||
if (idxSelected == -1) return;
|
||||
|
||||
HCURSOR curWait = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(32514/*OCR_WAIT*/), IMAGE_CURSOR, 0, 0, LR_DEFAULTCOLOR);
|
||||
SetCursor(curWait);
|
||||
ShellExecuteW(hwndDlg, L"open", url[idxSelected -1], NULL, L"c:\\", SW_SHOW);
|
||||
SetCursor(curHand);
|
||||
DestroyCursor(curWait);
|
||||
}
|
||||
void about_OnMouseMove(HWND hwndDlg, int cx, int cy)
|
||||
{
|
||||
POINT pt = {cx, cy};
|
||||
int idxNew = -1;
|
||||
for (int i = 1; i < IMAGES_COUNT - 1; i ++)
|
||||
{
|
||||
if (PtInRect(&rectLogo[i], pt))
|
||||
{
|
||||
if (!curHand)
|
||||
{
|
||||
curHand = (HCURSOR)LoadImage(mod.hDllInstance, MAKEINTRESOURCE(IDC_CUR_HAND), IMAGE_CURSOR, 0, 0, LR_DEFAULTCOLOR);
|
||||
}
|
||||
SetCursor(curHand);
|
||||
idxNew = i;
|
||||
}
|
||||
}
|
||||
if (idxNew != idxSelected)
|
||||
{
|
||||
// stop animation
|
||||
KillTimer(hwndDlg, TIMER_ID_WATERPULSE);
|
||||
KillTimer(hwndDlg, TIMER_ID_WATERRENDER);
|
||||
|
||||
if (idxSelected != -1) // redraw previously animated
|
||||
{
|
||||
InvalidateRect(hwndDlg, &rectLogo[idxSelected], FALSE);
|
||||
}
|
||||
// set new one
|
||||
idxSelected = idxNew;
|
||||
if (fltrWater) delete(fltrWater);
|
||||
fltrWater = NULL;
|
||||
if (tmpImage) delete(tmpImage);
|
||||
tmpImage = NULL;
|
||||
if (idxSelected != -1 && idxSelected != IMAGES_INDEX_WA) SetTimer(hwndDlg, TIMER_ID_WATERPULSE, 30, NULL); // start delay
|
||||
}
|
||||
}
|
||||
void about_OnDraw(HWND hwndDlg)
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc;
|
||||
hdc = BeginPaint(hwndDlg, &ps);
|
||||
RECT rc, ri;
|
||||
GetClientRect(hwndDlg, &rc);
|
||||
|
||||
// Draw Llama
|
||||
RECT rl;
|
||||
SetRect(&rl, rcLlama.left, rcLlama.top, rcLlama.right, rcLlama.bottom);
|
||||
if (imgLlama && IntersectRect(&ri, &rl, &ps.rcPaint))
|
||||
{
|
||||
HRGN hrgn = CreateRectRgn(rcLlama.left, rcLlama.top, rcLlama.right, rcLlama.bottom);
|
||||
|
||||
HRGN hrgn1 = CreateRectRgn(rectLogo[IMAGES_INDEX_MY].left,
|
||||
rectLogo[IMAGES_INDEX_MY].top,
|
||||
rectLogo[IMAGES_INDEX_MY].right,
|
||||
rectLogo[IMAGES_INDEX_MY].bottom);
|
||||
CombineRgn(hrgn, hrgn, hrgn1, RGN_DIFF);
|
||||
DeleteObject(hrgn1);
|
||||
hrgn1 = CreateRectRgn(rectLogo[IMAGES_INDEX_WA].left,
|
||||
rectLogo[IMAGES_INDEX_WA].top,
|
||||
rectLogo[IMAGES_INDEX_WA].right,
|
||||
rectLogo[IMAGES_INDEX_WA].bottom);
|
||||
CombineRgn(hrgn, hrgn, hrgn1, RGN_DIFF);
|
||||
SelectClipRgn(hdc, hrgn);
|
||||
DeleteObject(hrgn);
|
||||
DeleteObject(hrgn1);
|
||||
|
||||
imgLlama->Draw(hdc, rl.left, rl.top);
|
||||
SelectClipRgn(hdc, NULL);
|
||||
}
|
||||
|
||||
MLImage *img;
|
||||
for (int i = 0; i < IMAGES_COUNT -1; i++)
|
||||
{
|
||||
|
||||
if (IntersectRect(&ri, &rectLogo[i], &ps.rcPaint))
|
||||
{
|
||||
if (idxSelected == i && tmpImage)
|
||||
{
|
||||
img = tmpImage;
|
||||
HRGN hrgn = CreateRectRgn(rectLogo[i].left + 3, rectLogo[i].top + 3 , rectLogo[i].right - 3, rectLogo[i].bottom - 3);
|
||||
SelectClipRgn(hdc, hrgn);
|
||||
DeleteObject(hrgn);
|
||||
}
|
||||
else img = imgLogo[i];
|
||||
if (img == NULL || imgLlama == NULL) continue;
|
||||
if (i == IMAGES_INDEX_MY)
|
||||
{ // blend Llama First
|
||||
MLImageFilter_Blend1(imgMy, img, 0, 0,
|
||||
imgLlama->GetWidth() - (rectLogo[i].left - rcLlama.left), img->GetHeight(), imgLlama,
|
||||
rectLogo[i].left - rcLlama.left, rectLogo[i].top - rcLlama.top, RGB(255,255,255));
|
||||
img = imgMy;
|
||||
}
|
||||
img->Draw(hdc, rectLogo[i].left, rectLogo[i].top);
|
||||
SelectClipRgn(hdc, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
RECT rt;
|
||||
SetBkMode(hdc, TRANSPARENT);
|
||||
rc.left += 6;
|
||||
rc.bottom -= 2;
|
||||
rc.right -= 4;
|
||||
|
||||
HFONT oldF = NULL;
|
||||
|
||||
SetRect(&rt, rc.right - 192, rc.top, rc.right, rc.top + 16);
|
||||
if (IntersectRect(&ri, &rt, &ps.rcPaint))
|
||||
{
|
||||
/// Nullsoft (c)
|
||||
HFONT oldF = (HFONT)SelectObject(hdc, fntC);
|
||||
SetTextColor(hdc, RGB(100,100,200));
|
||||
DrawTextW(hdc, legal[0], -1, &rt, DT_LEFT | DT_SINGLELINE);
|
||||
}
|
||||
|
||||
SetRect(&rt, rc.left - 2, rc.bottom - 33, rc.right, rc.bottom);
|
||||
if (IntersectRect(&ri, &rt, &ps.rcPaint))
|
||||
{
|
||||
/// Separator
|
||||
MoveToEx(hdc, rt.left, rt.top, NULL);
|
||||
HPEN lp = CreatePen(PS_SOLID,1, RGB(190, 190, 190));
|
||||
HPEN op = (HPEN)SelectObject(hdc, lp);
|
||||
LineTo(hdc, rt.right, rt.top);
|
||||
SelectObject(hdc, lp);
|
||||
DeleteObject(lp);
|
||||
|
||||
/// Legal...
|
||||
oldF = (oldF != NULL) ? oldF : (HFONT)SelectObject(hdc, fntLegal);
|
||||
SetTextColor(hdc, RGB(0,0,0));
|
||||
for(int i = 1; i < LEGAL_COUNT; i++)
|
||||
{
|
||||
int y = rc.bottom - (LEGAL_COUNT - i)*10;
|
||||
SetRect(&rt, rc.left, y, rc.right, y +10);
|
||||
if (IntersectRect(&ri, &rt, &ps.rcPaint))
|
||||
DrawTextW(hdc, legal[i], -1, &rt, DT_LEFT | DT_SINGLELINE);
|
||||
}
|
||||
}
|
||||
if (oldF != NULL) SelectObject(hdc, oldF);
|
||||
EndPaint(hwndDlg, &ps);
|
||||
}
|
||||
void timer_OnLoadData(HWND hwndDlg)
|
||||
{
|
||||
static step = 0;
|
||||
KillTimer(hwndDlg, TIMER_ID_LOADDATA);
|
||||
RECT rc;
|
||||
GetClientRect(hwndDlg, &rc);
|
||||
for (int i = 0; i < IMAGES_COUNT; i++)
|
||||
{
|
||||
imgLogo[i] = new MLImage(LoadImageFromResource, TRUE);
|
||||
imgLogo[i]->Load();
|
||||
}
|
||||
|
||||
imgMy = new MLImage();
|
||||
MLImage::Copy(imgMy, imgLogo[IMAGES_INDEX_MY]);
|
||||
|
||||
imgLlamaOrig = new MLImage(LoadImageFromResource, TRUE);
|
||||
imgLlamaOrig->Load();
|
||||
imgLlama = new MLImage();
|
||||
MLImage::Copy(imgLlama, imgLlamaOrig);
|
||||
CreateToolTipWnd(hwndDlg);
|
||||
UpdateToolTips(hwndDlg);
|
||||
idxSelected = -1;
|
||||
SetRects(rc.right - rc.left, rc.bottom - rc.top);
|
||||
InvalidateRect(hwndDlg, &rc, FALSE);
|
||||
// SetTimer(hwndDlg, TIMER_ID_LLAMAFADE, TIMER_DELAY_LLAMAFADE, NULL);
|
||||
|
||||
}
|
||||
void timer_OnWaterPulse(HWND hwndDlg)
|
||||
{
|
||||
if (idxSelected == -1) return;
|
||||
|
||||
BOOL startRender = (!fltrWater);
|
||||
|
||||
if(!fltrWater)
|
||||
{
|
||||
KillTimer(hwndDlg, TIMER_ID_WATERPULSE); // stop timer - will change to slower interval
|
||||
fltrWater = new MLImageFilterWater();
|
||||
fltrWater->CreateFor(imgLogo[idxSelected]);
|
||||
tmpImage = new MLImage(imgLogo[idxSelected]->GetWidth(), imgLogo[idxSelected]->GetHeight());
|
||||
}
|
||||
|
||||
int ow = imgLogo[idxSelected]->GetWidth();
|
||||
int oh = imgLogo[idxSelected]->GetHeight();
|
||||
|
||||
for (int i = 0; i < oh*ow/1024; i++)
|
||||
{
|
||||
fltrWater->SineBlob(-1, -1, random(4,min(oh,ow)), 600, 0);
|
||||
}
|
||||
|
||||
if (startRender)
|
||||
{
|
||||
SetTimer(hwndDlg, TIMER_ID_WATERRENDER, TIMER_DELAY_WATERRENDER, NULL);
|
||||
SetTimer(hwndDlg, TIMER_ID_WATERPULSE, TIMER_DELAY_WATERPULSE, NULL);
|
||||
}
|
||||
}
|
||||
void timer_OnLlamaFade(HWND hwndDlg)
|
||||
{
|
||||
static int c = -2;
|
||||
static int step = 2;
|
||||
c = c + step;
|
||||
if (c > 30 && step > 0) {step = 0 - step; c = 30;}
|
||||
else if (c < 0 && step < 0) { step = 0 - step; c = 0; }
|
||||
MLImageFilter_Fader3(imgLlama, imgLlamaOrig, c);
|
||||
InvalidateRect(hwndDlg, &rcLlama, FALSE);
|
||||
}
|
||||
|
||||
HBITMAP LoadImageFromResource(INT_PTR handle)
|
||||
{
|
||||
int id = 0;
|
||||
BOOL incSize;
|
||||
MLImage *img = (MLImage*)handle;
|
||||
|
||||
if (img == imgLogo[IMAGES_INDEX_WA]) {id = IDB_LOGO_WAPLUGINS; incSize = FALSE;}
|
||||
else if (img == imgLogo[IMAGES_INDEX_IIS]) {id = IDB_LOGO_IIS; incSize = TRUE;}
|
||||
else if (img == imgLogo[IMAGES_INDEX_CT]) {id = IDB_LOGO_CODETECH; incSize = TRUE;}
|
||||
else if (img == imgLogo[IMAGES_INDEX_ID3V2]) {id = IDB_LOGO_ID3V2; incSize = TRUE;}
|
||||
else if (img == imgLogo[IMAGES_INDEX_MP3S]) {id = IDB_LOGO_MP3SURROUND; incSize = FALSE;} // because we don't use it for now
|
||||
else if (img == imgLogo[IMAGES_INDEX_MY]) {id = IDB_LOGO_MY; incSize = FALSE;}
|
||||
else if (img == imgLlamaOrig) {id = IDB_LLAMA; incSize = FALSE;}
|
||||
if (id == 0) return NULL;
|
||||
|
||||
HBITMAP hbmp = LoadBitmap(mod.hDllInstance, MAKEINTRESOURCE(id));
|
||||
if (hbmp == NULL) return hbmp;
|
||||
|
||||
if (incSize)
|
||||
{// we want to add additional white border
|
||||
HDC hdcWnd = GetWindowDC(NULL);
|
||||
HDC hdcSrc = CreateCompatibleDC(hdcWnd);
|
||||
HDC hdcDest = CreateCompatibleDC(hdcWnd);
|
||||
BITMAP bmp;
|
||||
GetObject(hbmp, sizeof(BITMAP), &bmp);
|
||||
int extraW = 40;
|
||||
int extraH = 16;
|
||||
HBITMAP hbmpNew = CreateBitmap(bmp.bmWidth + extraW, bmp.bmHeight + extraH, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
|
||||
|
||||
SelectObject(hdcSrc, hbmp);
|
||||
SelectObject(hdcDest, hbmpNew);
|
||||
|
||||
// fill borders
|
||||
HBRUSH br = CreateSolidBrush(RGB(255,255,255));
|
||||
RECT rc;
|
||||
SetRect(&rc, 0,0, bmp.bmWidth + extraW, extraH/2);
|
||||
FillRect(hdcDest, &rc, br);
|
||||
SetRect(&rc, 0, bmp.bmHeight + extraH/2, bmp.bmWidth + extraW, bmp.bmHeight + extraH);
|
||||
FillRect(hdcDest, &rc, br);
|
||||
SetRect(&rc, 0, extraH/2, extraW/2, bmp.bmHeight + extraH/2);
|
||||
FillRect(hdcDest, &rc, br);
|
||||
SetRect(&rc, bmp.bmWidth + extraW/2, extraH/2, bmp.bmWidth + extraW, bmp.bmHeight + extraH/2);
|
||||
FillRect(hdcDest, &rc, br);
|
||||
// copy original
|
||||
BitBlt(hdcDest, extraW/2, extraH/2, bmp.bmWidth, bmp.bmHeight, hdcSrc, 0,0, SRCCOPY);
|
||||
|
||||
DeleteObject(br);
|
||||
DeleteObject(hbmp);
|
||||
DeleteDC(hdcSrc);
|
||||
DeleteDC(hdcDest);
|
||||
ReleaseDC(NULL, hdcWnd);
|
||||
|
||||
hbmp = hbmpNew;
|
||||
}
|
||||
if (img == imgLogo[IMAGES_INDEX_MY])
|
||||
{ // need to add vesion number
|
||||
HDC hdcWnd = GetWindowDC(NULL);
|
||||
HDC hdcSrc = CreateCompatibleDC(hdcWnd);
|
||||
HDC hdcDest = CreateCompatibleDC(hdcWnd);
|
||||
|
||||
HFONT fnt = CreateFontW(-MulDiv(90, GetDeviceCaps(hdcDest, LOGPIXELSY), 72), 22, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, 0, L"Agency FB");
|
||||
SelectObject(hdcDest, fnt);
|
||||
|
||||
char *ver = mod.description + lstrlen(mod.description);
|
||||
while(ver != mod.description && *ver != ' ')
|
||||
{
|
||||
ver = CharPrevA(mod.description, ver);
|
||||
}
|
||||
if (*ver == ' ') ver++;
|
||||
|
||||
RECT rc;
|
||||
SetRect(&rc, 0, 0, 0, 0);
|
||||
DrawText(hdcDest, ver, -1, &rc, DT_CALCRECT | DT_RIGHT |DT_SINGLELINE);
|
||||
|
||||
int extraW = rc.right + 6;
|
||||
int extraH = 16;
|
||||
|
||||
BITMAP bmp;
|
||||
GetObject(hbmp, sizeof(BITMAP), &bmp);
|
||||
HBITMAP hbmpNew = CreateBitmap(bmp.bmWidth + extraW, bmp.bmHeight + extraH, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
|
||||
|
||||
SelectObject(hdcSrc, hbmp);
|
||||
SelectObject(hdcDest, hbmpNew);
|
||||
|
||||
// fill new area
|
||||
HBRUSH br = CreateSolidBrush(RGB(255,255,255));
|
||||
|
||||
SetRect(&rc, bmp.bmWidth,0, bmp.bmWidth + extraW, bmp.bmHeight + extraH);
|
||||
FillRect(hdcDest, &rc, br);
|
||||
SetRect(&rc, 0,0, bmp.bmWidth, extraH);
|
||||
FillRect(hdcDest, &rc, br);
|
||||
// copy original
|
||||
BitBlt(hdcDest, 0, extraH, bmp.bmWidth, bmp.bmHeight, hdcSrc, 0,0, SRCCOPY);
|
||||
// draw number
|
||||
|
||||
SetTextColor(hdcDest, RGB(80, 60, 10));
|
||||
SetBkMode(hdcDest, TRANSPARENT);
|
||||
SetRect(&rc, bmp.bmWidth + 6, rc.top -= 22, bmp.bmWidth + extraW, bmp.bmHeight + extraH);
|
||||
DrawText(hdcDest, ver, -1, &rc, DT_RIGHT |DT_SINGLELINE);
|
||||
DeleteObject(fnt);
|
||||
DeleteObject(br);
|
||||
DeleteObject(hbmp);
|
||||
DeleteDC(hdcSrc);
|
||||
DeleteDC(hdcDest);
|
||||
ReleaseDC(NULL, hdcWnd);
|
||||
|
||||
hbmp = hbmpNew;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return hbmp;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ADTS_H
|
||||
#define NULLSOFT_IN_MP3_ADTS_H
|
||||
|
||||
#include "ifc_mpeg_stream_reader.h"
|
||||
#include <bfc/std_mkncc.h> // for MKnCC()
|
||||
class adts
|
||||
{
|
||||
protected:
|
||||
adts() {}
|
||||
~adts() {}
|
||||
public:
|
||||
static FOURCC getServiceType() { return MK4CC('a','d','t','s'); }
|
||||
virtual int Initialize(bool forceMono, bool reverseStereo, bool allowSurround, int maxBits, bool allowRG, bool _useFloat = false, bool _useCRC = false)=0;
|
||||
virtual bool Open(ifc_mpeg_stream_reader *file)=0;
|
||||
virtual int Sync(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate)=0;
|
||||
virtual void CalculateFrameSize(int *frameSize)=0;
|
||||
virtual void GetOutputParameters(size_t *numBits, int *numChannels, int *sampleRate)=0;
|
||||
virtual void Flush(ifc_mpeg_stream_reader *file)=0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
enum
|
||||
{
|
||||
SUCCESS = 0,
|
||||
FAILURE=1,
|
||||
ENDOFFILE = 2,
|
||||
NEEDMOREDATA = 3,
|
||||
NEEDSYNC = 4,
|
||||
};
|
||||
virtual int Decode(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate, size_t *endCut)=0;
|
||||
virtual size_t GetCurrentBitrate()=0;
|
||||
virtual size_t GetDecoderDelay()=0;
|
||||
virtual int GetLayer()=0;
|
||||
virtual void Release()=0;
|
||||
virtual void SetDecoderHooks(void *layer3_vis, void *layer2_eq, void *layer3_eq) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,400 @@
|
||||
#include "main.h"
|
||||
#include "adts_mp2.h"
|
||||
#include "../winamp/wa_ipc.h"
|
||||
#include <math.h>
|
||||
#include "mpegutil.h"
|
||||
#include "../nsutil/pcm.h"
|
||||
|
||||
extern int g_ds;
|
||||
|
||||
<<<<<<< HEAD:in_mp3/adts_mp2.cpp
|
||||
DecoderHooks hooks={mp3GiveVisData, mp2Equalize, mp3Equalize};
|
||||
|
||||
=======
|
||||
>>>>>>> 5058463... fix old-school vis/eq mp3 stuff:mp3/adts_mp2.cpp
|
||||
ADTS_MP2::ADTS_MP2() : decoder(0), gain(1.f)
|
||||
{
|
||||
memset(&hooks, 0, sizeof(hooks));
|
||||
#ifndef NO_MP3SURROUND
|
||||
lineFilled=false;
|
||||
saDecHandle=0;
|
||||
saMode = SA_DEC_OFF;
|
||||
#endif
|
||||
decoderDelay = 529;
|
||||
endcut=0;
|
||||
|
||||
outputFrameSize = 0;
|
||||
bitsPerSample = 0;
|
||||
allowRG = false;
|
||||
useFloat = false;
|
||||
channels = 0;
|
||||
sampleRate = 0;
|
||||
|
||||
memset(&delayline, 0, sizeof(delayline));
|
||||
delaylineSize = 0;
|
||||
}
|
||||
|
||||
void ADTS_MP2::SetDecoderHooks(void *layer3_vis, void *layer2_eq, void *layer3_eq)
|
||||
{
|
||||
<<<<<<< HEAD:in_mp3/adts_mp2.cpp
|
||||
//*(void **)&hooks.layer3_vis = layer3_vis;
|
||||
=======
|
||||
*(void **)&hooks.layer3_vis = layer3_vis;
|
||||
>>>>>>> 5058463... fix old-school vis/eq mp3 stuff:mp3/adts_mp2.cpp
|
||||
*(void **)&hooks.layer2_eq = layer2_eq;
|
||||
*(void **)&hooks.layer3_eq = layer3_eq;
|
||||
}
|
||||
|
||||
int ADTS_MP2::Initialize(bool forceMono, bool reverseStereo, bool allowSurround, int maxBits, bool _allowRG, bool _useFloat, bool _useCRC)
|
||||
{
|
||||
allowRG = _allowRG;
|
||||
useFloat = _useFloat;
|
||||
int downmix = 0;
|
||||
if (reverseStereo)
|
||||
downmix = 2;
|
||||
else
|
||||
downmix = forceMono ? 1 : 0;
|
||||
bitsPerSample = maxBits;
|
||||
<<<<<<< HEAD:in_mp3/adts_mp2.cpp
|
||||
decoder = new CMpgaDecoder(&hooks, g_ds, downmix, !!_useCRC);
|
||||
|
||||
=======
|
||||
decoder = new CMpgaDecoder(hooks.layer3_vis?&hooks:(DecoderHooks *)0, MPEGAUDIO_QUALITY_FULL/*g_ds*/, downmix, _useCRC);
|
||||
>>>>>>> 5058463... fix old-school vis/eq mp3 stuff:mp3/adts_mp2.cpp
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (allowSurround)
|
||||
IIS_SADec_Init(&saDecHandle, 6);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ADTS_MP2::Open(ifc_mpeg_stream_reader *file)
|
||||
{
|
||||
decoder->Connect((CGioFile *)file);
|
||||
if (allowRG)
|
||||
gain = file->MPEGStream_Gain();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADTS_MP2::Close()
|
||||
{
|
||||
if (decoder)
|
||||
{
|
||||
delete decoder;
|
||||
decoder = 0;
|
||||
}
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (saDecHandle)
|
||||
IIS_SADec_Free(&saDecHandle);
|
||||
saDecHandle=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ADTS_MP2::GetOutputParameters(size_t *numBits, int *numChannels, int *sampleRate)
|
||||
{
|
||||
*sampleRate = this->sampleRate;
|
||||
*numChannels = channels;
|
||||
*numBits = bitsPerSample;
|
||||
}
|
||||
|
||||
void ADTS_MP2::CalculateFrameSize(int *frameSize)
|
||||
{
|
||||
*frameSize = outputFrameSize;
|
||||
if (decoder->GetStreamInfo()->GetLayer() == 1)
|
||||
*frameSize *= 3;
|
||||
}
|
||||
|
||||
void ADTS_MP2::Flush(ifc_mpeg_stream_reader *file)
|
||||
{
|
||||
decoder->Reset();
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (saDecHandle)
|
||||
IIS_SADec_Reset(saDecHandle);
|
||||
lineFilled=false;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t ADTS_MP2::GetCurrentBitrate()
|
||||
{
|
||||
return decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
}
|
||||
|
||||
size_t ADTS_MP2::GetDecoderDelay()
|
||||
{
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (!saDecHandle || saMode == SA_DEC_OFF || saMode == SA_DEC_BYPASS)
|
||||
return decoderDelay;
|
||||
else /* bcc adds 576 delay */
|
||||
return decoderDelay+576;
|
||||
#else
|
||||
return decoderDelay;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Decimate(const float *input, void *output, size_t numSamples, size_t *outputWritten, int bitsPerSample, bool useFloat, float gain)
|
||||
{
|
||||
if (!useFloat)
|
||||
{
|
||||
// TODO seen a few crashes reported where 'output' is 0
|
||||
nsutil_pcm_FloatToInt_Interleaved_Gain(output, input, bitsPerSample, numSamples, gain);
|
||||
}
|
||||
else if (gain != 1.f)
|
||||
{
|
||||
float *data = (float *)output;
|
||||
for (size_t i=0;i<numSamples;i++)
|
||||
data[i]*=gain;
|
||||
//data[i]=input[i]*gain;
|
||||
}
|
||||
|
||||
*outputWritten = numSamples * (bitsPerSample / 8);
|
||||
}
|
||||
/*
|
||||
notes for mp3 surround implementations
|
||||
need to check the first two frames for ancillary data
|
||||
store first valid in temp
|
||||
store second valid frame in delay line
|
||||
decimate first valid into output buffer
|
||||
ancillary data is stored one frame behind, so PCM data decoded from mp3 frame n combines with anc data from frame n+1
|
||||
*/
|
||||
int ADTS_MP2::Sync(ifc_mpeg_stream_reader *_file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate)
|
||||
{
|
||||
SSC ssc;
|
||||
CGioFile *file = (CGioFile *)_file;
|
||||
unsigned char ancBytes[8192] = {0};
|
||||
int numAncBytes = 0;
|
||||
|
||||
unsigned int delay=0, totalLength=0;
|
||||
|
||||
float floatTemp[1152*2] = {0};
|
||||
float *flData=useFloat?(float *)output:floatTemp;
|
||||
ssc = decoder->DecodeFrame(flData, sizeof(floatTemp), &outputFrameSize,
|
||||
<<<<<<< HEAD:in_mp3/adts_mp2.cpp
|
||||
ancBytes, &numAncBytes, 1, &delay, &totalLength);
|
||||
|
||||
// TODO: benski> we should really have CGioFile try to read this stuff
|
||||
if (delay && !file->prepad)
|
||||
{
|
||||
// validate
|
||||
if (delay >= 529)
|
||||
{
|
||||
decoderDelay = delay;
|
||||
endcut = 1152 - ((totalLength + delay) % 1152); // how many 0 samples had to be added?
|
||||
endcut += decoderDelay; // also need to cut out the encoder+decoder delay
|
||||
file->m_vbr_samples = totalLength;
|
||||
}
|
||||
}
|
||||
=======
|
||||
ancBytes, &numAncBytes);
|
||||
>>>>>>> fd5c493... have CGioFile read the OFL (from newer fraunhofer encoders):mp3/adts_mp2.cpp
|
||||
|
||||
switch (ssc)
|
||||
{
|
||||
case SSC_OK:
|
||||
{
|
||||
channels = decoder->GetStreamInfo()->GetEffectiveChannels();
|
||||
sampleRate = decoder->GetStreamInfo()->GetEffectiveSFreq();
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (!numAncBytes && saDecHandle)
|
||||
{
|
||||
ssc = decoder->DecodeFrame(delayline, sizeof(delayline), &delaylineSize,
|
||||
ancBytes, &numAncBytes);
|
||||
|
||||
if (SSC_SUCCESS(ssc))
|
||||
{
|
||||
lineFilled=true;
|
||||
}
|
||||
else if (ssc == SSC_W_MPGA_SYNCEOF)
|
||||
return ENDOFFILE;
|
||||
else
|
||||
return NEEDMOREDATA;
|
||||
}
|
||||
|
||||
if (saDecHandle)
|
||||
{
|
||||
SA_DEC_ERROR sa_error = IIS_SADec_DecodeAncData(saDecHandle, ancBytes, numAncBytes, 0, 0);
|
||||
if (sa_error == SA_DEC_NO_ERROR)
|
||||
{
|
||||
IIS_SADec_InitInfo(saDecHandle, sampleRate, channels);
|
||||
SA_DEC_INFO saInfo = IIS_SADec_GetInfo(saDecHandle);
|
||||
sampleRate = saInfo.SampleRate;
|
||||
channels = saInfo.nChannelsOut;
|
||||
saMode = saInfo.configuredMode;
|
||||
}
|
||||
else if (saMode == SA_DEC_OFF)
|
||||
{
|
||||
IIS_SADec_Free(&saDecHandle);
|
||||
saDecHandle=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineFilled=false;
|
||||
return NEEDMOREDATA;
|
||||
}
|
||||
}
|
||||
|
||||
if (saDecHandle)
|
||||
{
|
||||
float surroundFloatTemp[1152*6] = {0};
|
||||
int outputSamples = 0;
|
||||
/*SA_DEC_ERROR sa_error = */IIS_SADec_DecodeFrame(saDecHandle,
|
||||
flData, outputFrameSize/sizeof(float),
|
||||
(char *)ancBytes, numAncBytes,
|
||||
surroundFloatTemp, sizeof(surroundFloatTemp),
|
||||
&outputSamples, saMode, 0, 0,
|
||||
0, 0);
|
||||
if (useFloat)
|
||||
memcpy(output, surroundFloatTemp, sizeof(surroundFloatTemp));
|
||||
Decimate(surroundFloatTemp, output, outputSamples, outputWritten, bitsPerSample, useFloat, (float)gain);
|
||||
outputFrameSize = *outputWritten;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Decimate(floatTemp, output, outputFrameSize / sizeof(float), outputWritten, bitsPerSample, useFloat, (float)gain);
|
||||
outputFrameSize = *outputWritten;
|
||||
}
|
||||
}
|
||||
return SSC_OK;
|
||||
case SSC_W_MPGA_SYNCSEARCHED:
|
||||
return NEEDMOREDATA;
|
||||
case SSC_W_MPGA_SYNCEOF:
|
||||
return ENDOFFILE;
|
||||
case SSC_E_MPGA_WRONGLAYER:
|
||||
decoder->m_Mbs.Seek(1);
|
||||
default:
|
||||
return NEEDMOREDATA;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int ADTS_MP2::Decode(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate, size_t *endCut)
|
||||
{
|
||||
if (endcut)
|
||||
{
|
||||
*endCut = endcut;
|
||||
endcut=0;
|
||||
}
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (!saDecHandle && lineFilled) // if we don't have surround info, go ahead and flush out the delayline buffer
|
||||
{
|
||||
Decimate(delayline, output, delaylineSize / sizeof(float), outputWritten, bitsPerSample, useFloat, (float)gain);
|
||||
*bitrate = decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
lineFilled=false;
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
|
||||
if (saDecHandle && !lineFilled && !decoder->IsEof()) // have surround info, but don't have a previously decoded frame
|
||||
{
|
||||
// resync
|
||||
int ret = Sync(file, output, outputSize, outputWritten, bitrate);
|
||||
if (ret == SSC_OK && saDecHandle)
|
||||
{
|
||||
*bitrate = decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
else if (saDecHandle)
|
||||
return ret;
|
||||
else
|
||||
return adts::FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned char ancBytes[8192] = {0};
|
||||
int numAncBytes = 0;
|
||||
|
||||
int newl;
|
||||
|
||||
float floatTemp[1152*2] = {0};
|
||||
float *flData=useFloat?(float *)output:floatTemp;
|
||||
SSC ssc = decoder->DecodeFrame(flData, sizeof(floatTemp), &newl, ancBytes, &numAncBytes);
|
||||
|
||||
if (SSC_SUCCESS(ssc))
|
||||
{
|
||||
#ifndef NO_MP3SURROUND
|
||||
if (saDecHandle && lineFilled)
|
||||
{
|
||||
float surroundFloatTemp[1152*6] = {0};
|
||||
int outputSamples;
|
||||
/*SA_DEC_ERROR sa_error = */IIS_SADec_DecodeFrame(saDecHandle,
|
||||
delayline, delaylineSize/sizeof(float),
|
||||
(char *)ancBytes, numAncBytes,
|
||||
surroundFloatTemp, sizeof(surroundFloatTemp),
|
||||
&outputSamples, saMode, 0, 0,
|
||||
0, 0);
|
||||
|
||||
if (useFloat)
|
||||
memcpy(output, surroundFloatTemp, sizeof(surroundFloatTemp));
|
||||
Decimate(surroundFloatTemp, output, outputSamples, outputWritten, bitsPerSample, useFloat, (float)gain);
|
||||
memcpy(delayline, flData, delaylineSize);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Decimate(floatTemp, output, newl / sizeof(float), outputWritten, bitsPerSample, useFloat, (float)gain);
|
||||
}
|
||||
*bitrate = decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
else if (decoder->IsEof())
|
||||
{
|
||||
#ifndef NO_MP3SURROUND
|
||||
/* In case of SA processing one ancillary data
|
||||
package and maybe fill samples left in the dynamic
|
||||
buffer of the mp3 decoder. Take care, that the
|
||||
ancillary data buffer is greater then the dynamic buffer of
|
||||
the mp3 decoder. */
|
||||
if (saDecHandle && lineFilled)
|
||||
{
|
||||
decoder->GetLastAncData(ancBytes, &numAncBytes);
|
||||
float surroundFloatTemp[1152*6];
|
||||
int outputSamples = 0;
|
||||
/*SA_DEC_ERROR sa_error = */IIS_SADec_DecodeFrame(saDecHandle,
|
||||
delayline, delaylineSize/sizeof(float),
|
||||
(char *)ancBytes, numAncBytes,
|
||||
surroundFloatTemp, sizeof(surroundFloatTemp),
|
||||
&outputSamples, saMode, 0, 0,
|
||||
0, 0);
|
||||
|
||||
if (useFloat)
|
||||
memcpy(output, surroundFloatTemp, sizeof(surroundFloatTemp));
|
||||
Decimate(surroundFloatTemp, output, outputSamples, outputWritten, bitsPerSample, useFloat, (float)gain);
|
||||
lineFilled=false;
|
||||
*bitrate = decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return adts::ENDOFFILE;
|
||||
}
|
||||
else if (ssc == SSC_W_MPGA_SYNCNEEDDATA)
|
||||
{
|
||||
*bitrate = decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
return adts::NEEDMOREDATA;
|
||||
}
|
||||
else if (ssc==SSC_W_MPGA_SYNCLOST || ssc==SSC_W_MPGA_SYNCSEARCHED)
|
||||
{
|
||||
*bitrate = decoder->GetStreamInfo()->GetBitrate() / 1000;
|
||||
return adts::NEEDSYNC;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ssc == SSC_E_MPGA_WRONGLAYER)
|
||||
decoder->m_Mbs.Seek(1);
|
||||
|
||||
return adts::FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int ADTS_MP2::GetLayer()
|
||||
{
|
||||
if (decoder)
|
||||
return decoder->GetStreamInfo()->GetLayer();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ADTS_MP2::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ADTS_MP2_H
|
||||
#define NULLSOFT_IN_MP3_ADTS_MP2_H
|
||||
|
||||
#include "adts.h"
|
||||
#include "api.h"
|
||||
#include "config.h"
|
||||
#ifndef NO_MP3SURROUND
|
||||
#include "../mp3/bccDecLinklib/include/bccDecLink.h" // Binaural Cue Coding (aka mp3 surround)
|
||||
#endif
|
||||
|
||||
class ADTS_MP2 : public adts
|
||||
{
|
||||
public:
|
||||
ADTS_MP2();
|
||||
int Initialize(bool forceMono, bool reverse_stereo, bool allowSurround, int maxBits, bool allowRG, bool _useFloat, bool _useCRC);
|
||||
bool Open(ifc_mpeg_stream_reader *file);
|
||||
void Close();
|
||||
void GetOutputParameters(size_t *numBits, int *numChannels, int *sampleRate);
|
||||
void CalculateFrameSize(int *frameSize);
|
||||
void Flush(ifc_mpeg_stream_reader *file);
|
||||
size_t GetCurrentBitrate();
|
||||
size_t GetDecoderDelay();
|
||||
int Sync(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate);
|
||||
int Decode(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate, size_t *endCut);
|
||||
int GetLayer();
|
||||
void Release();
|
||||
void SetDecoderHooks(void *layer3_vis, void *layer2_eq, void *layer3_eq);
|
||||
<<<<<<< HEAD:in_mp3/adts_mp2.h
|
||||
|
||||
=======
|
||||
>>>>>>> 5058463... fix old-school vis/eq mp3 stuff:mp3/adts_mp2.h
|
||||
private:
|
||||
DecoderHooks hooks;
|
||||
CMpgaDecoder *decoder;
|
||||
int outputFrameSize;
|
||||
size_t bitsPerSample;
|
||||
double gain;
|
||||
bool allowRG;
|
||||
bool useFloat;
|
||||
|
||||
int channels;
|
||||
int sampleRate;
|
||||
unsigned int decoderDelay;
|
||||
unsigned int endcut;
|
||||
|
||||
#ifndef NO_MP3SURROUND
|
||||
float delayline[1152*2];
|
||||
int delaylineSize;
|
||||
bool lineFilled;
|
||||
SADEC_HANDLE saDecHandle;
|
||||
SA_DEC_MODE saMode;
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "adts_vlb.h"
|
||||
#include "giofile.h"
|
||||
#include "in2.h"
|
||||
extern In_Module mod;
|
||||
|
||||
ADTS_VLB::ADTS_VLB() : decoder(0), needsync(1)
|
||||
{}
|
||||
|
||||
int ADTS_VLB::Initialize(bool forceMono, bool reverseStereo, bool allowSurround, int maxBits, bool allowRG, bool _useFloat, bool _useCRC)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ADTS_VLB::Open(ifc_mpeg_stream_reader *file)
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(obj_vlbDecoderGUID);
|
||||
if (factory)
|
||||
decoder = (obj_vlbDecoder *)factory->getInterface();
|
||||
|
||||
if (decoder)
|
||||
{
|
||||
int status = decoder->Open((DataIOControl *)(CGioFile *)file);
|
||||
if (status == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ADTS_VLB::Close()
|
||||
{
|
||||
if (decoder)
|
||||
{
|
||||
waServiceFactory *factory = mod.service->service_getServiceByGuid(obj_vlbDecoderGUID);
|
||||
if (factory)
|
||||
factory->releaseInterface(decoder);
|
||||
}
|
||||
|
||||
decoder = 0;
|
||||
}
|
||||
|
||||
void ADTS_VLB::GetOutputParameters(size_t *numBits, int *numChannels, int *sampleRate)
|
||||
{
|
||||
*sampleRate = params.sampling_frequency;
|
||||
*numChannels = params.num_channels;
|
||||
*numBits = 16;
|
||||
}
|
||||
|
||||
void ADTS_VLB::CalculateFrameSize( int *frameSize )
|
||||
{
|
||||
*frameSize = 576 * 2 * params.num_channels;
|
||||
if ( *frameSize > 576 * 2 * 2 )
|
||||
*frameSize = 576 * 2 * 2;
|
||||
}
|
||||
|
||||
void ADTS_VLB::Flush(ifc_mpeg_stream_reader *file)
|
||||
{
|
||||
decoder->Flush();
|
||||
decoder->Close();
|
||||
decoder->Open((DataIOControl *)(CGioFile *)file);
|
||||
|
||||
needsync = 1;
|
||||
}
|
||||
|
||||
size_t ADTS_VLB::GetCurrentBitrate()
|
||||
{
|
||||
return params.bitrate / 1000;
|
||||
}
|
||||
|
||||
size_t ADTS_VLB::GetDecoderDelay()
|
||||
{
|
||||
return 0; // not really sure
|
||||
}
|
||||
|
||||
int ADTS_VLB::Sync(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate)
|
||||
{
|
||||
int status = decoder->Synchronize(¶ms);
|
||||
if (!status)
|
||||
{
|
||||
needsync = 0;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (file->MPEGStream_EOF())
|
||||
return ENDOFFILE;
|
||||
|
||||
return NEEDMOREDATA;
|
||||
}
|
||||
|
||||
int ADTS_VLB::Decode(ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate, size_t *endCut)
|
||||
{
|
||||
if (*outputWritten = decoder->Read(output, outputSize))
|
||||
{
|
||||
// TODO: benski> verify that params is valid here
|
||||
*bitrate = params.bitrate / 1000;
|
||||
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
|
||||
if (needsync)
|
||||
{
|
||||
int status = decoder->Synchronize(¶ms);
|
||||
if (!status)
|
||||
{
|
||||
needsync = 0;
|
||||
}
|
||||
else if (file->MPEGStream_EOF())
|
||||
{
|
||||
return adts::ENDOFFILE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsync)
|
||||
{
|
||||
int status = decoder->DecodeFrame(¶ms);
|
||||
|
||||
if (status > ERR_NO_ERROR && status != ERR_END_OF_FILE)
|
||||
{
|
||||
needsync = 1;
|
||||
return adts::FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (status == ERR_END_OF_FILE)
|
||||
{
|
||||
if (file->MPEGStream_EOF())
|
||||
{
|
||||
return adts::ENDOFFILE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*bitrate = params.bitrate / 1000;
|
||||
return adts::NEEDMOREDATA;
|
||||
}
|
||||
}
|
||||
|
||||
*bitrate = params.bitrate / 1000;
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return adts::SUCCESS;
|
||||
}
|
||||
|
||||
int ADTS_VLB::GetLayer()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void ADTS_VLB::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef NULLSOFT_IN_MP3_ADTS_VLB_H
|
||||
#define NULLSOFT_IN_MP3_ADTS_VLB_H
|
||||
#include "adts.h"
|
||||
|
||||
#include "../vlb/obj_vlbDecoder.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include <api/service/waServiceFactory.h>
|
||||
|
||||
class ADTS_VLB : public adts
|
||||
{
|
||||
public:
|
||||
ADTS_VLB();
|
||||
int Initialize( bool forceMono, bool reverseStereo, bool allowSurround, int maxBits, bool allowRG, bool _useFloat, bool _useCRC );
|
||||
|
||||
bool Open( ifc_mpeg_stream_reader *file );
|
||||
void Close();
|
||||
|
||||
void GetOutputParameters( size_t *numBits, int *numChannels, int *sampleRate );
|
||||
void CalculateFrameSize( int *frameSize );
|
||||
|
||||
void Flush( ifc_mpeg_stream_reader *file );
|
||||
|
||||
size_t GetCurrentBitrate();
|
||||
size_t GetDecoderDelay();
|
||||
|
||||
int Sync( ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate );
|
||||
int Decode( ifc_mpeg_stream_reader *file, unsigned __int8 *output, size_t outputSize, size_t *outputWritten, size_t *bitrate, size_t *endCut );
|
||||
|
||||
int GetLayer();
|
||||
void Release();
|
||||
|
||||
private:
|
||||
obj_vlbDecoder *decoder;
|
||||
int needsync;
|
||||
AACStreamParameters params;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "apev2.h"
|
||||
|
||||
APE::APE()
|
||||
{
|
||||
hasData=false;
|
||||
dirty=false;
|
||||
}
|
||||
|
||||
int APE::Decode(const void *data, size_t len)
|
||||
{
|
||||
if (APEv2::Tag::Parse(data, len) == APEv2::APEV2_SUCCESS)
|
||||
{
|
||||
hasData=true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return -1 for empty, 1 for OK, 0 for "don't understand metadata name"
|
||||
int APE::GetString(const char *metadata, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!hasData)
|
||||
return 0;
|
||||
|
||||
if (!_stricmp(metadata, "replaygain_track_gain")
|
||||
|| !_stricmp(metadata, "replaygain_track_peak")
|
||||
|| !_stricmp(metadata, "replaygain_album_gain")
|
||||
|| !_stricmp(metadata, "replaygain_album_peak"))
|
||||
{
|
||||
if (APEv2::Tag::GetString(metadata, data, dataLen) == APEv2::APEV2_SUCCESS)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *ape_key = MapWinampKeyToApeKey(metadata);
|
||||
if (ape_key)
|
||||
{
|
||||
if (APEv2::Tag::GetString(ape_key, data, dataLen) == APEv2::APEV2_SUCCESS)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APE::SetString(const char *metadata, const wchar_t *data)
|
||||
{
|
||||
if (!_stricmp(metadata, "replaygain_track_gain")
|
||||
|| !_stricmp(metadata, "replaygain_track_peak")
|
||||
|| !_stricmp(metadata, "replaygain_album_gain")
|
||||
|| !_stricmp(metadata, "replaygain_album_peak"))
|
||||
{
|
||||
APEv2::Tag::SetString(metadata, data);
|
||||
dirty=true;
|
||||
hasData=true;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *ape_key = MapWinampKeyToApeKey(metadata);
|
||||
if (ape_key)
|
||||
{
|
||||
APEv2::Tag::SetString(ape_key, data);
|
||||
dirty=true;
|
||||
hasData=true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APE::Clear()
|
||||
{
|
||||
APEv2::Tag::Clear();
|
||||
dirty=true;
|
||||
hasData=false;
|
||||
}
|
||||
|
||||
void APE::MarkClear()
|
||||
{
|
||||
dirty=true;
|
||||
hasData=false;
|
||||
}
|
||||
|
||||
int APE::SetKeyValueByIndex(size_t index, const char *key, const wchar_t *data)
|
||||
{
|
||||
dirty=true;
|
||||
return APEv2::Tag::SetKeyValueByIndex(index, key, data);
|
||||
}
|
||||
|
||||
int APE::RemoveItem(size_t index)
|
||||
{
|
||||
dirty=true;
|
||||
return APEv2::Tag::RemoveItem(index);
|
||||
}
|
||||
|
||||
int APE::AddItem()
|
||||
{
|
||||
dirty=true;
|
||||
hasData=true;
|
||||
APEv2::Tag::AddItem();
|
||||
return APEv2::APEV2_SUCCESS;
|
||||
}
|
||||
|
||||
struct ApeKeyMapping
|
||||
{
|
||||
const char *ape_key;
|
||||
const char *winamp_key;
|
||||
const wchar_t *winamp_keyW;
|
||||
};
|
||||
|
||||
static ApeKeyMapping apeKeyMapping[] =
|
||||
{
|
||||
{ "Track", "track", L"track" },
|
||||
{"Album", "album", L"album" },
|
||||
{"Artist", "artist", L"artist" },
|
||||
{"Comment", "comment", L"comment" },
|
||||
{"Year", "year", L"year" },
|
||||
{"Genre", "genre", L"genre" },
|
||||
{"Title", "title", L"title"},
|
||||
{"Composer", "composer", L"composer"},
|
||||
{"Performer", "performer", L"performer"},
|
||||
{"Album artist", "albumartist", L"albumartist"},
|
||||
};
|
||||
|
||||
const wchar_t *APE::MapApeKeyToWinampKeyW(const char *ape_key)
|
||||
{
|
||||
size_t num_mappings = sizeof(apeKeyMapping)/sizeof(apeKeyMapping[0]);
|
||||
for (size_t i=0;i!=num_mappings;i++)
|
||||
{
|
||||
if (!_stricmp(ape_key, apeKeyMapping[i].ape_key))
|
||||
return apeKeyMapping[i].winamp_keyW;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *APE::MapApeKeyToWinampKey(const char *ape_key)
|
||||
{
|
||||
size_t num_mappings = sizeof(apeKeyMapping)/sizeof(apeKeyMapping[0]);
|
||||
for (size_t i=0;i!=num_mappings;i++)
|
||||
{
|
||||
if (!_stricmp(ape_key, apeKeyMapping[i].ape_key))
|
||||
return apeKeyMapping[i].winamp_key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *APE::MapWinampKeyToApeKey(const char *winamp_key)
|
||||
{
|
||||
size_t num_mappings = sizeof(apeKeyMapping)/sizeof(apeKeyMapping[0]);
|
||||
for (size_t i=0;i!=num_mappings;i++)
|
||||
{
|
||||
if (!_stricmp(winamp_key, apeKeyMapping[i].winamp_key))
|
||||
return apeKeyMapping[i].ape_key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int APE::AddKeyValue(const char *key, const wchar_t *data)
|
||||
{
|
||||
dirty=true;
|
||||
hasData=true;
|
||||
APEv2::Item *newItem = APEv2::Tag::AddItem();
|
||||
newItem->SetKey(key);
|
||||
return APEv2::Tag::SetItemData(newItem, data);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef NULLSOFT_IN_MP3_APEV2_H
|
||||
#define NULLSOFT_IN_MP3_APEV2_H
|
||||
|
||||
#include "../apev2/tag.h"
|
||||
|
||||
class APE : private APEv2::Tag
|
||||
{
|
||||
public:
|
||||
APE();
|
||||
bool HasData() { return hasData; }
|
||||
bool IsDirty() { return dirty; }
|
||||
void ResetDirty() { dirty=0; hasData = true; }
|
||||
void Clear();
|
||||
void MarkClear();
|
||||
int Decode(const void *data, size_t len);
|
||||
|
||||
// return -1 for empty, 1 for OK, 0 for "don't understand tag name"
|
||||
int GetString(const char *tag, wchar_t *data, int dataLen);
|
||||
int SetString(const char *tag, const wchar_t *data);
|
||||
|
||||
int AddKeyValue(const char *key, const wchar_t *data);
|
||||
int SetKeyValueByIndex(size_t index, const char *key, const wchar_t *data);
|
||||
int RemoveItem(size_t index);
|
||||
|
||||
int AddItem();
|
||||
|
||||
|
||||
static const char *MapApeKeyToWinampKey(const char *ape_key);
|
||||
static const wchar_t *MapApeKeyToWinampKeyW(const char *ape_key);
|
||||
static const char *MapWinampKeyToApeKey(const char *winamp_key);
|
||||
|
||||
using APEv2::Tag::EnumValue;
|
||||
using APEv2::Tag::EncodeSize;
|
||||
using APEv2::Tag::Encode;
|
||||
using APEv2::Tag::FindItemByKey;
|
||||
using APEv2::Tag::GetNumItems;
|
||||
using APEv2::Tag::IsItemReadOnly;
|
||||
using APEv2::Tag::SetFlags;
|
||||
|
||||
private:
|
||||
bool hasData;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef NULLSOFT_IN_MP3_API_H
|
||||
#define NULLSOFT_IN_MP3_API_H
|
||||
|
||||
#include "../Agave/Config/api_config.h"
|
||||
|
||||
#include "api/memmgr/api_memmgr.h"
|
||||
extern api_memmgr *memmgr;
|
||||
#define WASABI_API_MEMMGR memmgr
|
||||
|
||||
#include "../Agave/Language/api_language.h"
|
||||
|
||||
#include "api/application/api_application.h"
|
||||
extern api_application *applicationApi;
|
||||
#define WASABI_API_APP applicationApi
|
||||
|
||||
#endif // !NULLSOFT_IN_MP3_API_H
|
||||
@@ -0,0 +1,685 @@
|
||||
#include "main.h"
|
||||
#include <shlobj.h>
|
||||
#include <commctrl.h>
|
||||
#include <windows.h>
|
||||
#include "../winamp/wa_ipc.h"
|
||||
#include "config.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "resource.h"
|
||||
|
||||
char g_http_tmp[MAX_PATH] = {0};
|
||||
|
||||
int config_write_mode = WRITE_UTF16;
|
||||
int config_read_mode = READ_LOCAL;
|
||||
|
||||
int config_parse_apev2 = 1;
|
||||
int config_parse_lyrics3 = 1;
|
||||
int config_parse_id3v1 = 1;
|
||||
int config_parse_id3v2 = 1;
|
||||
|
||||
int config_write_apev2 = 1;
|
||||
int config_write_id3v1 = 1;
|
||||
int config_write_id3v2 = 1;
|
||||
|
||||
int config_create_id3v1 = 1;
|
||||
int config_create_id3v2 = 1;
|
||||
int config_create_apev2 = 0;
|
||||
|
||||
int config_apev2_header = RETAIN_HEADER;
|
||||
int config_lp = 0;
|
||||
|
||||
BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
wchar_t cl[32] = {0};
|
||||
GetClassNameW(hwnd, cl, ARRAYSIZE(cl));
|
||||
if (!lstrcmpiW(cl, WC_TREEVIEW))
|
||||
{
|
||||
PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int CALLBACK BrowseCallbackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case BFFM_INITIALIZED:
|
||||
{
|
||||
SetWindowText(hwnd, WASABI_API_LNGSTRINGW(IDS_SELECT_DIRECTORY_TO_SAVE_TO));
|
||||
if (g_http_tmp[0]) SendMessage(hwnd, BFFM_SETSELECTIONA, 1, (LPARAM)g_http_tmp);
|
||||
|
||||
// this is not nice but it fixes the selection not working correctly on all OSes
|
||||
EnumChildWindows(hwnd, browseEnumProc, 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char app_name[] = "Nullsoft MPEG Decoder";
|
||||
|
||||
char *get_inifile() { return INI_FILE; }
|
||||
|
||||
int _r_i(char *name, int def)
|
||||
{
|
||||
if (!_strnicmp(name, "config_", 7)) name += 7;
|
||||
return GetPrivateProfileIntA(app_name, name, def, INI_FILE);
|
||||
}
|
||||
|
||||
#define RI(x) (( x ) = _r_i(#x,( x )))
|
||||
void _w_i(char *name, int d)
|
||||
{
|
||||
char str[120] = {0};
|
||||
wsprintfA(str, "%d", d);
|
||||
if (!_strnicmp(name, "config_", 7)) name += 7;
|
||||
WritePrivateProfileStringA(app_name, name, str, INI_FILE);
|
||||
}
|
||||
#define WI(x) _w_i(#x,( x ))
|
||||
|
||||
void _r_s(char *name, char *data, int mlen)
|
||||
{
|
||||
char buf[2048] = {0};
|
||||
lstrcpynA(buf, data, 2048);
|
||||
if (!_strnicmp(name, "config_", 7)) name += 7;
|
||||
GetPrivateProfileStringA(app_name, name, buf, data, mlen, INI_FILE);
|
||||
}
|
||||
#define RS(x) (_r_s(#x,x,sizeof(x)))
|
||||
|
||||
void _w_s(char *name, char *data)
|
||||
{
|
||||
if (!_strnicmp(name, "config_", 7)) name += 7;
|
||||
WritePrivateProfileStringA(app_name, name, data, INI_FILE);
|
||||
}
|
||||
#define WS(x) (_w_s(#x,x))
|
||||
|
||||
static void config_init()
|
||||
{
|
||||
char *p;
|
||||
if (mod.hMainWindow &&
|
||||
(p = (char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE))
|
||||
&& p != (char *)1)
|
||||
{
|
||||
strncpy(INI_FILE, p, MAX_PATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetModuleFileNameA(NULL, INI_FILE, sizeof(INI_FILE));
|
||||
p = INI_FILE + strlen(INI_FILE);
|
||||
while (p >= INI_FILE && *p != '.') p--;
|
||||
strcpy(++p, "ini");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AAC_SUPPORT
|
||||
#define DEF_EXT_LIST "MP3;MP2;MP1;AAC;VLB"
|
||||
#else
|
||||
#define DEF_EXT_LIST "MP3;MP2;MP1"
|
||||
#endif
|
||||
|
||||
#define __STR2WSTR(str) L##str
|
||||
#define WIDEN(str) __STR2WSTR(str)
|
||||
#define DEF_EXT_LISTW WIDEN(DEF_EXT_LIST)
|
||||
|
||||
#ifdef AAC_SUPPORT
|
||||
char config_extlist_aac[129] = DEF_EXT_LIST;
|
||||
#else
|
||||
char config_extlist[129] = DEF_EXT_LIST;
|
||||
#endif
|
||||
|
||||
char config_rating_email[255] = {0};
|
||||
|
||||
void config_read()
|
||||
{
|
||||
config_init();
|
||||
RI(allow_scartwork);
|
||||
RI(allow_sctitles);
|
||||
RI(sctitle_format);
|
||||
RI(config_http_buffersize);
|
||||
RI(config_http_prebuffer);
|
||||
RI(config_http_prebuffer_underrun);
|
||||
RI(config_downmix);
|
||||
RI(config_downsample);
|
||||
RI(config_max_bufsize_k);
|
||||
RI(config_eqmode);
|
||||
RI(config_gapless);
|
||||
|
||||
if(FAILED(SHGetFolderPathA(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, config_http_save_dir)))
|
||||
{
|
||||
if(FAILED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, config_http_save_dir)))
|
||||
{
|
||||
lstrcpynA(config_http_save_dir, "C:\\", MAX_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
RS(config_http_save_dir);
|
||||
RI(config_miscopts);
|
||||
RI(config_fastvis);
|
||||
|
||||
#ifdef AAC_SUPPORT
|
||||
RS(config_extlist_aac);
|
||||
#else
|
||||
RS(config_extlist);
|
||||
#endif
|
||||
|
||||
RI(config_write_mode);
|
||||
RI(config_read_mode);
|
||||
|
||||
RI(config_parse_apev2);
|
||||
RI(config_parse_lyrics3);
|
||||
RI(config_parse_id3v1);
|
||||
RI(config_parse_id3v2);
|
||||
|
||||
RI(config_write_apev2);
|
||||
RI(config_write_id3v1);
|
||||
RI(config_write_id3v2);
|
||||
|
||||
RI(config_create_apev2);
|
||||
RI(config_create_id3v1);
|
||||
RI(config_create_id3v2);
|
||||
|
||||
RI(config_apev2_header);
|
||||
|
||||
RI(config_lp);
|
||||
|
||||
RS(config_rating_email);
|
||||
}
|
||||
|
||||
void config_write()
|
||||
{
|
||||
WI(allow_scartwork);
|
||||
WI(config_fastvis);
|
||||
WI(config_miscopts);
|
||||
WI(allow_sctitles);
|
||||
WI(sctitle_format);
|
||||
WI(config_http_buffersize);
|
||||
WI(config_http_buffersize);
|
||||
WI(config_http_prebuffer);
|
||||
WI(config_http_prebuffer_underrun);
|
||||
WI(config_downmix);
|
||||
WI(config_downsample);
|
||||
WI(config_max_bufsize_k);
|
||||
WI(config_eqmode);
|
||||
WS(config_http_save_dir);
|
||||
#ifdef AAC_SUPPORT
|
||||
WS(config_extlist_aac);
|
||||
#else
|
||||
WS(config_extlist);
|
||||
#endif
|
||||
|
||||
WI(config_write_mode);
|
||||
WI(config_read_mode);
|
||||
|
||||
WI(config_parse_apev2);
|
||||
WI(config_parse_lyrics3);
|
||||
WI(config_parse_id3v1);
|
||||
WI(config_parse_id3v2);
|
||||
|
||||
WI(config_write_apev2);
|
||||
WI(config_write_id3v1);
|
||||
WI(config_write_id3v2);
|
||||
|
||||
WI(config_create_apev2);
|
||||
WI(config_create_id3v1);
|
||||
WI(config_create_id3v2);
|
||||
|
||||
WI(config_apev2_header);
|
||||
|
||||
WI(config_lp);
|
||||
|
||||
WS(config_rating_email);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK prefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK id3Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK advancedTaggingProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK httpProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK outputProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
#define ISSEP(x) ((x) == ' ' || (x) == ';' || (x) == ',' || (x) == ':' || (x) == '.')
|
||||
char *getfileextensions()
|
||||
{
|
||||
static char list[512];
|
||||
char *op = list;
|
||||
// char *g_fileassos="MP3;MP2;MP1\0MPEG Audio Files (*.MP3;*.MP2;*.MP1)\0";
|
||||
|
||||
char *p = config_extlist;
|
||||
int s = 0;
|
||||
while (p && *p)
|
||||
{
|
||||
while (ISSEP(*p)) p++;
|
||||
if (!p || !*p) break;
|
||||
if (s) *op++ = ';';
|
||||
s = 1;
|
||||
while (p && *p && !ISSEP(*p)) *op++ = *p++;
|
||||
}
|
||||
*op++ = 0;
|
||||
strcpy(op, WASABI_API_LNGSTRING(IDS_MPEG_AUDIO_FILES));
|
||||
while (op && *op) op++;
|
||||
p = config_extlist;
|
||||
s = 0;
|
||||
while (p && *p)
|
||||
{
|
||||
while (ISSEP(*p)) p++;
|
||||
if (!p || !*p) break;
|
||||
if (s) *op++ = ';';
|
||||
s = 1;
|
||||
*op++ = '*';
|
||||
*op++ = '.';
|
||||
while (p && *p && !ISSEP(*p)) *op++ = *p++;
|
||||
}
|
||||
*op++ = ')';
|
||||
*op++ = 0;
|
||||
*op++ = 0;
|
||||
return list;
|
||||
}
|
||||
|
||||
void config(HWND hwndParent)
|
||||
{
|
||||
wchar_t title[128] = {0};
|
||||
int x;
|
||||
PROPSHEETHEADER pshead;
|
||||
PROPSHEETPAGE pspage[5];
|
||||
ZeroMemory(&pshead, sizeof(PROPSHEETHEADER));
|
||||
pshead.dwSize = sizeof(PROPSHEETHEADER);
|
||||
pshead.hwndParent = hwndParent;
|
||||
pshead.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP;
|
||||
pshead.hInstance = WASABI_API_LNG_HINST;
|
||||
pshead.pszCaption = WASABI_API_LNGSTRINGW_BUF(IDS_MPEG_AUDIO_DECODER_SETTINGS,title,128);//"MPEG Audio Decoder Settings";
|
||||
pshead.nPages = sizeof(pspage) / sizeof(pspage[0]);
|
||||
pshead.nStartPage = config_lp;
|
||||
pshead.ppsp = pspage;
|
||||
|
||||
ZeroMemory(pspage, sizeof(pspage));
|
||||
for ( x = 0; x < sizeof(pspage) / sizeof(pspage[0]); x ++)
|
||||
pspage[x].dwSize = sizeof(PROPSHEETPAGE);
|
||||
for ( x = 0; x < sizeof(pspage) / sizeof(pspage[0]); x ++)
|
||||
pspage[x].hInstance = WASABI_API_LNG_HINST;
|
||||
pspage[0].pszTemplate = MAKEINTRESOURCE(IDD_PREFS);
|
||||
pspage[1].pszTemplate = MAKEINTRESOURCE(IDD_TAGOPTS);
|
||||
pspage[2].pszTemplate = MAKEINTRESOURCE(IDD_ADVANCED_TAGGING);
|
||||
pspage[3].pszTemplate = MAKEINTRESOURCE(IDD_OUTPUT);
|
||||
pspage[4].pszTemplate = MAKEINTRESOURCE(IDD_HTTP);
|
||||
pspage[0].pfnDlgProc = prefsProc;
|
||||
pspage[1].pfnDlgProc = id3Proc;
|
||||
pspage[2].pfnDlgProc = advancedTaggingProc;
|
||||
pspage[3].pfnDlgProc = outputProc;
|
||||
pspage[4].pfnDlgProc = httpProc;
|
||||
PropertySheet((PROPSHEETHEADER*)&pshead);
|
||||
config_write();
|
||||
extern char *g_fileassos;
|
||||
mod.FileExtensions = getfileextensions();
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK id3Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
if (config_parse_id3v1) CheckDlgButton(hwndDlg, IDC_READ_ID3V1, BST_CHECKED);
|
||||
if (config_parse_id3v2) CheckDlgButton(hwndDlg, IDC_READ_ID3V2, BST_CHECKED);
|
||||
|
||||
if (config_write_id3v1) CheckDlgButton(hwndDlg, IDC_WRITE_ID3V1, BST_CHECKED);
|
||||
if (config_write_id3v2) CheckDlgButton(hwndDlg, IDC_WRITE_ID3V2, BST_CHECKED);
|
||||
|
||||
if (config_create_id3v1) CheckDlgButton(hwndDlg, IDC_CREATE_ID3V1, BST_CHECKED);
|
||||
if (config_create_id3v2) CheckDlgButton(hwndDlg, IDC_CREATE_ID3V2, BST_CHECKED);
|
||||
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_LATIN_1));
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_SYSTEM_LANGUAGE));
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_SETCURSEL,(config_read_mode == READ_LOCAL),0);
|
||||
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_UNICODE_UTF_16));
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_LATIN_1));
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_SYSTEM_LANGUAGE));
|
||||
SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_SETCURSEL,config_write_mode,0);
|
||||
|
||||
SetDlgItemTextA(hwndDlg,IDC_RATING_EMAIL,(config_rating_email[0] ? config_rating_email : "rating@winamp.com\0"));
|
||||
|
||||
return FALSE;
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR pnmh = (LPNMHDR) lParam;
|
||||
if (pnmh->code == PSN_SETACTIVE)
|
||||
{
|
||||
config_lp = 1;
|
||||
}
|
||||
if (pnmh->code == PSN_APPLY)
|
||||
{
|
||||
config_parse_id3v1 = IsDlgButtonChecked(hwndDlg, IDC_READ_ID3V1);
|
||||
config_parse_id3v2 = IsDlgButtonChecked(hwndDlg, IDC_READ_ID3V2);
|
||||
|
||||
config_write_id3v1 = IsDlgButtonChecked(hwndDlg, IDC_WRITE_ID3V1);
|
||||
config_write_id3v2 = IsDlgButtonChecked(hwndDlg, IDC_WRITE_ID3V2);
|
||||
|
||||
config_create_id3v1 = IsDlgButtonChecked(hwndDlg, IDC_CREATE_ID3V1);
|
||||
config_create_id3v2 = IsDlgButtonChecked(hwndDlg, IDC_CREATE_ID3V2);
|
||||
|
||||
GetDlgItemTextA(hwndDlg,IDC_RATING_EMAIL,config_rating_email,sizeof(config_rating_email));
|
||||
if (!stricmp(config_rating_email, "rating@winamp.com\0")) config_rating_email[0] = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_COMBO1:
|
||||
if(HIWORD(wParam) == CBN_SELCHANGE)
|
||||
{
|
||||
int cur = (int)SendMessage((HWND)lParam,CB_GETCURSEL,0,0);
|
||||
if(!cur) config_read_mode = READ_LATIN;
|
||||
else if(cur == 1) config_read_mode = READ_LOCAL;
|
||||
}
|
||||
break;
|
||||
case IDC_COMBO2:
|
||||
if(HIWORD(wParam) == CBN_SELCHANGE)
|
||||
{
|
||||
int cur = (int)SendMessage((HWND)lParam,CB_GETCURSEL,0,0);
|
||||
if(!cur) config_write_mode = WRITE_UTF16;
|
||||
else if(cur == 1) config_write_mode = WRITE_LATIN;
|
||||
else if(cur == 2) config_write_mode = WRITE_LOCAL;
|
||||
}
|
||||
break;
|
||||
case IDC_RATING_EMAIL_RESET:
|
||||
if(HIWORD(wParam) == BN_CLICKED)
|
||||
{
|
||||
config_rating_email[0] = 0;
|
||||
SetDlgItemTextA(hwndDlg,IDC_RATING_EMAIL,(config_rating_email[0] ? config_rating_email : "rating@winamp.com\0"));
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK advancedTaggingProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
|
||||
if (config_parse_apev2) CheckDlgButton(hwndDlg, IDC_READ_APEV2, BST_CHECKED);
|
||||
if (config_write_apev2) CheckDlgButton(hwndDlg, IDC_WRITE_APEV2, BST_CHECKED);
|
||||
if (config_create_apev2) CheckDlgButton(hwndDlg, IDC_CREATE_APEV2, BST_CHECKED);
|
||||
|
||||
if (config_parse_lyrics3) CheckDlgButton(hwndDlg, IDC_READ_LYRICS3, BST_CHECKED);
|
||||
|
||||
SendDlgItemMessage(hwndDlg,IDC_APEV2_HEADER_OPTIONS,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_APEV2_RETAIN_HEADER));
|
||||
SendDlgItemMessage(hwndDlg,IDC_APEV2_HEADER_OPTIONS,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_APEV2_ADD_HEADER));
|
||||
SendDlgItemMessage(hwndDlg,IDC_APEV2_HEADER_OPTIONS,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_APEV2_REMOVE_HEADER));
|
||||
SendDlgItemMessage(hwndDlg,IDC_APEV2_HEADER_OPTIONS,CB_SETCURSEL,config_apev2_header, 0);
|
||||
|
||||
return FALSE;
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR pnmh = (LPNMHDR) lParam;
|
||||
if (pnmh->code == PSN_SETACTIVE)
|
||||
{
|
||||
config_lp = 2;
|
||||
}
|
||||
if (pnmh->code == PSN_APPLY)
|
||||
{
|
||||
config_parse_apev2 = IsDlgButtonChecked(hwndDlg, IDC_READ_APEV2);
|
||||
config_write_apev2 = IsDlgButtonChecked(hwndDlg, IDC_WRITE_APEV2);
|
||||
config_create_apev2 = IsDlgButtonChecked(hwndDlg, IDC_CREATE_APEV2);
|
||||
|
||||
config_parse_lyrics3 = IsDlgButtonChecked(hwndDlg, IDC_READ_LYRICS3);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_APEV2_HEADER_OPTIONS:
|
||||
if(HIWORD(wParam) == CBN_SELCHANGE)
|
||||
{
|
||||
int cur = (int)SendMessage((HWND)lParam,CB_GETCURSEL,0,0);
|
||||
if(!cur) config_apev2_header = RETAIN_HEADER;
|
||||
else if(cur == 1) config_apev2_header = ADD_HEADER;
|
||||
else if(cur == 2) config_apev2_header = REMOVE_HEADER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK prefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
SetDlgItemTextA(hwndDlg, IDC_EDIT1, config_extlist);
|
||||
SendDlgItemMessage(hwndDlg, IDC_EDIT1, EM_LIMITTEXT, 128, 0);
|
||||
{
|
||||
wchar_t str[10] = L"";
|
||||
wsprintf(str, L"%d", config_max_bufsize_k);
|
||||
SetDlgItemText(hwndDlg, IDC_BUFMAX, str);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_BUFMAX), EM_LIMITTEXT, 5, 0);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_BUTTON1:
|
||||
SetDlgItemText(hwndDlg, IDC_EDIT1, DEF_EXT_LISTW);
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR pnmh = (LPNMHDR) lParam;
|
||||
if (pnmh->code == PSN_SETACTIVE)
|
||||
{
|
||||
config_lp = 0;
|
||||
}
|
||||
if (pnmh->code == PSN_APPLY)
|
||||
{
|
||||
config_max_bufsize_k = GetDlgItemInt(hwndDlg, IDC_BUFMAX, NULL, 0);
|
||||
GetDlgItemTextA(hwndDlg, IDC_EDIT1, config_extlist, 128);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK outputProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
if (config_eqmode&1) CheckDlgButton(hwndDlg, IDC_RADIO2, 1);
|
||||
else CheckDlgButton(hwndDlg, IDC_RADIO1, 1);
|
||||
|
||||
if (!(config_eqmode&4)) CheckDlgButton(hwndDlg, IDC_FASTL3EQ, 1);
|
||||
if (config_eqmode&8) CheckDlgButton(hwndDlg, IDC_FASTL12EQ, 1);
|
||||
if (config_miscopts&1) CheckDlgButton(hwndDlg, IDC_CHECK1, BST_CHECKED);
|
||||
if (config_miscopts&2) CheckDlgButton(hwndDlg, IDC_CHECK2, BST_CHECKED);
|
||||
if (config_downmix == 2) CheckDlgButton(hwndDlg, IDC_REVSTEREO, BST_CHECKED);
|
||||
if (config_downsample == 1)
|
||||
CheckDlgButton(hwndDlg, IDC_HALFRATE, BST_CHECKED);
|
||||
else if (config_downsample == 2)
|
||||
CheckDlgButton(hwndDlg, IDC_QRATE, BST_CHECKED);
|
||||
else
|
||||
CheckDlgButton(hwndDlg, IDC_FULLRATE, BST_CHECKED);
|
||||
return FALSE;
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR pnmh = (LPNMHDR) lParam;
|
||||
if (pnmh->code == PSN_SETACTIVE)
|
||||
{
|
||||
config_lp = 3;
|
||||
}
|
||||
|
||||
if (pnmh->code == PSN_APPLY)
|
||||
{
|
||||
config_miscopts &= ~3;
|
||||
config_miscopts |= IsDlgButtonChecked(hwndDlg, IDC_CHECK1) ? 1 : 0;
|
||||
config_miscopts |= IsDlgButtonChecked(hwndDlg, IDC_CHECK2) ? 2 : 0;
|
||||
|
||||
config_eqmode = IsDlgButtonChecked(hwndDlg, IDC_RADIO1) ? 0 : 1;
|
||||
config_eqmode |= IsDlgButtonChecked(hwndDlg, IDC_FASTL3EQ) ? 0 : 4;
|
||||
config_eqmode |= IsDlgButtonChecked(hwndDlg, IDC_FASTL12EQ) ? 8 : 0;
|
||||
|
||||
config_downmix = IsDlgButtonChecked(hwndDlg, IDC_REVSTEREO) ? 2 : 0;
|
||||
|
||||
config_downsample = IsDlgButtonChecked(hwndDlg, IDC_HALFRATE) ? 1 : 0;
|
||||
config_downsample = IsDlgButtonChecked(hwndDlg, IDC_QRATE) ? 2 : config_downsample;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void SetHTTPSaveButtonText(HWND hwndDlg, char* path)
|
||||
{
|
||||
HWND control = GetDlgItem(hwndDlg, IDC_BUTTON2);
|
||||
HDC hdc = GetDC(control);
|
||||
RECT r = {0};
|
||||
char temp[MAX_PATH] = {0};
|
||||
|
||||
lstrcpynA(temp, path, MAX_PATH);
|
||||
SelectObject(hdc, (HFONT)SendMessage(control, WM_GETFONT, 0, 0));
|
||||
GetClientRect(control, &r);
|
||||
r.left += 5;
|
||||
r.right -= 5;
|
||||
DrawTextA(hdc, temp, -1, &r, DT_PATH_ELLIPSIS|DT_WORD_ELLIPSIS|DT_MODIFYSTRING);
|
||||
SetWindowTextA(control, temp);
|
||||
ReleaseDC(control, hdc);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK httpProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_CHECK2:
|
||||
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), IsDlgButtonChecked(hwndDlg, IDC_CHECK2));
|
||||
break;
|
||||
case IDC_BUTTON2:
|
||||
{
|
||||
BROWSEINFO bi = {0};
|
||||
wchar_t name[MAX_PATH] = {0};
|
||||
bi.hwndOwner = hwndDlg;
|
||||
bi.pszDisplayName = name;
|
||||
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
|
||||
bi.lpfn = BrowseCallbackProc;
|
||||
LPITEMIDLIST idlist = SHBrowseForFolder(&bi);
|
||||
if (idlist)
|
||||
{
|
||||
SHGetPathFromIDListA(idlist, g_http_tmp);
|
||||
IMalloc *m = 0;
|
||||
SHGetMalloc(&m);
|
||||
m->Free(idlist);
|
||||
SetHTTPSaveButtonText(hwndDlg, g_http_tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
case WM_INITDIALOG:
|
||||
SetDlgItemInt(hwndDlg, IDC_BUFFERS_NUMBUFS, config_http_buffersize, 0);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER), TBM_SETRANGEMAX, 0, 50);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER), TBM_SETRANGEMIN, 0, 0);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER), TBM_SETPOS, 1, config_http_prebuffer / 2);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER2), TBM_SETRANGEMAX, 0, 50);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER2), TBM_SETRANGEMIN, 0, 0);
|
||||
SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER2), TBM_SETPOS, 1, config_http_prebuffer_underrun / 2);
|
||||
CheckDlgButton(hwndDlg, IDC_CHECK1, allow_sctitles);
|
||||
CheckDlgButton(hwndDlg, IDC_SC_ARTWORK, allow_scartwork);
|
||||
CheckDlgButton(hwndDlg, IDC_CHECK3, sctitle_format);
|
||||
|
||||
if (config_miscopts&16)
|
||||
{
|
||||
CheckDlgButton(hwndDlg, IDC_CHECK2, BST_CHECKED);
|
||||
}
|
||||
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), (config_miscopts&16));
|
||||
SetHTTPSaveButtonText(hwndDlg, config_http_save_dir);
|
||||
lstrcpynA(g_http_tmp, config_http_save_dir, MAX_PATH);
|
||||
|
||||
return FALSE;
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR pnmh = (LPNMHDR) lParam;
|
||||
if (pnmh->code == PSN_SETACTIVE)
|
||||
{
|
||||
config_lp = 4;
|
||||
}
|
||||
|
||||
if (pnmh->code == PSN_APPLY)
|
||||
{
|
||||
sctitle_format = !!IsDlgButtonChecked(hwndDlg, IDC_CHECK3);
|
||||
allow_sctitles = !!IsDlgButtonChecked(hwndDlg, IDC_CHECK1);
|
||||
allow_scartwork = !!IsDlgButtonChecked(hwndDlg, IDC_SC_ARTWORK);
|
||||
{
|
||||
int s;
|
||||
int t;
|
||||
t = GetDlgItemInt(hwndDlg, IDC_BUFFERS_NUMBUFS, &s, 0);
|
||||
if (s) config_http_buffersize = t;
|
||||
if (config_http_buffersize < 16) config_http_buffersize = 16;
|
||||
}
|
||||
config_http_prebuffer = (int)SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER), TBM_GETPOS, 0, 0) * 2;
|
||||
config_http_prebuffer_underrun = (int)SendMessage(GetDlgItem(hwndDlg, IDC_PREBUFSLIDER2), TBM_GETPOS, 0, 0) * 2;
|
||||
lstrcpynA(config_http_save_dir, g_http_tmp, MAX_PATH);
|
||||
if (IsDlgButtonChecked(hwndDlg, IDC_CHECK2))
|
||||
{
|
||||
config_miscopts |= 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
config_miscopts &= ~16;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const int controls[] =
|
||||
{
|
||||
IDC_PREBUFSLIDER,
|
||||
IDC_PREBUFSLIDER2,
|
||||
};
|
||||
if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
|
||||
{
|
||||
MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0};
|
||||
msgbx.lpszText = message;
|
||||
msgbx.lpszCaption = title;
|
||||
msgbx.lpszIcon = MAKEINTRESOURCE(102);
|
||||
msgbx.hInstance = GetModuleHandle(0);
|
||||
msgbx.dwStyle = MB_USERICON;
|
||||
msgbx.hwndOwner = parent;
|
||||
return MessageBoxIndirectW(&msgbx);
|
||||
}
|
||||
|
||||
void about(HWND hwndParent)
|
||||
{
|
||||
wchar_t message[1024] = {0}, text[1024] = {0};
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_MPEG_AUDIO_DECODER_OLD,text,1024);
|
||||
wsprintfW(message, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
|
||||
mod.description, __DATE__);
|
||||
DoAboutMessageBox(hwndParent,text,message);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef NULLSOFT_IN_MP3_CONFIG_H
|
||||
#define NULLSOFT_IN_MP3_CONFIG_H
|
||||
|
||||
#include <bfc/platform/guid.h>
|
||||
enum
|
||||
{
|
||||
WRITE_UTF16 = 0,
|
||||
WRITE_LATIN = 1,
|
||||
WRITE_LOCAL = 2,
|
||||
WRITE_UTF8 = 3,
|
||||
};
|
||||
extern int config_write_mode;
|
||||
|
||||
enum
|
||||
{
|
||||
READ_LATIN = 0,
|
||||
READ_LOCAL = 1,
|
||||
};
|
||||
|
||||
extern int config_read_mode;
|
||||
|
||||
extern int config_parse_apev2;
|
||||
extern int config_parse_lyrics3;
|
||||
extern int config_parse_id3v1;
|
||||
extern int config_parse_id3v2;
|
||||
|
||||
extern int config_write_apev2;
|
||||
extern int config_write_id3v1;
|
||||
extern int config_write_id3v2;
|
||||
|
||||
extern int config_create_id3v1;
|
||||
extern int config_create_id3v2;
|
||||
extern int config_create_apev2;
|
||||
|
||||
enum
|
||||
{
|
||||
RETAIN_HEADER = 0,
|
||||
ADD_HEADER = 1,
|
||||
REMOVE_HEADER = 2,
|
||||
};
|
||||
extern int config_apev2_header;
|
||||
|
||||
extern int config_id3v2_version;
|
||||
extern int config_lp;
|
||||
extern char config_rating_email[255];
|
||||
|
||||
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
|
||||
static const GUID playbackConfigGroupGUID =
|
||||
{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
|
||||
/***************************************************************************\
|
||||
*
|
||||
* (C) copyright Fraunhofer - IIS (1998)
|
||||
* All Rights Reserved
|
||||
*
|
||||
* filename: giofile.h
|
||||
* project : MPEG Decoder
|
||||
* author : Martin Sieler
|
||||
* date : 1998-02-11
|
||||
* contents/description: HEADER - file I/O class for MPEG Decoder
|
||||
*
|
||||
*
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef _GIOFILE_H
|
||||
#define _GIOFILE_H
|
||||
|
||||
/* ------------------------ includes --------------------------------------*/
|
||||
#include <windows.h>
|
||||
|
||||
#include "CVbriHeader.h"
|
||||
#include "jnetlib/jnetlib.h"
|
||||
|
||||
#include "../vlb/dataio.h"
|
||||
#include "ID3v2.h"
|
||||
#include "LAMEInfo.h"
|
||||
#include "../nu/RingBuffer.h"
|
||||
#include "../apev2/tag.h"
|
||||
#include "ifc_mpeg_stream_reader.h"
|
||||
|
||||
/*-------------------------- defines --------------------------------------*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
class CGioFile : public DataIOControl, public ifc_mpeg_stream_reader
|
||||
{
|
||||
public:
|
||||
CGioFile();
|
||||
virtual ~CGioFile();
|
||||
|
||||
int Open(const wchar_t *pszName, int maxbufsizek);
|
||||
int Close();
|
||||
int Read(void *pBuffer, int cbToRead, int *pcbRead);
|
||||
int Peek(void *pBuffer, int cbToRead, int *pcbRead);
|
||||
|
||||
//dataiocontrol interface
|
||||
int IO(void *buf, int size, int count)
|
||||
{
|
||||
int l=0;
|
||||
Read(buf,count,&l);
|
||||
return l;
|
||||
}
|
||||
int Seek(long offset, int origin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//int Close() { return 0; }
|
||||
int EndOf(void)
|
||||
{
|
||||
return IsEof();
|
||||
}
|
||||
int DICGetLastError()
|
||||
{
|
||||
return DATA_IO_ERROR_NONE;
|
||||
}
|
||||
int DICGetDirection()
|
||||
{
|
||||
return DATA_IO_READ;
|
||||
}
|
||||
|
||||
unsigned int GetAvgVBRBitrate(void)
|
||||
{
|
||||
if (m_vbr_ms && m_vbr_frames)
|
||||
{
|
||||
if (m_vbr_bytes && encodingMethod != ENCODING_METHOD_CBR)
|
||||
return (unsigned int)(m_vbr_bytes * 8 / m_vbr_ms);
|
||||
else
|
||||
return (unsigned int)(mpeg_length * 8 / m_vbr_ms);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
bool IsEof() const;
|
||||
bool lengthVerified;
|
||||
bool isSeekReset()
|
||||
{
|
||||
|
||||
if (m_is_stream && m_seek_reset && m_is_stream_seek)
|
||||
{
|
||||
m_seek_reset = false;
|
||||
m_is_stream_seek = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsStreamSeekable()
|
||||
{
|
||||
return m_is_stream_seekable;
|
||||
}
|
||||
|
||||
int GetHeaderOffset();
|
||||
|
||||
int PercentAvailable()
|
||||
{
|
||||
if (!m_is_stream) return 0;
|
||||
if (!recvbuffersize) return 0;
|
||||
if (!m_connection) return 0;
|
||||
if (constate != 5) return 0;
|
||||
uint64_t bytes_100 = jnl_connection_receive_bytes_available(m_connection)*100;
|
||||
bytes_100 /= recvbuffersize;
|
||||
return (int)bytes_100;
|
||||
}
|
||||
|
||||
int RunStream()
|
||||
{
|
||||
if (m_is_stream && m_connection)
|
||||
{
|
||||
if (constate != 5) Read(NULL,0,NULL);
|
||||
else jnl_connection_run(m_connection, -1, -1, NULL, NULL);
|
||||
int p=jnl_connection_get_state(m_connection);
|
||||
if (p==JNL_CONNECTION_STATE_ERROR||
|
||||
p==JNL_CONNECTION_STATE_CLOSED)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int IsStream()
|
||||
{
|
||||
return m_is_stream;
|
||||
}
|
||||
void GetStreamInfo(wchar_t *, size_t len);
|
||||
|
||||
DWORD GetContentLength(void) const;
|
||||
DWORD GetCurrentPosition(void) const;
|
||||
void SetCurrentPosition(long dwPos, int How);
|
||||
|
||||
enum { GIO_FILE_BEGIN, GIO_FILE_CURRENT, GIO_FILE_END };
|
||||
|
||||
uint64_t mpeg_length; /* length of valid audio data */
|
||||
uint64_t mpeg_position; /* starting position of first valid decodable non-header MPEG frame */
|
||||
uint64_t file_position; /* position within the MPEG data that we've read so far */
|
||||
|
||||
uint64_t m_vbr_bytes;
|
||||
int m_vbr_frames, m_vbr_ms;
|
||||
int encodingMethod;
|
||||
uint64_t m_vbr_samples;
|
||||
int prepad, postpad;
|
||||
void Seek(int posms, int br);
|
||||
bool IsSeekable();
|
||||
|
||||
unsigned char id3v1_data[128];
|
||||
|
||||
char stream_url[256];
|
||||
char stream_name[256];
|
||||
char stream_genre[256];
|
||||
char stream_current_title[256];
|
||||
|
||||
char *m_content_type;
|
||||
int uvox_last_message;
|
||||
char *uvox_3901;
|
||||
char *uvox_3902;
|
||||
|
||||
typedef struct {
|
||||
char *uvox_stream_artwork;
|
||||
int uvox_stream_artwork_len;
|
||||
int uvox_stream_artwork_type;
|
||||
char *uvox_playing_artwork;
|
||||
int uvox_playing_artwork_len;
|
||||
int uvox_playing_artwork_type;
|
||||
} UVOX_ARTWORK;
|
||||
UVOX_ARTWORK uvox_artwork;
|
||||
|
||||
ID3v2 info;
|
||||
float GetGain();
|
||||
unsigned char m_vbr_toc[100];
|
||||
int m_vbr_frame_len;
|
||||
int m_vbr_flag;
|
||||
CVbriHeader *m_vbr_hdr;
|
||||
|
||||
FILETIME last_write_time;
|
||||
|
||||
void *GetID3v1()
|
||||
{
|
||||
if (m_id3v1_len == 128)
|
||||
return id3v1_data;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *GetID3v2(uint32_t *len)
|
||||
{
|
||||
if (stream_id3v2_buf)
|
||||
{
|
||||
*len = stream_id3v2_read;
|
||||
return stream_id3v2_buf;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *GetLyrics3(uint32_t *len)
|
||||
{
|
||||
if (lyrics3_data)
|
||||
{
|
||||
*len = lyrics3_size;
|
||||
return lyrics3_data;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *GetAPEv2(uint32_t *len)
|
||||
{
|
||||
if (apev2_data)
|
||||
{
|
||||
*len = m_apev2_len;
|
||||
return apev2_data;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
void ReadiTunesGaps();
|
||||
private:
|
||||
/* ID3v2 */
|
||||
int m_id3v1_len;
|
||||
|
||||
/* ID3v2 */
|
||||
uint32_t stream_id3v2_read;
|
||||
char *stream_id3v2_buf;
|
||||
int m_id3v2_len;
|
||||
|
||||
/* Lyrics3 */
|
||||
uint32_t lyrics3_size;
|
||||
char *lyrics3_data;
|
||||
|
||||
/* APEv2 */
|
||||
uint32_t m_apev2_len;
|
||||
char *apev2_data;
|
||||
APEv2::Tag apev2;
|
||||
|
||||
int m_is_stream;
|
||||
jnl_dns_t m_dns;
|
||||
jnl_connection_t m_connection;
|
||||
char last_full_url[4096];
|
||||
int is_stream_seek;
|
||||
char *host;
|
||||
char *proxy_host;
|
||||
char *req;
|
||||
unsigned short port;
|
||||
char *lpinfo;
|
||||
char *proxy_lp;
|
||||
char *request;
|
||||
int constate;
|
||||
char save_filename[256];
|
||||
char server_name[128];
|
||||
int recvbuffersize;
|
||||
|
||||
int64_t stream_bytes_read;
|
||||
int64_t stream_metabytes_read;
|
||||
unsigned int timeout_start;
|
||||
char force_lpinfo[256];
|
||||
unsigned char *m_full_buffer;
|
||||
int is_uvox;
|
||||
int uvox_stream_data_len;
|
||||
int uvox_message_cnt, uvox_desyncs;
|
||||
int uvox_sid,uvox_maxbr, uvox_avgbr,uvox_maxmsg;
|
||||
|
||||
unsigned char *uvox_stream_data;
|
||||
|
||||
char *uvox_meta[2][32];
|
||||
int meta_interval,meta_pos;
|
||||
uint64_t m_full_buffer_pos/*,m_full_buffer_len*/;
|
||||
char stream_title_save[580];
|
||||
char last_title_sent[256];
|
||||
|
||||
RingBuffer peekBuffer;
|
||||
//unsigned char m_peekbuf[8192];
|
||||
//int m_peekbuf_used;
|
||||
bool no_peek_hack;
|
||||
|
||||
int doConnect(const char *str, int start_offset);
|
||||
void processMetaData(char *data, int lent, int msgId = 0);
|
||||
|
||||
int m_redircnt;
|
||||
int m_auth_tries;
|
||||
|
||||
HANDLE hFile;
|
||||
bool fEof;
|
||||
|
||||
|
||||
char dlg_realm[256];
|
||||
static INT_PTR CALLBACK httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
|
||||
int m_http_response;
|
||||
bool m_seek_reset;
|
||||
bool m_is_stream_seek;
|
||||
bool m_is_stream_seekable;
|
||||
bool m_useaproxy;
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
#endif
|
||||
@@ -0,0 +1,452 @@
|
||||
#include ".\filterwater.h"
|
||||
#include <math.h>
|
||||
|
||||
#define random( min, max ) (( rand() % (int)((( max ) + 1 ) - ( min ))) + ( min ))
|
||||
|
||||
MLImageFilterWater::MLImageFilterWater(void)
|
||||
{
|
||||
hField1 = NULL;
|
||||
hField2 = NULL;
|
||||
hHandle = NULL;
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
|
||||
drawWithLight = TRUE;
|
||||
lightModifier = 1;
|
||||
hPage = 0;
|
||||
density = 5;
|
||||
|
||||
}
|
||||
|
||||
MLImageFilterWater::~MLImageFilterWater(void)
|
||||
{
|
||||
ClearData();
|
||||
}
|
||||
|
||||
void MLImageFilterWater::ClearData(void)
|
||||
{
|
||||
if (hHandle)
|
||||
{
|
||||
if (hField1) HeapFree(hHandle, NULL, hField1);
|
||||
if (hField2) HeapFree(hHandle, NULL, hField2);
|
||||
HeapDestroy(hHandle);
|
||||
hField1 = NULL;
|
||||
hField2 = NULL;
|
||||
hHandle = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOL MLImageFilterWater::CreateFor(const MLImage *image)
|
||||
{
|
||||
ClearData();
|
||||
width = image->GetWidth();
|
||||
height = image->GetHeight();
|
||||
hPage = 0;
|
||||
int len = height * width * sizeof(int);
|
||||
hHandle = HeapCreate(NULL, 3*len, 3*len);
|
||||
if (!hHandle)
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hField1 = (int*)HeapAlloc(hHandle, HEAP_ZERO_MEMORY, len);
|
||||
hField2 = (int*)HeapAlloc(hHandle, HEAP_ZERO_MEMORY, len);
|
||||
return hField1 && hField2;
|
||||
}
|
||||
|
||||
void MLImageFilterWater::Render(MLImage* destination, const MLImage* source)
|
||||
{
|
||||
if(!drawWithLight) DrawWaterNoLight(hPage, destination, source);
|
||||
else DrawWaterWithLight(destination, source);
|
||||
CalculateWater(hPage, density);
|
||||
// CalcWaterBigFilter(hPage, density);
|
||||
hPage ^= 1;
|
||||
}
|
||||
|
||||
void MLImageFilterWater::CalculateWater(int page, int density)
|
||||
{
|
||||
int newh;
|
||||
int count = width + 1;
|
||||
int *newptr;
|
||||
int *oldptr;
|
||||
|
||||
if(page == 0)
|
||||
{
|
||||
newptr = hField1;
|
||||
oldptr = hField2;
|
||||
}
|
||||
else
|
||||
{
|
||||
newptr = hField2;
|
||||
oldptr = hField1;
|
||||
}
|
||||
|
||||
int x, y;
|
||||
for (y = (height - 1) * width; count < y; count += 2)
|
||||
{
|
||||
for (x = count + width - 2; count < x; count++)
|
||||
{
|
||||
// This does the eight-pixel method.
|
||||
|
||||
newh = ((oldptr[count + width]
|
||||
+ oldptr[count - width]
|
||||
+ oldptr[count + 1]
|
||||
+ oldptr[count - 1]
|
||||
+ oldptr[count - width - 1]
|
||||
+ oldptr[count - width + 1]
|
||||
+ oldptr[count + width - 1]
|
||||
+ oldptr[count + width + 1]
|
||||
) >> 2 )
|
||||
- newptr[count];
|
||||
newptr[count] = newh - (newh >> density);
|
||||
/*
|
||||
// This is the "sludge" method...
|
||||
newh = (oldptr[count]<<2)
|
||||
+ oldptr[count-1-m_iWidth]
|
||||
+ oldptr[count+1-m_iWidth]
|
||||
+ oldptr[count-1+m_iWidth]
|
||||
+ oldptr[count+1+m_iWidth]
|
||||
+ ((oldptr[count-1]
|
||||
+ oldptr[count+1]
|
||||
+ oldptr[count-m_iWidth]
|
||||
+ oldptr[count+m_iWidth])<<1);
|
||||
|
||||
newptr[count] = (newh-(newh>>6)) >> density;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilterWater::SmoothWater(int page)
|
||||
{
|
||||
int newh;
|
||||
int count = width + 1;
|
||||
|
||||
int *newptr;
|
||||
int *oldptr;
|
||||
|
||||
if(page == 0)
|
||||
{
|
||||
newptr = hField1;
|
||||
oldptr = hField2;
|
||||
}
|
||||
else
|
||||
{
|
||||
newptr = hField2;
|
||||
oldptr = hField1;
|
||||
}
|
||||
|
||||
int x, y;
|
||||
|
||||
for(y = 1; y < height; y++, count += 2)
|
||||
{
|
||||
for( x = 1; x < width; x++, count++)
|
||||
{
|
||||
// This does the eight-pixel method.
|
||||
|
||||
newh = ((oldptr[count + width]
|
||||
+ oldptr[count - width]
|
||||
+ oldptr[count + 1]
|
||||
+ oldptr[count - 1]
|
||||
+ oldptr[count - width - 1]
|
||||
+ oldptr[count - width + 1]
|
||||
+ oldptr[count + width - 1]
|
||||
+ oldptr[count + width + 1]
|
||||
) >> 3 )
|
||||
+ newptr[count];
|
||||
newptr[count] = newh>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
void MLImageFilterWater::FlattenWater(void)
|
||||
{
|
||||
int len = width * height * sizeof(int);
|
||||
SecureZeroMemory(hField1, len);
|
||||
SecureZeroMemory(hField2, len);
|
||||
}
|
||||
void MLImageFilterWater::SineBlob(int x, int y, int radius, int height, int page)
|
||||
{
|
||||
int cx, cy;
|
||||
int left,top,right,bottom;
|
||||
double square, dist;
|
||||
double radsquare = radius * radius;
|
||||
double length = double((1024.0/(double)radius)*(1024.0/(double)radius));
|
||||
int *newptr;
|
||||
|
||||
if(page == 0)
|
||||
{
|
||||
newptr = hField1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newptr = hField2;
|
||||
}
|
||||
|
||||
int t = (this->width - 2*radius - 1);
|
||||
if (t == 0) t = 1;
|
||||
if(x<0) x = 1 + radius + rand() % t;
|
||||
t = (this->height - 2*radius - 1);
|
||||
if (t == 0) t = 1;
|
||||
if(y<0) y = 1 + radius + rand() % t;
|
||||
|
||||
radsquare = (radius*radius);
|
||||
|
||||
left = -radius; right = radius;
|
||||
top = -radius; bottom = radius;
|
||||
|
||||
// Perform edge clipping...
|
||||
if(x - radius < 1) left -= (x-radius-1);
|
||||
if(y - radius < 1) top -= (y-radius-1);
|
||||
if(x + radius > this->width - 1) right -= (x + radius - this->width + 1);
|
||||
if(y + radius > this->height - 1) bottom -= (y + radius - this->height + 1);
|
||||
|
||||
for(cy = top; cy < bottom; cy++)
|
||||
{
|
||||
for(cx = left; cx < right; cx++)
|
||||
{
|
||||
square = cy*cy + cx*cx;
|
||||
if(square < radsquare)
|
||||
{
|
||||
dist = sqrt(square*length);
|
||||
newptr[this->width*(cy+y) + cx+x] += (int)((cos(dist)+0xffff)*(height)) >> 19;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilterWater::WarpBlob(int x, int y, int radius, int height, int page)
|
||||
{
|
||||
int cx, cy;
|
||||
int left,top,right,bottom;
|
||||
int square;
|
||||
int radsquare = radius * radius;
|
||||
int *newptr;
|
||||
|
||||
if(page == 0)
|
||||
{
|
||||
newptr = hField1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newptr = hField2;
|
||||
}
|
||||
|
||||
radsquare = (radius*radius);
|
||||
|
||||
height /= 64;
|
||||
|
||||
left=-radius; right = radius;
|
||||
top=-radius; bottom = radius;
|
||||
|
||||
// Perform edge clipping...
|
||||
if(x - radius < 1) left -= (x-radius-1);
|
||||
if(y - radius < 1) top -= (y-radius-1);
|
||||
if(x + radius > this->width-1) right -= (x+ radius - this->width + 1);
|
||||
if(y + radius > this->height-1) bottom-= (y + radius - this->height + 1);
|
||||
|
||||
for(cy = top; cy < bottom; cy++)
|
||||
{
|
||||
for(cx = left; cx < right; cx++)
|
||||
{
|
||||
square = cy*cy + cx*cx;
|
||||
if(square < radsquare)
|
||||
{
|
||||
newptr[this->width*(cy+y) + cx+x] += int((radius-sqrt((float)square))*(float)(height));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void MLImageFilterWater::HeightBox (int x, int y, int radius, int height, int page)
|
||||
{
|
||||
int cx, cy;
|
||||
int left,top,right,bottom;
|
||||
int *newptr;
|
||||
|
||||
if(page == 0)
|
||||
{
|
||||
newptr = hField1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newptr = hField2;
|
||||
}
|
||||
|
||||
int t = (this->width - 2*radius - 1);
|
||||
if (t == 0) t = 1;
|
||||
if(x<0) x = 1 + radius + rand() % t;
|
||||
t = (this->height - 2*radius - 1);
|
||||
if (t == 0) t = 1;
|
||||
if(y<0) y = 1 + radius + rand() % t;
|
||||
|
||||
left=-radius; right = radius;
|
||||
top=-radius; bottom = radius;
|
||||
|
||||
// Perform edge clipping...
|
||||
if(x - radius < 1) left -= (x-radius-1);
|
||||
if(y - radius < 1) top -= (y-radius-1);
|
||||
if(x + radius > this->width-1) right -= (x+ radius - this->width + 1);
|
||||
if(y + radius > this->height-1) bottom-= (y + radius - this->height + 1);
|
||||
|
||||
for(cy = top; cy < bottom; cy++)
|
||||
{
|
||||
for(cx = left; cx < right; cx++)
|
||||
{
|
||||
newptr[this->width*(cy+y) + cx+x] = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
void MLImageFilterWater::HeightBlob(int x, int y, int radius, int height, int page)
|
||||
{
|
||||
int rquad;
|
||||
int cx, cy;
|
||||
int left, top, right, bottom;
|
||||
|
||||
rquad = radius * radius;
|
||||
|
||||
// Make a randomly-placed blob...
|
||||
int t = (this->width - 2*radius - 1);
|
||||
if (t == 0) t = 1;
|
||||
if(x<0) x = 1 + radius + rand() % t;
|
||||
t = (this->height - 2*radius - 1);
|
||||
if (t == 0) t = 1;
|
||||
if(y<0) y = 1 + radius + rand() % t;
|
||||
|
||||
left = -radius; right = radius;
|
||||
top = -radius; bottom = radius;
|
||||
|
||||
// Perform edge clipping...
|
||||
if(x - radius < 1) left -= (x-radius-1);
|
||||
if(y - radius < 1) top -= (y-radius-1);
|
||||
if(x + radius > this->width-1) right -= (x+ radius - this->width + 1);
|
||||
if(y + radius > this->height-1) bottom-= (y + radius - this->height + 1);
|
||||
|
||||
for(cy = top; cy < bottom; cy++)
|
||||
{
|
||||
int cyq = cy*cy;
|
||||
for(cx = left; cx < right; cx++)
|
||||
{
|
||||
if(cx*cx + cyq < rquad) newptr[this->width * (cy+y) + (cx+x)] += height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilterWater::CalcWaterBigFilter(int page, int density)
|
||||
{
|
||||
int newh;
|
||||
int count = (2 * width) + 2;
|
||||
|
||||
int *newptr;
|
||||
int *oldptr;
|
||||
|
||||
// Set up the pointers
|
||||
if(page == 0)
|
||||
{
|
||||
newptr = hField1;
|
||||
oldptr = hField2;
|
||||
}
|
||||
else
|
||||
{
|
||||
newptr = hField2;
|
||||
oldptr = hField1;
|
||||
}
|
||||
|
||||
int x, y;
|
||||
|
||||
for(y=2; y < height-2; y++, count += 4)
|
||||
{
|
||||
for(x=2; x < width-2; x++, count++)
|
||||
{
|
||||
// This does the 25-pixel method. It looks much okay.
|
||||
|
||||
newh = (
|
||||
(
|
||||
(
|
||||
(oldptr[count + width]
|
||||
+ oldptr[count - width]
|
||||
+ oldptr[count + 1]
|
||||
+ oldptr[count - 1]
|
||||
)<<1)
|
||||
+ ((oldptr[count - width - 1]
|
||||
+ oldptr[count - width + 1]
|
||||
+ oldptr[count + width - 1]
|
||||
+ oldptr[count + width + 1]))
|
||||
+ ( (
|
||||
oldptr[count - (width*2)]
|
||||
+ oldptr[count + (width*2)]
|
||||
+ oldptr[count - 2]
|
||||
+ oldptr[count + 2]
|
||||
) >> 1 )
|
||||
+ ( (
|
||||
oldptr[count - (width*2) - 1]
|
||||
+ oldptr[count - (width*2) + 1]
|
||||
+ oldptr[count + (width*2) - 1]
|
||||
+ oldptr[count + (width*2) + 1]
|
||||
+ oldptr[count - 2 - width]
|
||||
+ oldptr[count - 2 + width]
|
||||
+ oldptr[count + 2 - width]
|
||||
+ oldptr[count + 2 + width]
|
||||
) >> 2 )
|
||||
)
|
||||
>> 3)
|
||||
- (newptr[count]);
|
||||
|
||||
|
||||
newptr[count] = newh - (newh >> density);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilterWater::DrawWaterNoLight(int page, MLImage* destination, const MLImage* source)
|
||||
{
|
||||
unsigned int brk = width * height;
|
||||
|
||||
int *ptr = hField1;
|
||||
|
||||
DWORD *dataS = (DWORD*)source->GetData();
|
||||
DWORD *dataD = (DWORD*)destination->GetData();
|
||||
|
||||
for (unsigned int offset = 0; offset < brk; offset++)
|
||||
{
|
||||
int dx = ptr[offset] - ptr[offset + 1];
|
||||
int dy = ptr[offset] - ptr[offset + width];
|
||||
unsigned int index = offset + width * (dy>>3) + (dx>>3);
|
||||
dataD[offset] = (index < brk ) ? dataS[offset + width*(dy>>3) + (dx>>3)] : dataS[offset];
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilterWater::DrawWaterWithLight(MLImage* destination, const MLImage* source)
|
||||
{
|
||||
unsigned int brk = width * height;
|
||||
|
||||
int *ptr = hField1;
|
||||
|
||||
DWORD *dataS = (DWORD*)source->GetData();
|
||||
DWORD *dataD = (DWORD*)destination->GetData();
|
||||
|
||||
for (unsigned int offset = 0; offset < brk; offset++)
|
||||
{
|
||||
int dx = ptr[offset] - ptr[offset + 1];
|
||||
int dy = ptr[offset] - ptr[offset + width];
|
||||
unsigned int index = offset + width * (dy>>3) + (dx>>3);
|
||||
dataD[offset] = (index < brk ) ? GetShiftedColor(dataS[index], dx) : dataS[offset];
|
||||
}
|
||||
}
|
||||
COLORREF MLImageFilterWater::GetShiftedColor(COLORREF color,int shift)
|
||||
{
|
||||
int R,G, B;
|
||||
int r, g, b;
|
||||
|
||||
R = GetRValue(color)-shift;
|
||||
G = GetGValue(color)-shift;
|
||||
B = GetBValue(color)-shift;
|
||||
|
||||
r = (R < 0) ? 0 : (R > 255) ? 255 : R;
|
||||
g = (G < 0) ? 0 : (G > 255) ? 255 : G;
|
||||
b = (B < 0) ? 0 : (B > 255) ? 255 : B;
|
||||
|
||||
return RGB(r,g,b);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef NULLSOFT_ML_IMAGE_FILTERWATER_HEADER
|
||||
#define NULLSOFT_ML_IMAGE_FILTERWATER_HEADER
|
||||
|
||||
#include <windows.h>
|
||||
#include ".\image.h"
|
||||
|
||||
class MLImageFilterWater
|
||||
{
|
||||
public:
|
||||
MLImageFilterWater(void);
|
||||
~MLImageFilterWater(void);
|
||||
|
||||
public:
|
||||
BOOL CreateFor(const MLImage *image);
|
||||
void Render(MLImage* destination, const MLImage* source);
|
||||
|
||||
void CalculateWater(int page, int density);
|
||||
void SmoothWater(int page);
|
||||
void FlattenWater(void);
|
||||
|
||||
void SineBlob(int x, int y, int radius, int height, int page);
|
||||
void WarpBlob(int x, int y, int radius, int height, int page);
|
||||
void HeightBox (int x, int y, int radius, int height, int page);
|
||||
void HeightBlob(int x, int y, int radius, int height, int page);
|
||||
|
||||
protected:
|
||||
void ClearData(void);
|
||||
void CalcWaterBigFilter(int page, int density);
|
||||
void DrawWaterNoLight(int page,MLImage* destination, const MLImage* source);
|
||||
void DrawWaterWithLight(MLImage* destination, const MLImage* source);
|
||||
COLORREF GetShiftedColor(COLORREF color,int shift);
|
||||
|
||||
private:
|
||||
HANDLE hHandle;
|
||||
int height;
|
||||
int width;
|
||||
|
||||
BOOL drawWithLight;
|
||||
int lightModifier;
|
||||
int hPage;
|
||||
int density;
|
||||
|
||||
int* hField1;
|
||||
int* hField2;
|
||||
};
|
||||
|
||||
#endif //#define NULLSOFT_ML_IMAGE_FILTERWATER_HEADER
|
||||
@@ -0,0 +1,209 @@
|
||||
#include ".\image.h"
|
||||
|
||||
MLImage::MLImage(void)
|
||||
{
|
||||
loader = NULL;
|
||||
loaderDelete = TRUE;
|
||||
ResetData();
|
||||
}
|
||||
|
||||
MLImage::MLImage(IMGLOADFUNC loader, BOOL deleteDone)
|
||||
{
|
||||
ResetData();
|
||||
SetLoader(loader, deleteDone, FALSE);
|
||||
}
|
||||
|
||||
MLImage::MLImage(int width, int height)
|
||||
{
|
||||
loader = NULL;
|
||||
ResetData();
|
||||
Init(width,height);
|
||||
}
|
||||
|
||||
MLImage::~MLImage(void)
|
||||
{
|
||||
ResetData();
|
||||
}
|
||||
|
||||
INT_PTR MLImage::SetLoader(IMGLOADFUNC loader, BOOL deleteDone, BOOL forceLoad)
|
||||
{
|
||||
this->loader = loader;
|
||||
this->loaderDelete = deleteDone;
|
||||
if (loader && forceLoad) Load();
|
||||
return (loader != NULL) ? (INT_PTR) this : FALSE;
|
||||
}
|
||||
|
||||
BOOL MLImage::Load(void)
|
||||
{
|
||||
ResetData();
|
||||
|
||||
if (!loader) return FALSE;
|
||||
HBITMAP hbmpLoaded = loader((INT_PTR)this);
|
||||
if(hbmpLoaded == NULL) return FALSE;
|
||||
|
||||
BITMAP bi;
|
||||
if (GetObject(hbmpLoaded, sizeof(bi), &bi))
|
||||
{
|
||||
hbmp = ConvertTo32BppDIB(hbmpLoaded, bi.bmWidth, bi.bmHeight, &info, &data);
|
||||
}
|
||||
|
||||
if (loaderDelete) DeleteObject(hbmpLoaded);
|
||||
return (hbmp != NULL);
|
||||
}
|
||||
|
||||
void MLImage::ResetData(void)
|
||||
{
|
||||
if (hbmp) DeleteObject(hbmp);
|
||||
hbmp = NULL;
|
||||
SecureZeroMemory(&info, sizeof(BITMAPINFO));
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
BOOL MLImage::Draw(HDC hdcDest, int destX, int destY, int destWidth, int destHeight, int sourceX, int sourceY)
|
||||
{
|
||||
if (!hbmp) return FALSE;
|
||||
|
||||
int realheight = abs(info.bmiHeader.biHeight);
|
||||
int rsX = min(sourceX, info.bmiHeader.biWidth);
|
||||
int rsY = min(sourceY, info.bmiHeader.biWidth);
|
||||
int height = min(destHeight, realheight - rsY);
|
||||
|
||||
BOOL bResult = SetDIBitsToDevice( hdcDest, destX, destY,
|
||||
min(destWidth, info.bmiHeader.biWidth - rsX), height,
|
||||
rsX, realheight - height - rsY,
|
||||
0, height,
|
||||
data, &info, DIB_RGB_COLORS);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
BOOL MLImage::Draw(HDC hdcDest, int destX, int destY)
|
||||
{
|
||||
return (!hbmp) ? FALSE : SetDIBitsToDevice( hdcDest, destX, destY,
|
||||
info.bmiHeader.biWidth, abs(info.bmiHeader.biHeight),
|
||||
0, 0,
|
||||
0, abs(info.bmiHeader.biHeight),
|
||||
data, &info, DIB_RGB_COLORS);
|
||||
}
|
||||
|
||||
int MLImage::GetWidth(void) const
|
||||
{
|
||||
return (hbmp) ? info.bmiHeader.biWidth : 0;
|
||||
}
|
||||
|
||||
int MLImage::GetHeight(void) const
|
||||
{
|
||||
return (hbmp) ? abs(info.bmiHeader.biHeight) : 0;
|
||||
}
|
||||
|
||||
void* MLImage::GetData(void) const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
HBITMAP MLImage::ConvertTo32BppDIB(HBITMAP bmpHandle, int bmpWidth, int bmpHeight, LPBITMAPINFO bmpInfo, LPVOID *bmpData)
|
||||
{
|
||||
HBITMAP hbmpNew = NULL;
|
||||
|
||||
HDC hdc = GetWindowDC(NULL);
|
||||
HDC hdcTmp = CreateCompatibleDC(hdc);
|
||||
HBITMAP hbmpTmp = CreateCompatibleBitmap(hdc, bmpWidth, bmpHeight);
|
||||
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcTmp, hbmpTmp);
|
||||
|
||||
// render original bitmap to the temp dc
|
||||
HDC hdcBmp = CreateCompatibleDC(hdc);
|
||||
SelectObject(hdcBmp, bmpHandle);
|
||||
BitBlt(hdcTmp, 0, 0, bmpWidth, bmpHeight, hdcBmp, 0,0, SRCCOPY);
|
||||
SelectObject(hdcBmp, NULL);
|
||||
DeleteDC(hdcBmp);
|
||||
|
||||
// Create a 32 bit bitmap
|
||||
BITMAPINFO bih;
|
||||
// create DIB Section
|
||||
bih.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bih.bmiHeader.biWidth = bmpWidth;
|
||||
bih.bmiHeader.biHeight = 0 - bmpHeight;
|
||||
bih.bmiHeader.biPlanes = 1;
|
||||
bih.bmiHeader.biBitCount = 32;
|
||||
bih.bmiHeader.biCompression = BI_RGB;
|
||||
bih.bmiHeader.biSizeImage = 0;
|
||||
bih.bmiHeader.biXPelsPerMeter = 0;
|
||||
bih.bmiHeader.biYPelsPerMeter = 0;
|
||||
bih.bmiHeader.biClrUsed = 0;
|
||||
bih.bmiHeader.biClrImportant = 0;
|
||||
|
||||
// Create a DC which will be used to get DIB, then create DIBsection
|
||||
hbmpNew = CreateDIBSection(hdc, (const BITMAPINFO*) &bih, DIB_RGB_COLORS, bmpData, NULL, 0);
|
||||
|
||||
|
||||
DWORD* line = (DWORD*)(*bmpData);
|
||||
// Copy the bits into our 32 bit dib..
|
||||
for(int i=0; i<bmpHeight; i++)
|
||||
{
|
||||
for(int j=0; j<bmpWidth; j++)
|
||||
{
|
||||
line[(i*bmpWidth) + j] = FIXCOLORREF(GetPixel(hdcTmp, j, i));
|
||||
}
|
||||
}
|
||||
|
||||
SelectObject(hdcTmp, hbmpOld);
|
||||
ReleaseDC(NULL, hdc);
|
||||
DeleteDC(hdcTmp);
|
||||
|
||||
memcpy(bmpInfo, &bih, sizeof(BITMAPINFO));
|
||||
|
||||
return hbmpNew;
|
||||
}
|
||||
|
||||
MLImage* MLImage::Copy(MLImage* destination, const MLImage* original)
|
||||
{
|
||||
if (!destination) return NULL;
|
||||
|
||||
destination->ResetData();
|
||||
|
||||
destination->loader = original->loader;
|
||||
destination->loaderDelete = original->loaderDelete;
|
||||
destination->info = original->info;
|
||||
HDC hdc = GetWindowDC(NULL);
|
||||
destination->hbmp = CreateDIBSection(hdc, (const BITMAPINFO*) &destination->info, DIB_RGB_COLORS, &destination->data, NULL, 0);
|
||||
|
||||
CopyMemory(destination->data, original->data, 4*destination->GetHeight() * destination->GetWidth());
|
||||
ReleaseDC(NULL, hdc);
|
||||
return destination;
|
||||
}
|
||||
|
||||
MLImage* MLImage::Init(int width, int height)
|
||||
{
|
||||
ResetData();
|
||||
|
||||
loader = NULL;
|
||||
loaderDelete = TRUE;
|
||||
|
||||
// create DIB Section
|
||||
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
info.bmiHeader.biWidth = width;
|
||||
info.bmiHeader.biHeight = 0 - height;
|
||||
info.bmiHeader.biPlanes = 1;
|
||||
info.bmiHeader.biBitCount = 32;
|
||||
info.bmiHeader.biCompression = BI_RGB;
|
||||
info.bmiHeader.biSizeImage = 0;
|
||||
info.bmiHeader.biXPelsPerMeter = 0;
|
||||
info.bmiHeader.biYPelsPerMeter = 0;
|
||||
info.bmiHeader.biClrUsed = 0;
|
||||
info.bmiHeader.biClrImportant = 0;
|
||||
|
||||
HDC hdc = GetWindowDC(NULL);
|
||||
hbmp = CreateDIBSection(hdc, (const BITMAPINFO*) &info, DIB_RGB_COLORS, &data, NULL, 0);
|
||||
ReleaseDC(NULL, hdc);
|
||||
return this;
|
||||
}
|
||||
|
||||
MLImage* MLImage::Init(int width, int height, COLORREF color)
|
||||
{
|
||||
Init(width, height);
|
||||
|
||||
int rColor = FIXCOLORREF(color);
|
||||
DWORD *line = (DWORD*)(data);
|
||||
DWORD *end = line + GetHeight() * GetWidth();
|
||||
for(;line != end; line++) *line = rColor;
|
||||
return this;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef NULLSOFT_ML_IMAGE_HEADER
|
||||
#define NULLSOFT_ML_IMAGE_HEADER
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define RGBA(r,g,b,a) ((COLORREF)(((BYTE)(r)|((WORD)(g)<<8))|(((DWORD)(BYTE)(b))<<16)|(((DWORD)(BYTE)(a))<<24)))
|
||||
#define FIXCOLORREF(clr) RGBA(GetBValue(clr),GetGValue(clr), GetRValue(clr),((DWORD)(clr)) >> 24)
|
||||
|
||||
// loader function will be called every time MLImage need to
|
||||
// reload picture. Input parameter - handle to the calling object
|
||||
// Output - loaded bitmap
|
||||
typedef HBITMAP (*IMGLOADFUNC)(INT_PTR handle);
|
||||
|
||||
class MLImage
|
||||
{
|
||||
public:
|
||||
MLImage(void);
|
||||
MLImage(IMGLOADFUNC loader, BOOL deleteDone);
|
||||
MLImage(int width, int height);
|
||||
~MLImage(void);
|
||||
|
||||
public:
|
||||
// sets the loader function and returns handle to the class or NULL if error
|
||||
// loader - pointer to the loader function
|
||||
// deleteDone - if TRUE MLImage will delete HBITMAP object from loader every time it is done loading
|
||||
// forceLoad - forcing to load bitamp immedialty by calling Load()
|
||||
INT_PTR SetLoader(IMGLOADFUNC loader, BOOL deleteDone, BOOL forceLoad);
|
||||
BOOL Load(void); // load image
|
||||
|
||||
MLImage* Init(int width, int height); // init image (allocates memory)
|
||||
MLImage* Init(int width, int height, COLORREF color); // init image (allocates memory) and set
|
||||
|
||||
BOOL Draw(HDC hdcDest, int destX, int destY, int destWidth, int destHeight, int sourceX, int sourceY); // draw image
|
||||
BOOL Draw(HDC hdcDest, int destX, int destY); // draw image
|
||||
|
||||
public:
|
||||
int GetWidth(void) const;
|
||||
int GetHeight(void) const;
|
||||
void* GetData(void) const;
|
||||
|
||||
|
||||
private:
|
||||
void ResetData(void);
|
||||
|
||||
public:
|
||||
static MLImage* Copy(MLImage* destination, const MLImage* original);// copy all data from the original object (including image data) to the destination
|
||||
|
||||
private:
|
||||
static HBITMAP ConvertTo32BppDIB(HBITMAP bmpHandle, int bmpWidth, int bmpHeight, LPBITMAPINFO bmpInfo, LPVOID *bmpData);
|
||||
|
||||
private:
|
||||
IMGLOADFUNC loader; // pointer to the loader function
|
||||
BOOL loaderDelete; // TRUE - delete HBITMAP from loader after load
|
||||
|
||||
HBITMAP hbmp; // my bitmap
|
||||
BITMAPINFO info;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#endif // NULLSOFT_ML_IMAGE_HEADER
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include ".\imagefilters.h"
|
||||
|
||||
void MLImageFilter_GrayScale(MLImage *image)
|
||||
{
|
||||
DWORD *line = (DWORD*)(image->GetData());
|
||||
DWORD *end = line + image->GetHeight() * image->GetWidth();
|
||||
for(;line != end; line++)
|
||||
{
|
||||
BYTE y = (BYTE)(0.3f * GetBValue(*line) + 0.59f *GetGValue(*line) + 0.11f *GetRValue(*line));
|
||||
*line = RGB(y,y,y);
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilter_Invert(MLImage *image)
|
||||
{
|
||||
DWORD *line = (DWORD*)(image->GetData());
|
||||
DWORD *end = line + image->GetHeight() * image->GetWidth();
|
||||
for(;line != end; line++) *line = ((~*line) & 0x00FFFFFF) | (*line & 0xFF000000);
|
||||
}
|
||||
|
||||
void MLImageFilter_SetToColor(MLImage *image, COLORREF color)
|
||||
{
|
||||
COLORREF rColor = FIXCOLORREF(color);
|
||||
DWORD *line = (DWORD*)(image->GetData());
|
||||
DWORD *end = line + image->GetHeight() * image->GetWidth();
|
||||
for(;line != end; line++) *line = rColor;
|
||||
}
|
||||
|
||||
void MLImageFilter_Fader1(MLImage *dest, const MLImage* source, COLORREF color)
|
||||
{
|
||||
int len = dest->GetHeight() * dest->GetWidth();
|
||||
BYTE r = GetRValue(color), g = GetGValue(color), b = GetBValue(color);
|
||||
|
||||
DWORD *dataS = (DWORD*)(source->GetData());
|
||||
DWORD *dataD = (DWORD*)(dest->GetData());
|
||||
DWORD *end = dataD + len;
|
||||
for(;dataD != end; dataD++, dataS++)
|
||||
{
|
||||
*dataD = RGB( max(b, GetRValue(*dataS)), max(g, GetGValue(*dataS)), max(r, GetBValue(*dataS))) ;
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilter_Fader2(MLImage *dest, const MLImage* source, COLORREF color)
|
||||
{
|
||||
int len = dest->GetHeight() * dest->GetWidth();
|
||||
BYTE r = GetRValue(color), g = GetGValue(color), b = GetBValue(color);
|
||||
|
||||
DWORD *dataS = (DWORD*)(source->GetData());
|
||||
DWORD *dataD = (DWORD*)(dest->GetData());
|
||||
DWORD *end = dataD + len;
|
||||
for(;dataD != end; dataD++, dataS++)
|
||||
{
|
||||
*dataD = RGB( min(b, GetRValue(*dataS)), min(g, GetGValue(*dataS)), min(r, GetBValue(*dataS))) ;
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilter_Fader3(MLImage *dest, const MLImage* source, int koeff)
|
||||
{
|
||||
int len = dest->GetHeight() * dest->GetWidth();
|
||||
|
||||
DWORD *dataS = (DWORD*)(source->GetData());
|
||||
DWORD *dataD = (DWORD*)(dest->GetData());
|
||||
DWORD *end = dataD + len;
|
||||
for(;dataD != end; dataD++, dataS++)
|
||||
{
|
||||
*dataD = RGB(min(255,GetRValue(*dataS) + koeff), min(255,GetGValue(*dataS) + koeff), min(255, GetBValue(*dataS) + koeff));
|
||||
}
|
||||
}
|
||||
|
||||
void MLImageFilter_Blend1(MLImage *dest, MLImage *src1, int destX, int destY, int width, int height, const MLImage* src2, int srcX, int srcY, COLORREF color)
|
||||
{
|
||||
int widthS1 = src1->GetWidth();
|
||||
int widthS2 = src2->GetWidth();
|
||||
|
||||
DWORD *dataD = (DWORD*)(dest->GetData()) + destY * widthS1 + destX;
|
||||
DWORD *dataS1 = (DWORD*)(src1->GetData()) + destY * widthS1 + destX;
|
||||
DWORD *dataS2 = (DWORD*)(src2->GetData()) + srcY * widthS2 + srcX;
|
||||
|
||||
DWORD *curS1;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
DWORD *curD = dataD + y * widthS1;
|
||||
curS1 = dataS1 + y * widthS1;
|
||||
DWORD *curS2 = dataS2 + y * widthS2;
|
||||
for (DWORD *end = curS1 + width; end != curS1; curD++, curS1++, curS2++)
|
||||
{
|
||||
*curD = (*curS1 == color) ? *curS2 : *curS1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef NULLSOFT_ML_IMAGE_FILTER_HEADER
|
||||
#define NULLSOFT_ML_IMAGE_FILTER_HEADER
|
||||
|
||||
#include <windows.h>
|
||||
#include ".\image.h"
|
||||
#include ".\filterwater.h"
|
||||
|
||||
void MLImageFilter_GrayScale(MLImage *image);
|
||||
void MLImageFilter_Invert(MLImage *image);
|
||||
void MLImageFilter_SetToColor(MLImage *image, COLORREF color);
|
||||
void MLImageFilter_Fader1(MLImage *dest, const MLImage* source, COLORREF color);
|
||||
void MLImageFilter_Fader2(MLImage *dest, const MLImage* source, COLORREF color);
|
||||
void MLImageFilter_Fader3(MLImage *dest, const MLImage* source, int koeff);
|
||||
void MLImageFilter_Blend1(MLImage *dest, MLImage *src1, int destX, int destY, int width, int height, const MLImage* src2, int srcX, int srcY, COLORREF color);
|
||||
|
||||
|
||||
#endif //NULLSOFT_ML_IMAGE_FILTER_HEADER
|
||||
@@ -0,0 +1,556 @@
|
||||
#include "../id3v2/id3_tag.h"
|
||||
#include "id3.h"
|
||||
#include "config.h"
|
||||
#include "../nu/ns_wc.h"
|
||||
#include <strsafe.h>
|
||||
#define _isdigit(x) (( x ) >= '0' && ( x ) <= '9')
|
||||
|
||||
/* id3 helper functions */
|
||||
|
||||
void SetFrameEncoding(ID3_Frame *frame, int encoding)
|
||||
{
|
||||
switch (encoding)
|
||||
{
|
||||
case ENCODING_AUTO:
|
||||
if (config_write_mode == WRITE_UTF16)
|
||||
frame->Field(ID3FN_TEXTENC).Set(ID3TE_UNICODE);
|
||||
else
|
||||
frame->Field(ID3FN_TEXTENC).Set(ID3TE_ASCII);
|
||||
break;
|
||||
case ENCODING_FORCE_ASCII:
|
||||
frame->Field(ID3FN_TEXTENC).Set(ID3TE_ASCII);
|
||||
break;
|
||||
case ENCODING_FORCE_UNICODE:
|
||||
frame->Field(ID3FN_TEXTENC).Set(ID3TE_UNICODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *ID3_GetString(ID3_Frame *frame, ID3_FieldID fldName, size_t nIndex)
|
||||
{
|
||||
char *text = NULL;
|
||||
if (NULL != frame)
|
||||
{
|
||||
size_t nText = frame->Field(fldName).Size();
|
||||
text = (char *)calloc(nText + 1, sizeof(char));
|
||||
frame->Field(fldName).GetLocal(text, nText + 1, nIndex);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetUnicodeString(ID3_Frame *frame, ID3_FieldID fldName, size_t nIndex)
|
||||
{
|
||||
wchar_t *text = NULL;
|
||||
if (NULL != frame)
|
||||
{
|
||||
size_t nText = frame->Field(fldName).Size();
|
||||
text = (wchar_t *)calloc(sizeof(wchar_t) * (nText + 1), sizeof(wchar_t));
|
||||
frame->Field(fldName).GetUnicode(text, nText + 1, nIndex);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
wchar_t *ID3_FillUnicodeString(ID3_Frame *frame, ID3_FieldID fldName, wchar_t *dest, size_t destlen, size_t nIndex)
|
||||
{
|
||||
memset(dest, 0, destlen * sizeof(wchar_t));
|
||||
if (NULL != frame)
|
||||
{
|
||||
frame->Field(fldName).GetUnicode(dest, destlen, nIndex);
|
||||
return dest;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetTitle(ID3_Tag *tag)
|
||||
{
|
||||
wchar_t*sTitle = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sTitle;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(ID3FID_TITLE);
|
||||
if (frame != NULL)
|
||||
{
|
||||
sTitle = ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
}
|
||||
return sTitle;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetArtist(ID3_Tag *tag)
|
||||
{
|
||||
if (!tag) return 0;
|
||||
wchar_t *sArtist = NULL;
|
||||
ID3_Frame *frame = NULL;
|
||||
if ((frame = tag->Find(ID3FID_LEADARTIST)) || (frame = tag->Find(ID3FID_BAND)))
|
||||
{
|
||||
sArtist = ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
}
|
||||
return sArtist;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetAlbum(ID3_Tag *tag)
|
||||
{
|
||||
wchar_t *sAlbum = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sAlbum;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(ID3FID_ALBUM);
|
||||
if (frame != NULL)
|
||||
{
|
||||
sAlbum = ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
}
|
||||
return sAlbum;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetYear(ID3_Tag *tag)
|
||||
{
|
||||
wchar_t *sYear = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sYear;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(ID3FID_RECORDINGTIME);
|
||||
if (frame != NULL)
|
||||
sYear = ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
|
||||
if (!sYear || !*sYear)
|
||||
{
|
||||
frame = tag->Find(ID3FID_YEAR);
|
||||
if (frame != NULL)
|
||||
sYear = ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
}
|
||||
|
||||
return sYear;
|
||||
}
|
||||
|
||||
void ID3_AddSetComment(ID3_Tag *tag, const wchar_t *comment)
|
||||
{
|
||||
ID3_Frame *frame = tag->Find(ID3FID_COMMENT, ID3FN_DESCRIPTION, L"");
|
||||
if (frame)
|
||||
{
|
||||
if (!comment || !comment[0])
|
||||
tag->RemoveFrame(frame);
|
||||
else
|
||||
{
|
||||
SetFrameEncoding(frame);
|
||||
frame->Field(ID3FN_TEXT).SetUnicode(comment);
|
||||
unsigned char null3[3] = {0, 0, 0};
|
||||
frame->Field(ID3FN_LANGUAGE).Get(null3, 3);
|
||||
if (!null3[0]) frame->Field(ID3FN_LANGUAGE).SetLatin("eng");
|
||||
}
|
||||
}
|
||||
else if (comment && comment[0])
|
||||
{
|
||||
frame = new ID3_Frame(ID3FID_COMMENT);
|
||||
SetFrameEncoding(frame);
|
||||
frame->Field(ID3FN_LANGUAGE).SetLatin("eng");
|
||||
//frame->Field(ID3FN_LANGUAGE).Set(null3, 3);
|
||||
frame->Field(ID3FN_DESCRIPTION).SetUnicode(L"");
|
||||
frame->Field(ID3FN_TEXT).SetUnicode(comment);
|
||||
tag->AddFrame(frame, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void ID3_AddSetRating(ID3_Tag *tag, const wchar_t *rating)
|
||||
{
|
||||
luint rating_integer = 0;
|
||||
if (rating)
|
||||
rating_integer = _wtoi(rating);
|
||||
|
||||
bool custom_frame = false, own_frame = false;
|
||||
ID3_Frame* frame = NULL;
|
||||
if (config_rating_email[0])
|
||||
{
|
||||
frame = tag->Find(ID3FID_POPULARIMETER, ID3FN_EMAIL, config_rating_email);
|
||||
if (!frame) custom_frame = true;
|
||||
}
|
||||
if (!frame)
|
||||
{
|
||||
frame = tag->Find(ID3FID_POPULARIMETER, ID3FN_EMAIL, "rating@winamp.com\0");
|
||||
if (frame) own_frame = true;
|
||||
}
|
||||
if (!frame)
|
||||
{
|
||||
frame = tag->Find(ID3FID_POPULARIMETER);
|
||||
if (frame) own_frame = true;
|
||||
}
|
||||
// try to use a custom field if our own was present and the custom wasn't
|
||||
if (custom_frame && own_frame)
|
||||
{
|
||||
frame->Clear();
|
||||
frame = NULL;
|
||||
}
|
||||
if (!frame)
|
||||
{
|
||||
frame = new ID3_Frame(ID3FID_POPULARIMETER);
|
||||
if (!config_rating_email[0])
|
||||
frame->Field(ID3FN_EMAIL).Set((uchar *)"rating@winamp.com\0", 18);
|
||||
else
|
||||
{
|
||||
frame->Field(ID3FN_EMAIL).Set((uchar *)config_rating_email, strlen(config_rating_email)+1);
|
||||
}
|
||||
tag->AddFrame(frame, TRUE);
|
||||
}
|
||||
if (frame)
|
||||
{
|
||||
switch(rating_integer)
|
||||
{
|
||||
case 2:
|
||||
rating_integer=64;
|
||||
break;
|
||||
case 3:
|
||||
rating_integer=128;
|
||||
break;
|
||||
case 4:
|
||||
rating_integer=196;
|
||||
break;
|
||||
case 5:
|
||||
rating_integer = 255;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rating_integer)
|
||||
tag->RemoveFrame(frame);
|
||||
else
|
||||
frame->Field(ID3FN_RATING).Set(rating_integer);
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetComment(ID3_Tag *tag, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
wchar_t *comment = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return comment;
|
||||
}
|
||||
ID3_Frame* frame = tag->Find(ID3FID_COMMENT, ID3FN_DESCRIPTION, L"");
|
||||
if (frame)
|
||||
{
|
||||
comment = ID3_FillUnicodeString(frame, ID3FN_TEXT, dest, destlen);
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetRating(ID3_Tag *tag, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
if (NULL == tag)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
ID3_Frame* frame = NULL;
|
||||
if (config_rating_email[0])
|
||||
frame = tag->Find(ID3FID_POPULARIMETER, ID3FN_EMAIL, config_rating_email);
|
||||
if (!frame)
|
||||
frame = tag->Find(ID3FID_POPULARIMETER, ID3FN_EMAIL, "rating@winamp.com\0");
|
||||
if (!frame)
|
||||
frame = tag->Find(ID3FID_POPULARIMETER);
|
||||
if (frame)
|
||||
{
|
||||
int rating = (int)frame->Field(ID3FN_RATING).Get();
|
||||
|
||||
if (rating >= 224 && rating <= 255)
|
||||
rating = 5;
|
||||
else if (rating >= 160 && rating <= 223)
|
||||
rating = 4;
|
||||
else if (rating >= 96 && rating <= 159)
|
||||
rating = 3;
|
||||
else if (rating >= 32 && rating <= 95)
|
||||
rating = 2;
|
||||
else if (rating >= 1 && rating <= 31)
|
||||
rating = 1;
|
||||
else
|
||||
rating = 0;
|
||||
|
||||
StringCchPrintfW(dest, destlen, L"%u", rating);
|
||||
return dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetComment(ID3_Tag *tag, const wchar_t *desc, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
wchar_t *comment = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return comment;
|
||||
}
|
||||
ID3_Frame* frame = tag->Find(ID3FID_COMMENT, ID3FN_DESCRIPTION, desc);
|
||||
if (frame)
|
||||
{
|
||||
comment = ID3_FillUnicodeString(frame, ID3FN_TEXT, dest, destlen);
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetMusicbrainzRecordingID(ID3_Tag *tag, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
if (NULL == tag)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ID3_Frame* frame = tag->Find(ID3FID_UNIQUEFILEID, ID3FN_OWNER, L"http://musicbrainz.org");
|
||||
if (frame)
|
||||
{
|
||||
uchar data[64] = {0};
|
||||
luint dataSize = frame->Field(ID3FN_DATA).Size();
|
||||
frame->Field(ID3FN_DATA).Get(data, 64);
|
||||
int converted = MultiByteToWideCharSZ(CP_ACP, 0, (const char *)data, (int)dataSize, dest, (int)destlen);
|
||||
dest[converted]=0;
|
||||
return dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetGracenoteTagID(ID3_Tag *tag)
|
||||
{
|
||||
if (NULL == tag)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ID3_Frame* frame = tag->Find(ID3FID_UNIQUEFILEID, ID3FN_OWNER, L"http://www.cddb.com/id3/taginfo1.html");
|
||||
if (frame)
|
||||
{
|
||||
uchar data[64] = {0};
|
||||
luint dataSize = frame->Field(ID3FN_DATA).Size();
|
||||
frame->Field(ID3FN_DATA).Get(data, 64);
|
||||
int converted = MultiByteToWideChar(CP_ACP, 0, (const char *)data, (int)dataSize, 0, 0);
|
||||
wchar_t *dest = (wchar_t *)calloc((converted+1), sizeof(wchar_t));
|
||||
converted = MultiByteToWideChar(CP_ACP, 0, (const char *)data, (int)dataSize, dest, converted);
|
||||
dest[converted]=0;
|
||||
return dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetGracenoteTagID(ID3_Tag *tag, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
if (NULL == tag)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ID3_Frame* frame = tag->Find(ID3FID_UNIQUEFILEID, ID3FN_OWNER, L"http://www.cddb.com/id3/taginfo1.html");
|
||||
if (frame)
|
||||
{
|
||||
uchar data[64] = {0};
|
||||
luint dataSize = frame->Field(ID3FN_DATA).Size();
|
||||
frame->Field(ID3FN_DATA).Get(data, 64);
|
||||
int converted = MultiByteToWideCharSZ(CP_ACP, 0, (const char *)data, (int)dataSize, dest, (int)destlen);
|
||||
dest[converted]=0;
|
||||
return dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ID3_AddSetGracenoteTagID(ID3_Tag *tag, const wchar_t *tagID)
|
||||
{
|
||||
ID3_Frame *frame = tag->Find(ID3FID_UNIQUEFILEID, ID3FN_OWNER, L"http://www.cddb.com/id3/taginfo1.html");
|
||||
if (frame)
|
||||
{
|
||||
if (!tagID || !tagID[0])
|
||||
tag->RemoveFrame(frame);
|
||||
else
|
||||
{
|
||||
size_t origLen = wcslen(tagID); // so we can not write the null terminator
|
||||
uchar data[64] = {0};
|
||||
luint dataSize = WideCharToMultiByte(CP_ACP, 0, tagID, (int)origLen, (char *)data, 64, 0, 0);
|
||||
frame->Field(ID3FN_DATA).Set(data, dataSize);
|
||||
}
|
||||
}
|
||||
else if (tagID && tagID[0])
|
||||
{
|
||||
frame = new ID3_Frame(ID3FID_UNIQUEFILEID);
|
||||
SetFrameEncoding(frame, ENCODING_FORCE_ASCII);
|
||||
frame->Field(ID3FN_OWNER).SetLatin("http://www.cddb.com/id3/taginfo1.html");
|
||||
size_t origLen = wcslen(tagID); // so we can not write the null terminator
|
||||
uchar data[64] = {0};
|
||||
luint dataSize = WideCharToMultiByte(CP_ACP, 0, tagID, (int)origLen, (char *)data, 64, 0, 0);
|
||||
frame->Field(ID3FN_DATA).Set(data, dataSize);
|
||||
tag->AddFrame(frame, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // benski> CUT
|
||||
char *ID3_GetTUID(ID3_Tag *tag)
|
||||
{
|
||||
char *tuid = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return tuid;
|
||||
}
|
||||
ID3_Frame* frame = NULL;
|
||||
frame = tag->Find(ID3FID_UNIQUEFILEID);
|
||||
if (frame)
|
||||
{
|
||||
char *tmp = ID3_GetString(frame, ID3FN_DATA);
|
||||
if (tmp)
|
||||
{
|
||||
// verify first four characters are '3CD3'
|
||||
if (!strncmp(tmp, "3CD3", 4))
|
||||
{
|
||||
char m, n;
|
||||
char *p = tmp + 4;
|
||||
n = *p++;
|
||||
m = 'P' - n;
|
||||
p += m;
|
||||
|
||||
n = *p++;
|
||||
m = 'Z' - n; // length of TUID;
|
||||
tuid = _strdup(p);
|
||||
tuid[m] = 0; // null terminate
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
return tuid;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *ID3_GetGenre(ID3_Tag *tag)
|
||||
{
|
||||
char *sGenre = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sGenre;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(ID3FID_CONTENTTYPE);
|
||||
if (frame != NULL)
|
||||
{
|
||||
sGenre = ID3_GetString(frame, ID3FN_TEXT);
|
||||
}
|
||||
return sGenre;
|
||||
}
|
||||
|
||||
void ID3_AddUserText(ID3_Tag *tag, wchar_t *desc, const wchar_t *value, int encoding)
|
||||
{
|
||||
ID3_Frame *frame = tag->Find(ID3FID_USERTEXT, ID3FN_DESCRIPTION, desc);
|
||||
if (frame)
|
||||
{
|
||||
if (!value || !value[0])
|
||||
tag->RemoveFrame(frame);
|
||||
else
|
||||
{
|
||||
SetFrameEncoding(frame, encoding);
|
||||
frame->Field(ID3FN_TEXT).SetUnicode(value);
|
||||
}
|
||||
}
|
||||
else if (value && value[0])
|
||||
{
|
||||
frame = new ID3_Frame(ID3FID_USERTEXT);
|
||||
SetFrameEncoding(frame, encoding);
|
||||
frame->Field(ID3FN_DESCRIPTION).SetUnicode(desc);
|
||||
frame->Field(ID3FN_TEXT).SetUnicode(value);
|
||||
tag->AddFrame(frame, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetUserText(ID3_Tag *tag, wchar_t *desc)
|
||||
{
|
||||
if (tag == NULL)
|
||||
return NULL;
|
||||
|
||||
ID3_Frame *frame = tag->Find(ID3FID_USERTEXT, ID3FN_DESCRIPTION, desc);
|
||||
if (frame)
|
||||
return ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetUserText(ID3_Tag *tag, wchar_t *desc, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
if (tag == NULL)
|
||||
return NULL;
|
||||
|
||||
ID3_Frame *frame = tag->Find(ID3FID_USERTEXT, ID3FN_DESCRIPTION, desc);
|
||||
if (frame)
|
||||
return ID3_FillUnicodeString(frame, ID3FN_TEXT, dest, destlen);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetTagText(ID3_Tag *tag, ID3_FrameID f)
|
||||
{
|
||||
wchar_t *sComposer = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sComposer;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(f);
|
||||
if (frame != NULL)
|
||||
{
|
||||
sComposer = ID3_GetUnicodeString(frame, ID3FN_TEXT);
|
||||
}
|
||||
return sComposer;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetTagText(ID3_Tag *tag, ID3_FrameID f, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
wchar_t *sComposer = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sComposer;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(f);
|
||||
if (frame != NULL)
|
||||
{
|
||||
sComposer = ID3_FillUnicodeString(frame, ID3FN_TEXT, dest, destlen);
|
||||
}
|
||||
return sComposer;
|
||||
}
|
||||
|
||||
wchar_t *ID3_GetTagUrl(ID3_Tag *tag, ID3_FrameID f, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
wchar_t *sComposer = NULL;
|
||||
if (NULL == tag)
|
||||
{
|
||||
return sComposer;
|
||||
}
|
||||
ID3_Frame *frame = tag->Find(f);
|
||||
if (frame != NULL)
|
||||
{
|
||||
sComposer = ID3_FillUnicodeString(frame, ID3FN_URL, dest, destlen);
|
||||
}
|
||||
return sComposer;
|
||||
}
|
||||
|
||||
#if 0
|
||||
char *ID3_GetGenreDisplayable(ID3_Tag *tag)
|
||||
{
|
||||
char *sGenre = ID3_GetGenre(tag);
|
||||
if (!sGenre) return NULL;
|
||||
|
||||
while (sGenre && *sGenre == ' ') sGenre++;
|
||||
|
||||
if (sGenre[0] == '(' || _isdigit(sGenre[0]))
|
||||
{
|
||||
int isparam = !_isdigit(sGenre[0]);
|
||||
char *pCur = &sGenre[isparam];
|
||||
int cnt = 0;
|
||||
while (_isdigit(*pCur))
|
||||
{
|
||||
cnt++;
|
||||
pCur++;
|
||||
}
|
||||
while (pCur && *pCur == ' ') pCur++;
|
||||
|
||||
if (cnt > 0 && (isparam && *pCur == ')') || (!isparam && !*pCur))
|
||||
{
|
||||
// if the genre number is greater than 255, its invalid.
|
||||
size_t ulGenre = atoi(&sGenre[isparam]);
|
||||
if (ulGenre >= 0 && ulGenre < numberOfGenres)
|
||||
{
|
||||
char *tmp = (char*)malloc(strlen(genres[ulGenre]) + 1);
|
||||
if (tmp)
|
||||
{
|
||||
memcpy(tmp, genres[ulGenre], strlen(genres[ulGenre]) + 1);
|
||||
free(sGenre);
|
||||
sGenre = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sGenre;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef NULLSOFT_IN_MP3_IN_H
|
||||
#define NULLSOFT_IN_MP3_IN_H
|
||||
|
||||
extern char *genres[];
|
||||
extern size_t numberOfGenres;
|
||||
enum
|
||||
{
|
||||
ENCODING_AUTO=0,
|
||||
ENCODING_FORCE_ASCII = 1,
|
||||
ENCODING_FORCE_UNICODE = 2,
|
||||
};
|
||||
|
||||
char *ID3_GetString(ID3_Frame *frame, ID3_FieldID fldName, size_t nIndex=1);
|
||||
wchar_t *ID3_GetUnicodeString(ID3_Frame *frame, ID3_FieldID fldName, size_t nIndex=1);
|
||||
wchar_t *ID3_FillUnicodeString(ID3_Frame *frame, ID3_FieldID fldName, wchar_t *dest, size_t destlen, size_t nIndex=1);
|
||||
|
||||
wchar_t *ID3_GetTitle(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetArtist(ID3_Tag *tag);
|
||||
//char *ID3_GetAlbumLocal(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetAlbum(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetYear(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetComment(ID3_Tag *tag, wchar_t *dest, size_t destlen);
|
||||
wchar_t *ID3_GetComment(ID3_Tag *tag, const wchar_t *desc, wchar_t *dest, size_t destlen);
|
||||
char *ID3_GetTUID(ID3_Tag *tag);
|
||||
char *ID3_GetGenre(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetTagText(ID3_Tag *tag, ID3_FrameID f);
|
||||
wchar_t *ID3_GetTagText(ID3_Tag *tag, ID3_FrameID f, wchar_t *dest, size_t destlen);
|
||||
wchar_t *ID3_GetTagUrl(ID3_Tag *tag, ID3_FrameID f, wchar_t *dest, size_t destlen);
|
||||
char *ID3_GetGenreDisplayable(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetUserText(ID3_Tag *tag, wchar_t *desc);
|
||||
wchar_t *ID3_GetUserText(ID3_Tag *tag, wchar_t *desc, wchar_t *dest, size_t destlen);
|
||||
void ID3_AddUserText(ID3_Tag *tag, wchar_t *desc, const wchar_t *value, int encoding=ENCODING_AUTO);
|
||||
void ID3_AddSetComment(ID3_Tag *tag, const wchar_t *comment);
|
||||
void ID3_AddSetRating(ID3_Tag *tag, const wchar_t *rating);
|
||||
wchar_t *ID3_GetRating(ID3_Tag *tag, wchar_t *dest, size_t destlen);
|
||||
wchar_t *ID3_GetMusicbrainzRecordingID(ID3_Tag *tag, wchar_t *dest, size_t destlen);
|
||||
wchar_t *ID3_GetGracenoteTagID(ID3_Tag *tag);
|
||||
wchar_t *ID3_GetGracenoteTagID(ID3_Tag *tag, wchar_t *dest, size_t destlen);
|
||||
void ID3_AddSetGracenoteTagID(ID3_Tag *tag, const wchar_t *tagID);
|
||||
|
||||
void SetFrameEncoding(ID3_Frame *frame, int encoding = ENCODING_AUTO);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <bfc/dispatch.h>
|
||||
|
||||
class ifc_mpeg_stream_reader : public Dispatchable
|
||||
{
|
||||
protected:
|
||||
ifc_mpeg_stream_reader() {}
|
||||
~ifc_mpeg_stream_reader() {}
|
||||
|
||||
public:
|
||||
int MPEGStream_Peek( void *buffer, size_t to_read, size_t *bytes_read );
|
||||
int MPEGStream_Read( void *buffer, size_t to_read, size_t *bytes_read );
|
||||
int MPEGStream_EOF();
|
||||
float MPEGStream_Gain();
|
||||
|
||||
DISPATCH_CODES
|
||||
{
|
||||
MPEGSTREAM_PEEK = 0,
|
||||
MPEGSTREAM_READ = 1,
|
||||
MPEGSTREAM_EOF = 2,
|
||||
MPEGSTREAM_GAIN = 3,
|
||||
};
|
||||
};
|
||||
|
||||
inline int ifc_mpeg_stream_reader::MPEGStream_Peek( void *buffer, size_t to_read, size_t *bytes_read )
|
||||
{
|
||||
return _call( MPEGSTREAM_PEEK, (int)1, buffer, to_read, bytes_read );
|
||||
}
|
||||
|
||||
inline int ifc_mpeg_stream_reader::MPEGStream_Read( void *buffer, size_t to_read, size_t *bytes_read )
|
||||
{
|
||||
return _call( MPEGSTREAM_READ, (int)1, buffer, to_read, bytes_read );
|
||||
}
|
||||
|
||||
inline int ifc_mpeg_stream_reader::MPEGStream_EOF()
|
||||
{
|
||||
return _call( MPEGSTREAM_EOF, (int)true );
|
||||
}
|
||||
|
||||
inline float ifc_mpeg_stream_reader::MPEGStream_Gain()
|
||||
{
|
||||
return _call( MPEGSTREAM_GAIN, (float)1.0f );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
// 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
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO
|
||||
BEGIN
|
||||
IDD_PREFS, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 6
|
||||
RIGHTMARGIN, 220
|
||||
TOPMARGIN, 6
|
||||
BOTTOMMARGIN, 121
|
||||
END
|
||||
|
||||
IDD_OUTPUT, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 6
|
||||
RIGHTMARGIN, 220
|
||||
TOPMARGIN, 6
|
||||
BOTTOMMARGIN, 156
|
||||
END
|
||||
|
||||
IDD_HTTP, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 6
|
||||
RIGHTMARGIN, 220
|
||||
TOPMARGIN, 6
|
||||
BOTTOMMARGIN, 148
|
||||
END
|
||||
|
||||
IDD_TAGOPTS, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 6
|
||||
RIGHTMARGIN, 220
|
||||
TOPMARGIN, 6
|
||||
BOTTOMMARGIN, 163
|
||||
END
|
||||
|
||||
IDD_HTTPAUTH, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 153
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 71
|
||||
END
|
||||
|
||||
IDD_ADVANCED_TAGGING, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 6
|
||||
RIGHTMARGIN, 220
|
||||
TOPMARGIN, 6
|
||||
BOTTOMMARGIN, 116
|
||||
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
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_PREFS DIALOGEX 0, 0, 226, 127
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION
|
||||
CAPTION "General"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
GROUPBOX "File Association",IDC_STATIC,6,6,214,61
|
||||
LTEXT "Extension list (semicolon delimited):",IDC_STATIC,12,18,140,8
|
||||
EDITTEXT IDC_EDIT1,12,31,201,12,ES_AUTOHSCROLL
|
||||
PUSHBUTTON "Set to default",IDC_BUTTON1,12,49,50,13
|
||||
LTEXT "Default: MP3;MP2;MP1;AAC;VLB",IDC_STATIC,68,51,144,8
|
||||
GROUPBOX "Full File Buffering",IDC_STATIC,6,71,214,32
|
||||
LTEXT "Buffer the entire file from disk if the file is smaller than:",IDC_STATIC,13,81,115,16
|
||||
EDITTEXT IDC_BUFMAX,141,83,24,12,ES_AUTOHSCROLL | ES_NUMBER
|
||||
LTEXT "kilobytes",IDC_STATIC,169,85,28,8
|
||||
END
|
||||
|
||||
IDD_OUTPUT DIALOGEX 0, 0, 226, 163
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
|
||||
CAPTION "Decoder"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
GROUPBOX "Quality",IDC_STATIC,6,2,213,50
|
||||
CONTROL "&Full",IDC_FULLRATE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,12,12,30,10
|
||||
CONTROL "&Half",IDC_HALFRATE,"Button",BS_AUTORADIOBUTTON,12,24,30,10
|
||||
CONTROL "&Quarter",IDC_QRATE,"Button",BS_AUTORADIOBUTTON,12,36,40,10
|
||||
LTEXT "Selecting 'Half' or 'Quarter' quality will downsample the output, resulting in less processor usage.",IDC_STATIC,80,12,125,34
|
||||
GROUPBOX "Misc Options",IDC_STATIC,6,55,213,40
|
||||
CONTROL "CRC checking",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,67,105,10
|
||||
CONTROL "Show average bitrate on VBR files",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,80,180,10
|
||||
GROUPBOX "Equalizer",IDC_STATIC,6,98,213,61
|
||||
CONTROL "Logarithmic EQ (default)",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,12,110,128,10
|
||||
CONTROL "Linear EQ",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,12,120,72,10
|
||||
CONTROL "Fast Layer 3 EQ",IDC_FASTL3EQ,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,132,98,10
|
||||
CONTROL "Fast Layer 1/2 EQ",IDC_FASTL12EQ,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,144,106,10
|
||||
END
|
||||
|
||||
IDD_HTTP DIALOGEX 0, 0, 226, 154
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
|
||||
CAPTION "Streaming"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
GROUPBOX "Streaming Data Buffer",IDC_STATIC,6,6,86,64
|
||||
EDITTEXT IDC_BUFFERS_NUMBUFS,12,18,27,12
|
||||
LTEXT "KB",IDC_STATIC,42,20,10,8
|
||||
LTEXT "Increase this value to give better skip protection on slower connections.",IDC_STATIC,12,33,73,33
|
||||
GROUPBOX "Streaming Prebuffer",IDC_STATIC,96,6,124,100
|
||||
CONTROL "Slider1",IDC_PREBUFSLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,102,18,111,10
|
||||
CTEXT "0% 50% 100%",IDC_STATIC,103,31,110,8
|
||||
CTEXT "(how much to prebuffer at the start of a stream)",IDC_STATIC,105,42,105,16
|
||||
CONTROL "Slider1",IDC_PREBUFSLIDER2,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,102,61,111,10
|
||||
CTEXT "0% 50% 100%",IDC_STATIC,103,74,110,8
|
||||
CTEXT "(how much to prebuffer after a buffer underrun)",IDC_STATIC,105,85,105,16
|
||||
GROUPBOX "Streaming Extensions",IDC_STATIC,6,72,86,76
|
||||
CONTROL "Enable SHOUTcast title support",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,12,83,74,18
|
||||
CONTROL "Include stream name in title",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,12,105,74,18
|
||||
CONTROL "Enable SHOUTcast v2 artwork support",IDC_SC_ARTWORK,
|
||||
"Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,12,126,74,18
|
||||
GROUPBOX "Saving",IDC_STATIC,96,110,124,38
|
||||
CONTROL "Save files to:",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,120,57,10
|
||||
PUSHBUTTON "",IDC_BUTTON2,102,132,112,11
|
||||
END
|
||||
|
||||
IDD_TAGOPTS DIALOGEX 0, 0, 226, 168
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION
|
||||
CAPTION "ID3 Tags"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
GROUPBOX "ID3 Tag Reading",IDC_STATIC,6,6,214,122
|
||||
CONTROL "Read ID3v1 tags",IDC_READ_ID3V1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,19,70,10
|
||||
CONTROL "Read ID3v2 tags",IDC_READ_ID3V2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,31,70,10
|
||||
LTEXT "Read ASCII tags as:",IDC_STATIC,98,16,65,8
|
||||
COMBOBOX IDC_COMBO1,109,28,80,40,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
GROUPBOX "ID3 Tag Writing",IDC_STATIC,6,47,214,81
|
||||
CONTROL "Modify ID3v1 tags",IDC_WRITE_ID3V1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,60,73,10
|
||||
CONTROL "Modify ID3v2 tags",IDC_WRITE_ID3V2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,72,73,10
|
||||
LTEXT "Write tags as:",IDC_STATIC,98,56,45,8
|
||||
COMBOBOX IDC_COMBO2,109,67,80,40,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
GROUPBOX "ID3 Tag Creation",IDC_STATIC,6,86,214,42
|
||||
CONTROL "Create new ID3v1 tags when adding metadata",IDC_CREATE_ID3V1,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,100,163,10
|
||||
LTEXT "",IDC_STATIC,17,107,8,8
|
||||
CONTROL "Create new ID3v2 tags when adding metadata",IDC_CREATE_ID3V2,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,112,163,10
|
||||
GROUPBOX "ID3v2 Tag Rating Field Email Address",IDC_STATIC,6,132,214,31
|
||||
EDITTEXT IDC_RATING_EMAIL,13,144,162,12,ES_AUTOHSCROLL
|
||||
PUSHBUTTON "Reset",IDC_RATING_EMAIL_RESET,179,143,36,14
|
||||
END
|
||||
|
||||
IDD_HTTPAUTH DIALOGEX 0, 0, 160, 76
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "HTTP Login Required"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
LTEXT "Realm: ",IDC_STATIC,7,7,25,8
|
||||
LTEXT "",IDC_REALM,40,7,113,8
|
||||
LTEXT "Enter your login and password in the form of:\n\tlogin:password",IDC_STATIC,7,18,146,17
|
||||
EDITTEXT IDC_EDIT1,7,39,146,12,ES_AUTOHSCROLL
|
||||
DEFPUSHBUTTON "OK",IDOK,7,58,50,13
|
||||
PUSHBUTTON "Cancel",IDCANCEL,61,58,50,13
|
||||
END
|
||||
|
||||
IDD_INFO_ID3V1 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 "ID3v1",IDC_STATIC,0,0,175,133
|
||||
CONTROL "&Include ID3v1 tag in file",IDC_ID3V1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,9,100,10
|
||||
RTEXT "Track #",IDC_STATIC,7,24,36,8
|
||||
EDITTEXT IDC_ID3V11_TRACK,46,22,19,12,ES_AUTOHSCROLL | ES_NUMBER
|
||||
RTEXT "Title",IDC_STATIC,7,39,36,8
|
||||
EDITTEXT IDC_ID3_TITLE,46,37,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Artist",IDC_STATIC,7,54,36,8
|
||||
EDITTEXT IDC_ID3_ARTIST,46,52,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Album",IDC_STATIC,7,69,36,8
|
||||
EDITTEXT IDC_ID3_ALBUM,46,67,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Year",IDC_STATIC,7,83,36,8
|
||||
EDITTEXT IDC_ID3_YEAR,46,81,31,12,ES_NUMBER
|
||||
RTEXT "Genre",IDC_STATIC,81,83,22,8
|
||||
COMBOBOX IDC_ID3_GENRE,106,81,64,159,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
|
||||
RTEXT "Comment",IDC_STATIC,7,99,36,8
|
||||
EDITTEXT IDC_ID3_COMMENT,46,97,123,12,ES_AUTOHSCROLL
|
||||
PUSHBUTTON "Copy &to ID3v2",IDC_ID3V1_TO_V2,107,113,62,14
|
||||
END
|
||||
|
||||
IDD_INFO_ID3V2 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 "ID3v2",IDC_STATIC,0,0,341,164
|
||||
CONTROL "&Include ID3v2 tag in file",IDC_ID3V2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,9,108,10
|
||||
RTEXT "Track #",IDC_STATIC,7,24,36,8
|
||||
EDITTEXT IDC_ID3V2_TRACK,46,22,36,12,ES_AUTOHSCROLL
|
||||
RTEXT "Disc #",IDC_STATIC,90,24,36,8
|
||||
EDITTEXT IDC_ID3V2_DISC,128,22,42,12,ES_AUTOHSCROLL
|
||||
RTEXT "Title",IDC_STATIC,7,39,36,8
|
||||
EDITTEXT IDC_ID3V2_TITLE,46,37,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Artist",IDC_STATIC,7,54,36,8
|
||||
EDITTEXT IDC_ID3V2_ARTIST,46,52,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Album",IDC_STATIC,7,69,36,8
|
||||
EDITTEXT IDC_ID3V2_ALBUM,46,67,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Year",IDC_STATIC,7,83,36,8
|
||||
EDITTEXT IDC_ID3V2_YEAR,46,81,31,12,ES_AUTOHSCROLL | ES_NUMBER
|
||||
RTEXT "Genre",IDC_STATIC,81,83,22,8
|
||||
COMBOBOX IDC_ID3V2_GENRE,106,81,64,159,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
|
||||
RTEXT "Comment",IDC_STATIC,7,99,36,8
|
||||
EDITTEXT IDC_ID3V2_COMMENT,46,97,124,29,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
|
||||
RTEXT "Album Artist",IDC_STATIC,3,131,40,8
|
||||
EDITTEXT IDC_ID3V2_ALBUM_ARTIST,46,129,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Composer",IDC_STATIC,7,146,36,8
|
||||
EDITTEXT IDC_ID3V2_COMPOSER,46,144,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Publisher",IDC_STATIC,172,24,37,8
|
||||
EDITTEXT IDC_ID3V2_PUBLISHER,212,22,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Orig. Artist",IDC_STATIC,173,39,36,8
|
||||
EDITTEXT IDC_ID3V2_MARTIST,212,37,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Copyright",IDC_STATIC,173,54,36,8
|
||||
EDITTEXT IDC_ID3V2_RECORD,212,52,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "URL",IDC_STATIC,173,69,36,8
|
||||
EDITTEXT IDC_ID3V2_URL,212,67,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "Encoded by",IDC_STATIC,173,84,36,8
|
||||
EDITTEXT IDC_ID3V2_ENCODER,212,82,124,12,ES_AUTOHSCROLL
|
||||
RTEXT "BPM",IDC_STATIC,173,99,36,8
|
||||
EDITTEXT IDC_ID3V2_BPM,212,97,41,12,ES_AUTOHSCROLL
|
||||
RTEXT "Track Gain",IDC_STATIC,173,114,36,8
|
||||
EDITTEXT IDC_TRACK_GAIN,212,112,41,12,ES_AUTOHSCROLL | ES_READONLY
|
||||
RTEXT "Album Gain",IDC_STATIC,255,114,36,8
|
||||
EDITTEXT IDC_ALBUM_GAIN,294,112,41,12,ES_AUTOHSCROLL | ES_READONLY
|
||||
PUSHBUTTON "Copy &to ID3v1",IDC_ID3V2_TO_V1,272,144,62,14
|
||||
END
|
||||
|
||||
IDD_INFO_LYRICS3 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 "Lyrics3",-1,0,0,196,70
|
||||
CONTROL "&Include Lyrics3 tag in file",IDC_LYRICS3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,9,108,10
|
||||
RTEXT "Title",-1,7,24,36,8
|
||||
EDITTEXT IDC_LYRICS3_TITLE,46,22,144,12,ES_AUTOHSCROLL | ES_READONLY
|
||||
RTEXT "Artist",-1,7,39,36,8
|
||||
EDITTEXT IDC_LYRICS3_ARTIST,46,37,144,12,ES_AUTOHSCROLL | ES_READONLY
|
||||
RTEXT "Album",-1,7,54,36,8
|
||||
EDITTEXT IDC_LYRICS3_ALBUM,46,52,144,12,ES_AUTOHSCROLL | ES_READONLY
|
||||
END
|
||||
|
||||
IDD_INFO_APEV2 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 "APEv2",IDC_STATIC,0,0,341,164
|
||||
CONTROL "&Include APEv2 tag in file",IDC_APEV2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,9,108,10
|
||||
CONTROL "",IDC_APE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,6,20,163,136
|
||||
LTEXT "Name:",IDC_STATIC,176,20,22,8
|
||||
EDITTEXT IDC_APE_KEY,176,31,158,14,ES_AUTOHSCROLL
|
||||
LTEXT "Value:",IDC_STATIC,176,47,21,8
|
||||
EDITTEXT IDC_APE_VALUE,176,57,158,81,ES_MULTILINE | ES_AUTOHSCROLL | ES_OEMCONVERT | ES_WANTRETURN | WS_VSCROLL
|
||||
PUSHBUTTON "Add New",IDC_APE_ADD,176,142,50,14
|
||||
PUSHBUTTON "Delete",IDC_APE_DELETE,230,142,50,14
|
||||
PUSHBUTTON "Delete All",IDC_DELETE_ALL,284,142,50,14
|
||||
END
|
||||
|
||||
IDD_ADVANCED_TAGGING DIALOGEX 0, 0, 226, 122
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION
|
||||
CAPTION "APEv2 and Lyrics3"
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
GROUPBOX "APEv2 Tags",-1,6,6,214,111
|
||||
CONTROL "Read APEv2 tags",IDC_READ_APEV2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,17,73,10
|
||||
CONTROL "Modify existing APEv2 tags when updating metadata",IDC_WRITE_APEV2,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,29,181,10
|
||||
CONTROL "Create new APEv2 tags when adding metadata",IDC_CREATE_APEV2,
|
||||
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,40,165,10
|
||||
LTEXT "When writing APEv2 headers:",IDC_STATIC_APEV2_HEADER,13,54,96,8
|
||||
COMBOBOX IDC_APEV2_HEADER_OPTIONS,25,66,166,40,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
GROUPBOX "Lyrics3 Tags",-1,6,90,214,27
|
||||
CONTROL "Read Lyrics3 tags",IDC_READ_LYRICS3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,102,73,10
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_MPEG_AUDIO_DECODER_OLD "Nullsoft MPEG Audio Decoder"
|
||||
IDS_CANNOT_WRITE_STREAMS_TO_DISK "Sorry cannot write streams to disk"
|
||||
IDS_BUFFER_X "Buffer: %d%%"
|
||||
IDS_ERROR_SYNCING_TO_STREAM "Error syncing to stream"
|
||||
IDS_CONNECTING "Connecting"
|
||||
IDS_NO_VALID_MULTICONNECT_URL "No valid Multiconnect URL"
|
||||
IDS_REDIRECT_LIMIT_EXCEEDED "Redirect limit exceeded"
|
||||
IDS_READING_ID3 "Reading ID3"
|
||||
IDS_STREAM_TERMINATED "Stream terminated"
|
||||
IDS_STREAM_TEMPORARILY_INTERRUPTED "Stream temporarily interrupted"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_MPEG_AUDIO_DECODER "Nullsoft MPEG Audio Decoder v%s"
|
||||
65535 "{CD3EEF98-011C-4213-BC16-3F91C937B9B8}"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NETWORK_RECEIVED_X_BYTES "Network received: %llu bytes\n"
|
||||
IDS_SERVER "Server: %s\n"
|
||||
IDS_CONTENT_TYPE "Content-Type: %s\n"
|
||||
IDS_ULTRAVOX_SYNC "Ultravox sync: %d messages, %d desyncs\n"
|
||||
IDS_ULTRAVOX_DATA_MESSAGE "Ultravox Data Message: 0x%X\n"
|
||||
IDS_ULTRAVOX_SID_AVGBR_MAXBR "Ultravox SID/AvgBR/MaxBR: %d/%d/%d\n"
|
||||
IDS_METADATA_RECEIVED "Metadata received: %d bytes\n"
|
||||
IDS_METADATA_INTERVAL "Metadata interval: %d bytes\n"
|
||||
IDS_ID3v2_TAG "ID3v2 tag: %d bytes\n"
|
||||
IDS_VBR_LEADING_FRAME "VBR leading frame: %d bytes\n"
|
||||
IDS_STREAM_NAME "Stream name: %s\n"
|
||||
IDS_CURRENT_TITLE "Current title: %s\n"
|
||||
IDS_CONTENT_LENGTH "Content length: %d bytes\n"
|
||||
IDS_SAVING_TO "Saving to: %s\n"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_CUSTOM "Custom"
|
||||
IDS_MONO "Mono"
|
||||
IDS_STEREO "Stereo"
|
||||
IDS_3_CHANNEL "3 channel"
|
||||
IDS_4_CHANNEL "4 channel"
|
||||
IDS_SURROUND "Surround"
|
||||
IDS_5_1 "5.1"
|
||||
IDS_7_1 "7.1"
|
||||
IDS_ERROR "error"
|
||||
IDS_NONE "None"
|
||||
IDS_50_15_MICROSEC "50/15 microsec"
|
||||
IDS_INVALID "invalid"
|
||||
IDS_JOINT_STEREO "Joint Stereo"
|
||||
IDS_2_CHANNEL "2 Channel"
|
||||
IDS_PAYLOAD_SIZE "Payload Size: %u bytes"
|
||||
IDS_FORMAT_AAC "\nFormat: AAC"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_MPEG2_HE_AAC_IS "\nMPEG-2 HE-AAC (Implicitly Signalled)"
|
||||
IDS_MPEG4_HE_AAC_IS "\nMPEG-4 HE-AAC (Implicitly Signalled)"
|
||||
IDS_SAMPLE_RATE_OUTPUT "\nSample Rate: %u (Output: %u)"
|
||||
IDS_SAMPLE_RATE "\nSample Rate: %u "
|
||||
IDS_SBR_PRESENT "\nSBR: Present"
|
||||
IDS_SBR_NOT_PRESENT "\nSBR: Not Present"
|
||||
IDS_CHANNELS_OUTPUT "\nChannels: %u (Output: %u)"
|
||||
IDS_CHANNELS "\nChannels: %u"
|
||||
IDS_MODE_MONO " Mode: Mono"
|
||||
IDS_MODE_STEREO " Mode: Stereo"
|
||||
IDS_MODE_PARAMETRIC_STEREO " Mode: Parametric Stereo"
|
||||
IDS_MODE_DUAL_CHANNEL " Mode: Dual Channel"
|
||||
IDS_MODE_4_CHANNEL_2_CPE " Mode: 4 Channel 2 CPE"
|
||||
IDS_MODE_4_CHANNEL_MPEG " Mode: 4 Channel MPEG"
|
||||
IDS_MODE_5_CHANNEL " Mode: 5 Channel"
|
||||
IDS_MODE_5_1 " Mode: 5.1"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_MODE_6_1 " Mode: 6.1"
|
||||
IDS_MODE_7_1 " Mode: 7.1"
|
||||
IDS_BITRATE "\nBitrate: %u"
|
||||
IDS_AVERAGE_BITRATE "\nBitrate: VBR (%u)"
|
||||
IDS_HEADER_FOUND_AT_X_BYTES "\nHeader found at: %d bytes"
|
||||
IDS_LENGTH_X_SECONDS "\nLength: %u seconds"
|
||||
IDS_PROFILE "\nProfile: %s"
|
||||
IDS_YES "Yes"
|
||||
IDS_NO "No"
|
||||
IDS_ENC_DELAY_ZERO_PADDING "\nEnc Delay: %u, Zero Padding: %u"
|
||||
IDS_S_LAYER_X "\n%s layer %d"
|
||||
IDS_X_KBIT "\n%d kbps, %d frames"
|
||||
IDS_X_KBIT_APPROX "\n%d kbps, approx. %d frames"
|
||||
IDS_X_KBIT_VBR "\n%d kbps (VBR%s), %d frames"
|
||||
IDS_X_HZ_S "\n%d Hz %s"
|
||||
IDS_COPYRIGHTED ", Copyrighted: %s"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_ORIGINAL "\nOriginal: %s"
|
||||
IDS_EMPHASIS ", Emphasis: %s"
|
||||
IDS_EXT_MP3_SURROUND "\nExtension: MP3 Surround"
|
||||
IDS_MP3_HAS_BEEN_MODIFIED_NOT_ALL_MAY_BE_CORRECT
|
||||
"\nMP3 has been modified from its original encoding. Some information may be incorrect."
|
||||
IDS_MPEG_AUDIO_FILES "MPEG Audio Files ("
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_MPEG_AUDIO_DECODER_SETTINGS "MPEG Audio Decoder Settings"
|
||||
IDS_LATIN_1 "Latin-1"
|
||||
IDS_SYSTEM_LANGUAGE "System Language"
|
||||
IDS_UNICODE_UTF_16 "Unicode (UTF-16)"
|
||||
IDS_SELECT_DIRECTORY_TO_SAVE_TO "Select directory to save streamed mp3s"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_X_KBIT_ABR "\n%d kbps (ABR), %d frames"
|
||||
IDS_NAME "Name"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_VALUE "Value"
|
||||
IDS_APEV2_RETAIN_HEADER "Retain existing header"
|
||||
IDS_APEV2_ADD_HEADER "Add header"
|
||||
IDS_APEV2_REMOVE_HEADER "Remove header"
|
||||
IDS_ERROR_SAVING_METADATA "Error saving metadata"
|
||||
IDS_METADATA_ERROR_READONLY "Cannot save metadata: File is read-only!"
|
||||
IDS_METADATA_ERROR_OPENING_FILE
|
||||
"Cannot save metadata: Error opening file."
|
||||
IDS_METADATA_ERROR_APEV2 "Cannot save metadata: APEv2 tag writing failed."
|
||||
IDS_METADATA_ERROR_LYRICS3
|
||||
"Cannot save metadata: Lyrics3 tag writing failed."
|
||||
IDS_METADATA_ERROR_ID3V1 "Cannot save metadata: ID3v1 tag writing failed."
|
||||
IDS_METADATA_ERROR_ID3V2 "Cannot save metadata: ID3v2 tag writing failed."
|
||||
IDS_METADATA_ERROR_UNSPECIFIED "Cannot save metadata: Unspecified error."
|
||||
IDS_TIMED_OUT "timed out"
|
||||
IDS_FAMILY_STRING_MP3 "MPEG Layer 3 Audio File"
|
||||
IDS_FAMILY_STRING_MP2 "MPEG Layer 2 Audio File"
|
||||
IDS_FAMILY_STRING_MP1 "MPEG Layer 1 Audio File"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_FAMILY_STRING_MPEG2_AAC "MPEG-2 Advanced Audio Coding File"
|
||||
IDS_FAMILY_STRING_DOLBY "Dolby Very Low Bitrate AAC File"
|
||||
IDS_ABOUT_TEXT "%s\n© 1998-2023 Winamp SA\n\nBuild date: %hs\n\nMPEG Audio Decoding provided by libmpg123"//MPEG Layer-3 audio compression technology\nlicensed by Fraunhofer IIS & THOMSON multimedia.\n\nMPEG-4 AAC decoding licensed by Fraunhofer IIS"
|
||||
IDS_LENGTH_X_PART_SECONDS "\nLength: %.2f seconds"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
#include "version.rc2"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29424.173
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_mp3", "in_mp3.vcxproj", "{AEA9FF14-57EC-49AB-BC3A-764A011AC002}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F}
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
|
||||
{E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apev2", "..\apev2\apev2.vcxproj", "{48387E27-2666-4ACF-9C21-AE508C529580}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
|
||||
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}") = "id3v2", "..\id3v2\id3v2.vcxproj", "{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}"
|
||||
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
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsutil", "..\nsutil\nsutil.vcxproj", "{DABE6307-F8DD-416D-9DAC-673E2DECB73F}"
|
||||
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
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Debug|x64.Build.0 = Debug|x64
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Release|Win32.Build.0 = Release|Win32
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Release|x64.ActiveCfg = Release|x64
|
||||
{AEA9FF14-57EC-49AB-BC3A-764A011AC002}.Release|x64.Build.0 = Release|x64
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Debug|x64.Build.0 = Debug|x64
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|Win32.Build.0 = Release|Win32
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.Release|x64.ActiveCfg = Release|x64
|
||||
{48387E27-2666-4ACF-9C21-AE508C529580}.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
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Debug|x64.Build.0 = Debug|x64
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Release|Win32.Build.0 = Release|Win32
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.Release|x64.ActiveCfg = Release|x64
|
||||
{0C7AAFA2-2A78-4B91-99A3-3E866B484ADB}.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
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.Build.0 = Debug|x64
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.Build.0 = Release|Win32
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.ActiveCfg = Release|x64
|
||||
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FC646532-2050-40A5-A2AB-F699F1C071C4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,386 @@
|
||||
<?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>{AEA9FF14-57EC-49AB-BC3A-764A011AC002}</ProjectGuid>
|
||||
<RootNamespace>in_mp3</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>_DEBUG;AAC_SUPPORT;WIN32;_WINDOWS;_USRDLL;UNICODE_INPUT_PLUGIN;DO_LAYER12;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0409</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>wsock32.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>comctl32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<MapFileName>$(IntDir)$(TargetName).map</MapFileName>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</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>_DEBUG;AAC_SUPPORT;WIN64;_WINDOWS;_USRDLL;UNICODE_INPUT_PLUGIN;DO_LAYER12;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0409</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>wsock32.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>comctl32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<MapFileName>$(IntDir)$(TargetName).map</MapFileName>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</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>NDEBUG;AAC_SUPPORT;WIN32;_WINDOWS;_USRDLL;UNICODE_INPUT_PLUGIN;DO_LAYER12;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4996;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0409</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>wsock32.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>comctl32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<GenerateMapFile>false</GenerateMapFile>
|
||||
<MapFileName>$(IntDir)$(TargetName).map</MapFileName>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>true</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</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>NDEBUG;AAC_SUPPORT;WIN64;_WINDOWS;_USRDLL;UNICODE_INPUT_PLUGIN;DO_LAYER12;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4996;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Culture>0x0409</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>wsock32.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<DelayLoadDLLs>comctl32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<GenerateMapFile>false</GenerateMapFile>
|
||||
<MapFileName>$(IntDir)$(TargetName).map</MapFileName>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>true</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</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="..\..\..\apev2\apev2.vcxproj">
|
||||
<Project>{48387e27-2666-4acf-9c21-ae508c529580}</Project>
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\id3v2\id3v2.vcxproj">
|
||||
<Project>{0c7aafa2-2a78-4b91-99a3-3e866b484adb}</Project>
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\nsutil\nsutil.vcxproj">
|
||||
<Project>{dabe6307-f8dd-416d-9dac-673e2decb73f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\replicant\jnetlib\jnetlib.vcxproj">
|
||||
<Project>{e105a0a2-7391-47c5-86ac-718003524c3d}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\replicant\nu\nu.vcxproj">
|
||||
<Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
|
||||
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\nu\RingBuffer.h" />
|
||||
<ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_metatag.h" />
|
||||
<ClInclude Include="AACFrame.h" />
|
||||
<ClInclude Include="adts.h" />
|
||||
<ClInclude Include="adts_vlb.h" />
|
||||
<ClInclude Include="AlbumArt.h" />
|
||||
<ClInclude Include="apev2.h" />
|
||||
<ClInclude Include="api__in_mp3.h" />
|
||||
<ClInclude Include="config.h" />
|
||||
<ClInclude Include="CVbriHeader.h" />
|
||||
<ClInclude Include="DecodeThread.h" />
|
||||
<ClInclude Include="DXHEAD.H" />
|
||||
<ClInclude Include="FactoryHelper.h" />
|
||||
<ClInclude Include="giofile.h" />
|
||||
<ClInclude Include="id3.h" />
|
||||
<ClInclude Include="ID3v1.h" />
|
||||
<ClInclude Include="ID3v2.h" />
|
||||
<ClInclude Include="ifc_mpeg_stream_reader.h" />
|
||||
<ClInclude Include="LAMEinfo.h" />
|
||||
<ClInclude Include="Lyrics3.h" />
|
||||
<ClInclude Include="main.h" />
|
||||
<ClInclude Include="Metadata.h" />
|
||||
<ClInclude Include="MetadataFactory.h" />
|
||||
<ClInclude Include="MP3Info.h" />
|
||||
<ClInclude Include="mpegutil.h" />
|
||||
<ClInclude Include="OFL.h" />
|
||||
<ClInclude Include="pdtimer.h" />
|
||||
<ClInclude Include="RawMediaReader.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="Stopper.h" />
|
||||
<ClInclude Include="uvox_3901.h" />
|
||||
<ClInclude Include="uvox_3902.h" />
|
||||
<ClInclude Include="WasabiMetadata.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\nu\listview.cpp" />
|
||||
<ClCompile Include="..\..\..\nu\RingBuffer.cpp" />
|
||||
<ClCompile Include="AACFrame.cpp" />
|
||||
<ClCompile Include="adts_vlb.cpp" />
|
||||
<ClCompile Include="AlbumArt.cpp" />
|
||||
<ClCompile Include="apev2.cpp" />
|
||||
<ClCompile Include="config.cpp" />
|
||||
<ClCompile Include="CVbriHeader.cpp" />
|
||||
<ClCompile Include="DecodeThread.cpp" />
|
||||
<ClCompile Include="DXHEAD.C">
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsCpp</CompileAs>
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">CompileAsCpp</CompileAs>
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">CompileAsCpp</CompileAs>
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">CompileAsCpp</CompileAs>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedInfo.cpp" />
|
||||
<ClCompile Include="ExtendedRead.cpp" />
|
||||
<ClCompile Include="giofile.cpp" />
|
||||
<ClCompile Include="id3.cpp" />
|
||||
<ClCompile Include="id3dlg.cpp" />
|
||||
<ClCompile Include="ID3v1.cpp" />
|
||||
<ClCompile Include="ID3v2.cpp" />
|
||||
<ClCompile Include="LAMEinfo.cpp" />
|
||||
<ClCompile Include="Lyrics3.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="Metadata.cpp" />
|
||||
<ClCompile Include="MetadataFactory.cpp" />
|
||||
<ClCompile Include="MP3Info.cpp" />
|
||||
<ClCompile Include="mpegutil.cpp" />
|
||||
<ClCompile Include="OFL.cpp" />
|
||||
<ClCompile Include="pdtimer.cpp" />
|
||||
<ClCompile Include="RawMediaReader.cpp" />
|
||||
<ClCompile Include="Stopper.cpp" />
|
||||
<ClCompile Include="titlelist.cpp" />
|
||||
<ClCompile Include="uvox_3901.cpp" />
|
||||
<ClCompile Include="uvox_3902.cpp" />
|
||||
<ClCompile Include="WasabiMetadata.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_mp3.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,218 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AACFrame.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="adts_vlb.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AlbumArt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="apev2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CVbriHeader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DecodeThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DXHEAD.C">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedRead.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="giofile.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="id3.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="id3dlg.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ID3v1.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ID3v2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LAMEinfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Lyrics3.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="MetadataFactory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MP3Info.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="mpegutil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OFL.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pdtimer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RawMediaReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Stopper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="titlelist.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="uvox_3902.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="uvox_3901.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WasabiMetadata.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\listview.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\RingBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AACFrame.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="adts.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="adts_vlb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="apev2.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AlbumArt.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="api__in_mp3.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CVbriHeader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DecodeThread.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DXHEAD.H">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FactoryHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="giofile.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="id3.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ID3v1.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ID3v2.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ifc_mpeg_stream_reader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LAMEinfo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Lyrics3.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Metadata.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MetadataFactory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MP3Info.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="mpegutil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OFL.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pdtimer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RawMediaReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Stopper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="uvox_3901.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="uvox_3902.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WasabiMetadata.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\nu\RingBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_metatag.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{21897fc1-e5e6-4baf-8768-1f94870c3daf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Ressource Files">
|
||||
<UniqueIdentifier>{cf21f14f-48fa-49b3-bd4e-121aea3d1c61}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{d4950784-f6d6-4b2a-8b50-077f3edbee40}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_mp3.rc">
|
||||
<Filter>Ressource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,342 @@
|
||||
//#define PLUGIN_NAME "Nullsoft MPEG Audio Decoder"
|
||||
#include "main.h"
|
||||
#include <time.h>
|
||||
#include "DecodeThread.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "../nu/ServiceBuilder.h"
|
||||
#include "config.h"
|
||||
#include "AlbumArt.h"
|
||||
#include "MetadataFactory.h"
|
||||
#include "../nu/Singleton.h"
|
||||
#include "RawMediaReader.h"
|
||||
|
||||
char lastfn_status[256] = {0};
|
||||
int lastfn_status_err = 0;
|
||||
CRITICAL_SECTION g_lfnscs;
|
||||
CRITICAL_SECTION streamInfoLock;
|
||||
|
||||
int lastfn_data_ready;
|
||||
|
||||
int config_fastvis=0;
|
||||
unsigned char config_miscopts=0;
|
||||
unsigned char allow_sctitles=1;
|
||||
unsigned char sctitle_format=1;
|
||||
unsigned char config_eqmode=4,config_http_proxynonport80=1;
|
||||
unsigned int winampVersion=0x00005010; // default version # to use if winamp version is 5.1 or less (and therefore doesn't have a valid HWND during Init)
|
||||
char config_http_save_dir[MAX_PATH] = "C:\\";
|
||||
int config_http_buffersize=64, config_http_prebuffer=40, config_http_prebuffer_underrun=10;
|
||||
unsigned char config_downmix=0, config_downsample=0, allow_scartwork=1;
|
||||
|
||||
int config_max_bufsize_k=128;
|
||||
int config_gapless=1;
|
||||
char INI_FILE[MAX_PATH] = {0};
|
||||
|
||||
wchar_t lastfn[8192] = {0}; // currently playing file (used for getting info on the current file)
|
||||
|
||||
// Used for correcting DSP plug-in pitch changes
|
||||
int paused = 0; // are we paused?
|
||||
|
||||
int m_is_stream = 0;
|
||||
bool m_is_stream_seekable = true;
|
||||
|
||||
volatile int killDecodeThread=0; // the kill switch for the decode thread
|
||||
HANDLE thread_handle=INVALID_HANDLE_VALUE; // the handle to the decode thread
|
||||
|
||||
DWORD WINAPI DecodeThread(LPVOID b); // the decode thread procedure
|
||||
|
||||
extern char *getfileextensions();
|
||||
|
||||
|
||||
#include "api/service/waservicefactory.h"
|
||||
|
||||
#include "FactoryHelper.h"
|
||||
|
||||
// wasabi based services for localisation support
|
||||
HINSTANCE WASABI_API_LNG_HINST = 0;
|
||||
HINSTANCE WASABI_API_ORIG_HINST = 0;
|
||||
|
||||
api_language *WASABI_API_LNG = 0;
|
||||
api_application *WASABI_API_APP = 0;
|
||||
api_config *AGAVE_API_CONFIG = 0;
|
||||
api_memmgr *WASABI_API_MEMMGR = 0;
|
||||
|
||||
AlbumArtFactory albumArtFactory;
|
||||
MetadataFactory metadataFactory;
|
||||
|
||||
static RawMediaReaderService raw_media_reader_service;
|
||||
static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
|
||||
|
||||
int init()
|
||||
{
|
||||
if (!IsWindow(mod.hMainWindow))
|
||||
return IN_INIT_FAILURE;
|
||||
|
||||
winampVersion = (unsigned int)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION);
|
||||
mod.service->service_register(&metadataFactory);
|
||||
mod.service->service_register(&albumArtFactory);
|
||||
raw_factory.Register(mod.service, &raw_media_reader_service);
|
||||
|
||||
ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
|
||||
ServiceBuild(WASABI_API_LNG, languageApiGUID);
|
||||
ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
|
||||
ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
|
||||
|
||||
// need to have this initialised before we try to do anything with localisation features
|
||||
WASABI_API_START_LANG(mod.hDllInstance,InMp3LangGUID);
|
||||
|
||||
static wchar_t szDescription[256];
|
||||
swprintf(szDescription, 256, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MPEG_AUDIO_DECODER), PLUGIN_VERSION);
|
||||
mod.description = (char*)szDescription;
|
||||
|
||||
InitializeCriticalSection(&g_lfnscs);
|
||||
InitializeCriticalSection(&streamInfoLock);
|
||||
mod.UsesOutputPlug|=2;
|
||||
config_read();
|
||||
mod.FileExtensions=getfileextensions();
|
||||
return IN_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
DeleteCriticalSection(&g_lfnscs);
|
||||
DeleteCriticalSection(&streamInfoLock);
|
||||
ServiceRelease(mod.service, AGAVE_API_CONFIG, AgaveConfigGUID);
|
||||
ServiceRelease(mod.service, WASABI_API_APP, applicationApiServiceGuid);
|
||||
ServiceRelease(mod.service, WASABI_API_MEMMGR, memMgrApiServiceGuid);
|
||||
mod.service->service_deregister(&albumArtFactory);
|
||||
raw_factory.Deregister(mod.service);
|
||||
}
|
||||
|
||||
int g_eq_ok;
|
||||
|
||||
int isourfile(const wchar_t *fn)
|
||||
{
|
||||
if (!_wcsnicmp(fn,L"uvox://",7)) return 1;
|
||||
if (!_wcsnicmp(fn,L"icy://",6)) return 1;
|
||||
if (!_wcsnicmp(fn,L"sc://",5)) return 1;
|
||||
if (!_wcsnicmp(fn,L"shoutcast://",12)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int m_force_seek=-1;
|
||||
|
||||
// called when winamp wants to play a file
|
||||
int play(const in_char *fn)
|
||||
{
|
||||
DWORD thread_id;
|
||||
lastfn_status_err=0;
|
||||
paused=0;
|
||||
g_length=-1000;
|
||||
decode_pos_ms=0;
|
||||
seek_needed=m_force_seek;
|
||||
m_force_seek=-1;
|
||||
m_is_stream = 0;
|
||||
m_is_stream_seekable = false;
|
||||
killDecodeThread=0;
|
||||
g_sndopened=0;
|
||||
lastfn_data_ready=0;
|
||||
lastfn_status[0]=0;
|
||||
g_bufferstat=0;
|
||||
g_closeaudio=0;
|
||||
lstrcpynW(lastfn,fn, 8192);
|
||||
mod.is_seekable = 0;
|
||||
mod.SetInfo(0,0,0,0);
|
||||
|
||||
g_ds=config_downsample;
|
||||
|
||||
g_eq_ok=1;
|
||||
// launch decode thread
|
||||
thread_handle = (HANDLE)CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) DecodeThread,NULL,0,&thread_id);
|
||||
SetThreadPriority(thread_handle, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// standard pause implementation
|
||||
void pause()
|
||||
{
|
||||
paused=1;
|
||||
if (g_sndopened)
|
||||
mod.outMod->Pause(1);
|
||||
}
|
||||
|
||||
void unpause()
|
||||
{
|
||||
paused=0;
|
||||
if (g_sndopened)
|
||||
mod.outMod->Pause(0);
|
||||
}
|
||||
|
||||
int ispaused()
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
|
||||
// stop playing.
|
||||
void stop()
|
||||
{
|
||||
killDecodeThread=1;
|
||||
WaitForSingleObject(thread_handle,INFINITE);
|
||||
CloseHandle(thread_handle);
|
||||
g_eq_ok=0;
|
||||
thread_handle = INVALID_HANDLE_VALUE;
|
||||
g_length=-1000;
|
||||
lastfn[0]=0;
|
||||
if (g_closeaudio)
|
||||
{
|
||||
g_closeaudio=0;
|
||||
mod.outMod->Close();
|
||||
mod.SAVSADeInit();
|
||||
}
|
||||
g_sndopened=0;
|
||||
m_force_seek=-1;
|
||||
}
|
||||
|
||||
|
||||
// returns length of playing track
|
||||
int getlength()
|
||||
{
|
||||
return g_length;
|
||||
}
|
||||
|
||||
|
||||
// returns current output position, in ms.
|
||||
// you could just use return mod.outMod->GetOutputTime(),
|
||||
// but the dsp plug-ins that do tempo changing tend to make
|
||||
// that wrong.
|
||||
int getoutputtime()
|
||||
{
|
||||
if (g_bufferstat)
|
||||
return g_bufferstat;
|
||||
|
||||
if (!lastfn_data_ready||!g_sndopened)
|
||||
return 0;
|
||||
|
||||
if (seek_needed!=-1)
|
||||
return seek_needed;
|
||||
|
||||
return decode_pos_ms +
|
||||
(mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime());
|
||||
}
|
||||
|
||||
|
||||
// called when the user releases the seek scroll bar.
|
||||
// usually we use it to set seek_needed to the seek
|
||||
// point (seek_needed is -1 when no seek is needed)
|
||||
// and the decode thread checks seek_needed.
|
||||
void setoutputtime(int time_in_ms)
|
||||
{
|
||||
|
||||
|
||||
if (m_is_stream == 0 || (m_is_stream !=0 && m_is_stream_seekable))
|
||||
{
|
||||
seek_needed=time_in_ms;
|
||||
m_force_seek=-1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// standard volume/pan functions
|
||||
void setvolume(int volume)
|
||||
{
|
||||
mod.outMod->SetVolume(volume);
|
||||
}
|
||||
void setpan(int pan)
|
||||
{
|
||||
mod.outMod->SetPan(pan);
|
||||
}
|
||||
|
||||
|
||||
// this is an odd function. it is used to get the title and/or
|
||||
// length of a track.
|
||||
// if filename is either NULL or of length 0, it means you should
|
||||
// return the info of lastfn. Otherwise, return the information
|
||||
// for the file in filename.
|
||||
// if title is NULL, no title is copied into it.
|
||||
// if length_in_ms is NULL, no length is copied into it.
|
||||
|
||||
static int memcmpv(char *d, char v, int l)
|
||||
{
|
||||
while (l--)
|
||||
if (*d++ != v) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eq_set(int on, char data[10], int preamp)
|
||||
{
|
||||
int x;
|
||||
eq_preamp = preamp;
|
||||
eq_enabled = on;
|
||||
for (x = 0; x < 10; x ++)
|
||||
eq_tab[x] = data[x];
|
||||
|
||||
// if eq zeroed out, dont use eq
|
||||
if (eq_enabled && preamp==31 && !memcmpv(data,31,10))
|
||||
eq_enabled=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// render 576 samples into buf.
|
||||
// this function is only used by DecodeThread.
|
||||
|
||||
// note that if you adjust the size of sample_buffer, for say, 1024
|
||||
// sample blocks, it will still work, but some of the visualization
|
||||
// might not look as good as it could. Stick with 576 sample blocks
|
||||
// if you can, and have an additional auxiliary (overflow) buffer if
|
||||
// necessary..
|
||||
|
||||
|
||||
// module definition.
|
||||
|
||||
extern In_Module mod =
|
||||
{
|
||||
IN_VER_RET, // defined in IN2.H
|
||||
"nullsoft(in_mp3.dll)",
|
||||
0, // hMainWindow (filled in by winamp)
|
||||
0, // hDllInstance (filled in by winamp)
|
||||
0,
|
||||
// this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
|
||||
0, // is_seekable
|
||||
1, // uses output plug-in system
|
||||
config,
|
||||
about,
|
||||
init,
|
||||
quit,
|
||||
getfileinfo,
|
||||
id3Dlg,
|
||||
isourfile,
|
||||
play,
|
||||
pause,
|
||||
unpause,
|
||||
ispaused,
|
||||
stop,
|
||||
|
||||
getlength,
|
||||
getoutputtime,
|
||||
setoutputtime,
|
||||
|
||||
setvolume,
|
||||
setpan,
|
||||
|
||||
0,0,0,0,0,0,0,0,0, // visualization calls filled in by winamp
|
||||
|
||||
0,0, // dsp calls filled in by winamp
|
||||
|
||||
eq_set,
|
||||
|
||||
NULL, // setinfo call filled in by winamp
|
||||
|
||||
0, // out_mod filled in by winamp
|
||||
};
|
||||
|
||||
// exported symbol. Returns output module.
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllexport) In_Module * winampGetInModule2()
|
||||
{
|
||||
return &mod;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include "giofile.h"
|
||||
#include "dxhead.h"
|
||||
#include "CVbriHeader.h"
|
||||
#include "resource.h"
|
||||
#include "in2.h"
|
||||
|
||||
#define PLUGIN_VERSION L"4.6"
|
||||
|
||||
extern In_Module mod;
|
||||
|
||||
extern char INI_FILE[MAX_PATH];
|
||||
extern int g_length;
|
||||
extern int lastfn_data_ready;
|
||||
extern int id3Dlg(const wchar_t *fn, HWND hwnd);
|
||||
extern int getlength();
|
||||
extern void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms);
|
||||
|
||||
extern int config_max_bufsize_k;
|
||||
extern int config_gapless;
|
||||
extern int config_fastvis;
|
||||
extern unsigned char config_miscopts;
|
||||
extern unsigned char config_downmix, config_downsample;
|
||||
extern int config_http_buffersize, config_http_prebuffer, config_http_prebuffer_underrun;
|
||||
extern unsigned char allow_sctitles,sctitle_format, allow_scartwork;
|
||||
extern char config_http_save_dir[MAX_PATH];
|
||||
|
||||
extern wchar_t lastfn[8192]; // currently playing file (used for getting info on the current file)
|
||||
extern char g_stream_title[256];
|
||||
extern char lastfn_status[256];
|
||||
extern int lastfn_status_err;
|
||||
extern int paused; // are we paused?
|
||||
extern void config_read();
|
||||
extern void about(HWND hwndParent);
|
||||
|
||||
extern void strmBuf_Quit();
|
||||
extern int strmBuf_Start(char *streamurl, int num_bytes, int pre_buffer_top, int pre_buffer_bottom);
|
||||
extern int strmBuf_Read(void *data, int bytes_requested);
|
||||
|
||||
extern void config(HWND hwndParent);
|
||||
extern volatile int killDecodeThread;
|
||||
|
||||
extern unsigned char eq_preamp;
|
||||
extern unsigned char eq_enabled;
|
||||
extern unsigned char eq_tab[10];
|
||||
extern unsigned char config_eqmode;
|
||||
extern unsigned int winampVersion;
|
||||
extern int g_eq_ok;
|
||||
|
||||
extern CRITICAL_SECTION g_lfnscs;
|
||||
extern CRITICAL_SECTION streamInfoLock;
|
||||
|
||||
#if !defined(__alpha) && !defined(_WIN64)
|
||||
static __inline long float_to_long(double t)
|
||||
{
|
||||
long r;
|
||||
__asm fld t
|
||||
__asm fistp r
|
||||
return r;
|
||||
}
|
||||
#else
|
||||
#define float_to_long(x) ((long)( x ))
|
||||
#endif
|
||||
|
||||
extern void processMetaDataC(char *data, int len, int msgId );
|
||||
|
||||
enum
|
||||
{
|
||||
UVOX_METADATA_STYLE_AOLRADIO = 0,
|
||||
UVOX_METADATA_STYLE_SHOUTCAST = 1,
|
||||
UVOX_METADATA_STYLE_SHOUTCAST2 = 2,
|
||||
UVOX_METADATA_STYLE_SHOUTCAST2_ARTWORK = 3,
|
||||
UVOX_METADATA_STYLE_SHOUTCAST2_ARTWORK_PLAYING = 4,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void *Next;
|
||||
int style;
|
||||
long timer;
|
||||
char title[16384];
|
||||
int part;
|
||||
int total_parts;
|
||||
int part_len;
|
||||
int type;
|
||||
} TitleType;
|
||||
#define TITLELISTTYPE TitleType
|
||||
|
||||
extern TITLELISTTYPE *TitleLinkedList;
|
||||
extern TITLELISTTYPE TitleListTerminator;
|
||||
|
||||
extern void initTitleList(void);
|
||||
extern TITLELISTTYPE *newTitleListEntry(void);
|
||||
extern void removeTitleListEntry(TITLELISTTYPE *Entry);
|
||||
extern void clearTitleList(void);
|
||||
|
||||
char *GetUltravoxUserAgent();
|
||||
char *GetUserAgent();
|
||||
void w9x_lstrcpynW(wchar_t *dest, const wchar_t *src, int maxLen);
|
||||
|
||||
// maximum acceptable deviance between LAME header bytesize and real filesize (minus id3 tag)
|
||||
// has to be large enough to accomodate unknown tags (APE, lyrics3)
|
||||
const int MAX_ACCEPTABLE_DEVIANCE = 16384;
|
||||
void get_extended_info(const wchar_t *fn, int *len);
|
||||
#define UVOX_3901_LEN 32768
|
||||
|
||||
void ConvertTryUTF8(const char *in, wchar_t *out, size_t outlen);
|
||||
|
||||
#ifdef AAC_SUPPORT
|
||||
extern char config_extlist_aac[129];
|
||||
#define config_extlist config_extlist_aac
|
||||
#else
|
||||
extern char config_extlist[129];
|
||||
#endif
|
||||
|
||||
extern int m_force_seek;
|
||||
extern CGioFile *g_playing_file;
|
||||
|
||||
int _r_i(char *name, int def);
|
||||
void _w_i(char *name, int d);
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "vlb_sub/aacdecoder.h"
|
||||
#include "vlbout.h"
|
||||
|
||||
void CStreamInfo::setSampleRate()
|
||||
{
|
||||
SetSamplingRate(CChannelInfo::SamplingRateFromIndex(GetSamplingRateIndex ()));
|
||||
}
|
||||
|
||||
#ifndef ACTIVEX_CONTROL
|
||||
//methods used by in_mp4
|
||||
extern "C"
|
||||
{
|
||||
__declspec( dllexport ) int aacGetBitBuffer()
|
||||
{
|
||||
return (int) new CBitBuffer;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) int aacGetDecoderInterfaces(CAacDecoder **decoder, CBitBuffer *buf, CStreamInfo **info, VLBOut **dataout)
|
||||
{
|
||||
*decoder=new CAacDecoder(*buf);
|
||||
*info=new CStreamInfo;
|
||||
*dataout=new VLBOut();
|
||||
return 1;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) void aacDeleteBitBuffer(CBitBuffer *bitBuffer)
|
||||
{
|
||||
if (bitBuffer)
|
||||
delete bitBuffer;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) void aacDeleteDecoderInterfaces(CAacDecoder *decoder, CStreamInfo *info, VLBOut *dataout)
|
||||
{
|
||||
if (decoder)
|
||||
delete decoder;
|
||||
if (info)
|
||||
delete info;
|
||||
if (dataout)
|
||||
delete dataout;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,372 @@
|
||||
#include "DecodeThread.h"
|
||||
#include "main.h"
|
||||
// benski> cut some old shit
|
||||
// this code would take the filterbank coefficients to get an approximate spectrum
|
||||
// and modify the coefficients to do a quick&easy EQ
|
||||
// it's been off by default for quite a few versions (i think starting with 5.12)
|
||||
// but not I just yanked it out. cause none of us have 486SX's any more
|
||||
|
||||
static float eq_lookup1[64]={
|
||||
4.000000f,3.610166f,3.320019f,3.088821f,2.896617f,
|
||||
2.732131f,2.588368f,2.460685f,2.345845f,2.241498f,
|
||||
2.145887f,2.057660f,1.975760f,1.899338f,1.827707f,
|
||||
1.760303f,1.696653f,1.636363f,1.579094f,1.524558f,
|
||||
1.472507f,1.422724f,1.375019f,1.329225f,1.285197f,
|
||||
1.242801f,1.201923f,1.162456f,1.124306f,1.087389f,
|
||||
1.051628f,1.016951f,0.983296f,0.950604f,0.918821f,
|
||||
0.887898f,0.857789f,0.828454f,0.799853f,0.771950f,
|
||||
0.744712f,0.718108f,0.692110f,0.666689f,0.641822f,
|
||||
0.617485f,0.593655f,0.570311f,0.547435f,0.525008f,
|
||||
0.503013f,0.481433f,0.460253f,0.439458f,0.419035f,
|
||||
0.398970f,0.379252f,0.359868f,0.340807f,0.322060f,
|
||||
0.303614f,0.285462f,0.267593f,0.250000
|
||||
};
|
||||
|
||||
static float eq_lookup2[64] = {
|
||||
2.00000000f, 1.96825397f, 1.93650794f, 1.90476191f, 1.87301588f,
|
||||
1.84126985f, 1.80952382f, 1.77777779f, 1.74603176f, 1.71428573f,
|
||||
1.68253970f, 1.65079367f, 1.61904764f, 1.58730161f, 1.55555558f,
|
||||
1.52380955f, 1.49206352f, 1.46031749f, 1.42857146f, 1.39682543f,
|
||||
1.36507940f, 1.33333337f, 1.30158734f, 1.26984131f, 1.23809528f,
|
||||
1.20634925f, 1.17460322f, 1.14285719f, 1.11111116f, 1.07936513f,
|
||||
1.04761910f, 1.01587307f, 0.98412699f, 0.95238096f, 0.92063493f,
|
||||
0.88888890f, 0.85714287f, 0.82539684f, 0.79365081f, 0.76190478f,
|
||||
0.73015875f, 0.69841272f, 0.66666669f, 0.63492066f, 0.60317463f,
|
||||
0.57142860f, 0.53968257f, 0.50793654f, 0.47619048f, 0.44444445f,
|
||||
0.41269842f, 0.38095239f, 0.34920636f, 0.31746033f, 0.28571430f,
|
||||
0.25396827f, 0.22222222f, 0.19047619f, 0.15873016f, 0.12698413f,
|
||||
0.09523810f, 0.06349207f, 0.03174603f, 0.00000000
|
||||
};
|
||||
|
||||
unsigned char eq_preamp = 0;
|
||||
unsigned char eq_enabled = 0;
|
||||
unsigned char eq_tab[10] = {0};
|
||||
|
||||
float g_vis_table[2][2][32][18] = {0};
|
||||
|
||||
void mp3GiveVisData(float vistable[2][32][18],int gr, int nch)
|
||||
{
|
||||
if (g_vis_enabled)
|
||||
{
|
||||
memcpy(&g_vis_table[gr][0][0][0],&vistable[0][0][0],sizeof(float)*32*18*nch);
|
||||
}
|
||||
}
|
||||
|
||||
void mp2Equalize(float *xr, int nch, int srate, int nparts)
|
||||
{
|
||||
if (!g_eq_ok || !(mod.UsesOutputPlug & 2)) return;
|
||||
if (!eq_enabled) return;
|
||||
float *eq_lookup = (config_eqmode&1)?eq_lookup2:eq_lookup1;
|
||||
float preamp = eq_lookup[eq_preamp];
|
||||
int offs[11] = { 0,1,2,3,4,5,6,9,15,18,32};
|
||||
int scale_offs[11] = {0};
|
||||
int x;
|
||||
unsigned char eqt[12] = {0};
|
||||
memcpy(eqt+1,eq_tab,10);
|
||||
eqt[0]=eqt[1];
|
||||
eqt[11]=eqt[10];
|
||||
for (x = 0; x < 11; x ++)
|
||||
{
|
||||
scale_offs[x] = float_to_long( ((float) offs[x] / (float) srate * 44100.0));
|
||||
if (scale_offs[x] > 32) scale_offs[x] = 32;
|
||||
}
|
||||
{
|
||||
if (nch == 1)
|
||||
{
|
||||
register int i;
|
||||
for (i = 0; i < 10; i ++)
|
||||
{
|
||||
register int x=scale_offs[i];
|
||||
register int t=scale_offs[i+1];
|
||||
float d = eq_lookup[(int)eqt[i]]*preamp;
|
||||
float dd = (eq_lookup[(int)eqt[i+1]]*preamp-d)/(float)(t-x);
|
||||
if (dd < 0.000000001f && dd > -0.000000001f) dd = 0.000000001f;
|
||||
while (x < t)
|
||||
{
|
||||
register float *p = xr+x;
|
||||
int e=nparts;
|
||||
while (e--)
|
||||
{
|
||||
*(p) *= d;
|
||||
p+=32;
|
||||
}
|
||||
p += 32*(18-nparts);
|
||||
d += dd;
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
register int i;
|
||||
for (i = 0; i < 10; i ++)
|
||||
{
|
||||
register int x=scale_offs[i];
|
||||
register int t=scale_offs[i+1];
|
||||
float d = eq_lookup[(int)eqt[i]]*preamp;
|
||||
float dd = (eq_lookup[(int)eqt[i+1]]*preamp-d)/(float)(t-x);
|
||||
if (dd < 0.000000001f && dd > -0.000000001f) dd = 0.000000001f;
|
||||
while (x < t)
|
||||
{
|
||||
register float *p = xr+x;
|
||||
int e=nparts;
|
||||
while (e--)
|
||||
{
|
||||
*(p+32*18) *= d;
|
||||
*(p) *= d;
|
||||
p+=32;
|
||||
}
|
||||
p += 32*(18-nparts);
|
||||
d += dd;
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mp3Equalize(float *xr, int nch, int srate)
|
||||
{
|
||||
if (!g_eq_ok || !(mod.UsesOutputPlug & 2)) return;
|
||||
if (!eq_enabled) return;
|
||||
float *eq_lookup = (config_eqmode&1)?eq_lookup2:eq_lookup1;
|
||||
float preamp = eq_lookup[eq_preamp];
|
||||
static int scale_offs[11];
|
||||
static int lrate;
|
||||
unsigned char eqt[12] = {0};
|
||||
memcpy(eqt+1,eq_tab,10);
|
||||
eqt[0]=eqt[1];
|
||||
eqt[11]=eqt[10];
|
||||
if (lrate!=srate)
|
||||
{
|
||||
lrate=srate;
|
||||
for (int x = 0; x < 11; x ++)
|
||||
{
|
||||
int offs[11] = { 0,2,4, 9,16, 30, 63, 95,153,308, 576};
|
||||
scale_offs[x] = float_to_long( ((float) offs[x] / (float) srate * 44100.0));
|
||||
if (scale_offs[x] > 576) scale_offs[x] = 576;
|
||||
}
|
||||
}
|
||||
{
|
||||
if (nch == 1)
|
||||
{
|
||||
register float *p = xr;
|
||||
register float d = eq_lookup[eqt[0]]*preamp;
|
||||
register int i = 0;
|
||||
for (i = 0; i < 10; i ++)
|
||||
{
|
||||
register int x=scale_offs[i];
|
||||
register int t=scale_offs[i+1];
|
||||
register float dd = (eq_lookup[eqt[i+1]]*preamp-d)/(float)(t-x);
|
||||
while (x++ < t)
|
||||
{
|
||||
*(p++) *= d;
|
||||
d += dd;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
register float *p = xr;
|
||||
register int i;
|
||||
for (i = 0; i < 10; i ++)
|
||||
{
|
||||
register int x=scale_offs[i];
|
||||
register int t=scale_offs[i+1];
|
||||
float d = eq_lookup[(int)eqt[i]]*preamp;
|
||||
float dd = (eq_lookup[(int)eqt[i+1]]*preamp-d)/(float)(t-x);
|
||||
if (dd < 0.000000001f && dd > -0.000000001f) dd = 0.000000001f;
|
||||
while (x++ < t)
|
||||
{
|
||||
*(p+32*18) *= d;
|
||||
*(p++) *= d;
|
||||
d += dd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void genOsc(char *tempdata, short *samples, int len)
|
||||
{
|
||||
float d = 0.0f, dd = len/(75.0f*2.0f);
|
||||
short int *sbuf = samples;
|
||||
int x,y=0;
|
||||
signed char *c = (signed char *) tempdata;
|
||||
for (x = 0; x < 75; x ++)
|
||||
{
|
||||
float q=0;
|
||||
int td = float_to_long((d+=dd));
|
||||
for (; y < td; y ++)
|
||||
q += *sbuf++;
|
||||
q *= (32.0f/(dd*65536.0f));
|
||||
*c++ = (signed char) float_to_long(q);
|
||||
}
|
||||
}
|
||||
void genSpec(char *tempdata, float *xr, int nch)
|
||||
{
|
||||
static int offsets[76] =
|
||||
{
|
||||
0,1,2,3,4,5,7,8,9,10,12,13,15,16,18,20,21,23,25,27,29,31,33,36,38,41,43,46,48,51,
|
||||
54,57,60,64,67,71,74,78,82,86,91,95,100,105,109,115,120,126,131,137,144,150,157,
|
||||
164,171,178,186,194,203,211,220,230,239,250,260,271,282,294,306,319,332,345,360,
|
||||
374,389,576
|
||||
};
|
||||
{
|
||||
for (int x = 0; x < 75; x++)
|
||||
{
|
||||
float sum = 0.0;
|
||||
int z;
|
||||
int sx = offsets[x];
|
||||
int ex = offsets[x+1];
|
||||
if (nch == 2)
|
||||
{
|
||||
float *p = &xr[0];
|
||||
int w=32*18;
|
||||
for (z = sx; z < ex; z ++)
|
||||
{
|
||||
register float t1=p[z], t2=p[w+z];
|
||||
if (t1 <0.0) t1=-t1;
|
||||
if (t2<0.0f) t2=-t2;
|
||||
sum += (t1+t2) * 0.5f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float *p = &xr[0];
|
||||
for (z = sx; z < ex; z ++)
|
||||
{
|
||||
register float t=p[z];
|
||||
if (t < 0.0) t=-t;
|
||||
sum += t;
|
||||
}
|
||||
}
|
||||
sum *= 1.0f + (x) * 12.0f / (ex-sx) ;
|
||||
sum *= 1.8f/24000.0f;
|
||||
if (sum < 0.0) sum = 0.0;
|
||||
if (sum > 255.0) sum = 255.0;
|
||||
tempdata[x] = (unsigned char) float_to_long(sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_layer3_vis(short *samples, float *xr, int nch, int ts)
|
||||
{
|
||||
int vis_waveNch;
|
||||
int vis_specNch;
|
||||
int csa = mod.SAGetMode();
|
||||
int is_vis_running = mod.VSAGetMode(&vis_specNch,&vis_waveNch);
|
||||
static char tempdata[75*2];
|
||||
int len=32*18*nch;
|
||||
|
||||
if (is_vis_running)
|
||||
{
|
||||
char data[576*4] = {0};
|
||||
int data_offs=0;
|
||||
int x;
|
||||
|
||||
if (nch == 1 && vis_specNch > 0)
|
||||
{
|
||||
float *specdata = xr;
|
||||
int y;
|
||||
for (y=0; y < 576; y++)
|
||||
{
|
||||
float p = *specdata++ / 24.0f;
|
||||
if (p < 0.0) p = -p;
|
||||
if (p > 255.0) p = 255.0;
|
||||
data[data_offs++] = (unsigned char) float_to_long(p);
|
||||
}
|
||||
if (vis_specNch == 2)
|
||||
{
|
||||
memcpy(data+data_offs,data+data_offs-576,576);
|
||||
data_offs += 576;
|
||||
}
|
||||
}
|
||||
else if (vis_specNch == 2)
|
||||
{
|
||||
for (x = 0; x < 2; x ++)
|
||||
{
|
||||
float *specdata = &xr[x*32*18];
|
||||
for (int y=0; y < 576; y++)
|
||||
{
|
||||
float p = *specdata++ / 24.0f;
|
||||
if (p < 0.0) p = -p;
|
||||
if (p > 255.0) p = 255.0;
|
||||
data[data_offs++] = (unsigned char) float_to_long(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (vis_specNch == 1)
|
||||
{
|
||||
float *specdata = &xr[0];
|
||||
int y;
|
||||
for (y = 0; y < 576; y++)
|
||||
{
|
||||
register float p1=specdata[0],p2=specdata[32*18],p;
|
||||
if (p1 < 0.0) p1=-p1;
|
||||
if (p2 < 0.0) p2=-p2;
|
||||
p = (p1+p2)/ 48.0f;
|
||||
specdata++;
|
||||
if (p > 255.0) p = 255.0;
|
||||
data[data_offs++] = (unsigned char) float_to_long(p);
|
||||
}
|
||||
} // end of spectrum code
|
||||
|
||||
if (nch == 1 && vis_waveNch > 0)
|
||||
{
|
||||
for (x = 0; x < 576; x++)
|
||||
{
|
||||
data[data_offs++] = ((samples[x])>>8);
|
||||
}
|
||||
if (vis_waveNch == 2)
|
||||
{
|
||||
memcpy(data+data_offs,data+data_offs-576,576);
|
||||
data_offs += 576;
|
||||
}
|
||||
}
|
||||
else if (vis_waveNch == 2)
|
||||
{
|
||||
for (x = 0; x < 2; x ++)
|
||||
{
|
||||
int y;
|
||||
for (y = 0; y < 576; y ++ )
|
||||
{
|
||||
data[data_offs++] = ((samples[y*2+x])>>8);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (vis_waveNch == 1)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < 576; x ++)
|
||||
{
|
||||
data[data_offs++] = ((int)samples[x*2]+(int)samples[x*2+1])>>9;
|
||||
}
|
||||
}
|
||||
mod.VSAAdd(data,ts);
|
||||
}
|
||||
if (csa==4)
|
||||
{
|
||||
tempdata[0] = 0;
|
||||
tempdata[1] = 0;
|
||||
mod.SAAdd(tempdata,ts,4);
|
||||
}
|
||||
else if (csa == 3)
|
||||
{
|
||||
genSpec(tempdata,xr,nch);
|
||||
genOsc(tempdata+75,samples,len);
|
||||
mod.SAAdd(tempdata,ts,0x80000003);
|
||||
}
|
||||
else if (csa == 2)
|
||||
{
|
||||
genOsc(tempdata,samples,len);
|
||||
mod.SAAdd(tempdata,ts,2);
|
||||
}
|
||||
else if (csa == 1) {
|
||||
genSpec(tempdata,xr,nch);
|
||||
mod.SAAdd(tempdata,ts,1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef NULLSOFT_MPEGUTILH
|
||||
#define NULLSOFT_MPEGUTILH
|
||||
|
||||
extern float g_vis_table[2][2][32][18];
|
||||
void do_layer3_vis(short *samples, float *xr, int nch, int ts);
|
||||
void mp3Equalize(float *xr, int nch, int srate);
|
||||
void mp2Equalize(float *xr, int nch, int srate, int nparts);
|
||||
void mp3GiveVisData(float vistable[2][32][18],int gr, int nch);
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "pdtimer.h"
|
||||
|
||||
__int64 pdReadResolution(void)
|
||||
{
|
||||
__int64 myfeq;
|
||||
LARGE_INTEGER feq;
|
||||
|
||||
QueryPerformanceFrequency( &feq);
|
||||
myfeq = feq.QuadPart;
|
||||
|
||||
return myfeq;
|
||||
}
|
||||
|
||||
__int64 pdReadTimer(void)
|
||||
{
|
||||
__int64 mynow;
|
||||
|
||||
LARGE_INTEGER now;
|
||||
|
||||
QueryPerformanceCounter( &now );
|
||||
mynow = now.QuadPart;
|
||||
|
||||
return mynow;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef NULLSOFT_PDTIMERH
|
||||
#define NULLSOFT_PDTIMERH
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
__int64 pdReadResolution(void);
|
||||
__int64 pdReadTimer(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
// proxydt.h
|
||||
#ifndef PROXYDT_H
|
||||
#define PROXYDT_H
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <atlbase.h>
|
||||
|
||||
char* detectBrowserProxy();
|
||||
|
||||
char* DetectIEProxy();
|
||||
|
||||
char* DetectNS4Proxy();
|
||||
char* DetectNS6Proxy();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,253 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by script1.rc
|
||||
//
|
||||
#define IDS_NULLSOFT_MPEG_AUDIO_DECODER_OLD 0
|
||||
#define IDS_CANNOT_WRITE_STREAMS_TO_DISK 1
|
||||
#define IDS_BUFFER_X 2
|
||||
#define IDC_ID3_REMOVE 3
|
||||
#define IDC_ID3V1_REMOVE 3
|
||||
#define IDC_REVERTTAG 3
|
||||
#define IDS_ERROR_SYNCING_TO_STREAM 3
|
||||
#define IDC_ID3V2_REMOVE 4
|
||||
#define IDS_CONNECTING 4
|
||||
#define IDC_ID3V1_SAVE 5
|
||||
#define IDS_NO_VALID_MULTICONNECT_URL 5
|
||||
#define IDC_ID3_REMOVE_ALL 6
|
||||
#define IDS_REDIRECT_LIMIT_EXCEEDED 6
|
||||
#define IDC_ID3V1_TO_V2 7
|
||||
#define IDS_READING_ID3 7
|
||||
#define IDC_ID3V2_SAVE 8
|
||||
#define IDS_STREAM_TERMINATED 8
|
||||
#define IDC_ID3V2_TO_V1 9
|
||||
#define IDS_STREAM_TEMPORARILY_INTERRUPTED 9
|
||||
#define IDC_ID3V2_STOP 10
|
||||
#define IDS_NETWORK_RECEIVED_X_BYTES 16
|
||||
#define IDS_SERVER 17
|
||||
#define IDS_CONTENT_TYPE 18
|
||||
#define IDS_ULTRAVOX_SYNC 19
|
||||
#define IDS_ULTRAVOX_DATA_MESSAGE 20
|
||||
#define IDS_ULTRAVOX_SID_AVGBR_MAXBR 21
|
||||
#define IDS_METADATA_RECEIVED 22
|
||||
#define IDS_METADATA_INTERVAL 23
|
||||
#define IDS_ID3v2_TAG 24
|
||||
#define IDS_VBR_LEADING_FRAME 25
|
||||
#define IDS_STREAM_NAME 26
|
||||
#define IDS_CURRENT_TITLE 27
|
||||
#define IDS_CONTENT_LENGTH 28
|
||||
#define IDS_SAVING_TO 29
|
||||
#define IDS_CUSTOM 32
|
||||
#define IDS_MONO 33
|
||||
#define IDS_STEREO 34
|
||||
#define IDS_3_CHANNEL 35
|
||||
#define IDS_4_CHANNEL 36
|
||||
#define IDS_SURROUND 37
|
||||
#define IDS_5_1 38
|
||||
#define IDS_7_1 39
|
||||
#define IDS_ERROR 40
|
||||
#define IDS_NONE 41
|
||||
#define IDS_50_15_MICROSEC 42
|
||||
#define IDS_INVALID 43
|
||||
#define IDS_JOINT_STEREO 44
|
||||
#define IDS_2_CHANNEL 45
|
||||
#define IDS_PAYLOAD_SIZE 46
|
||||
#define IDS_FORMAT_AAC 47
|
||||
#define IDS_MPEG2_HE_AAC_IS 48
|
||||
#define IDS_MPEG4_HE_AAC_IS 49
|
||||
#define IDS_SAMPLE_RATE_OUTPUT 50
|
||||
#define IDS_SAMPLE_RATE 51
|
||||
#define IDS_SBR_PRESENT 52
|
||||
#define IDS_SBR_NOT_PRESENT 53
|
||||
#define IDS_CHANNELS_OUTPUT 54
|
||||
#define IDS_CHANNELS 55
|
||||
#define IDS_MODE_MONO 56
|
||||
#define IDS_MODE_STEREO 57
|
||||
#define IDS_MODE_PARAMETRIC_STEREO 58
|
||||
#define IDS_MODE_DUAL_CHANNEL 59
|
||||
#define IDS_MODE_4_CHANNEL_2_CPE 60
|
||||
#define IDS_MODE_4_CHANNEL_MPEG 61
|
||||
#define IDS_MODE_5_CHANNEL 62
|
||||
#define IDS_MODE_5_1 63
|
||||
#define IDS_MODE_6_1 64
|
||||
#define IDS_MODE_7_1 65
|
||||
#define IDS_BITRATE 66
|
||||
#define IDS_AVERAGE_BITRATE 67
|
||||
#define IDS_HEADER_FOUND_AT_X_BYTES 68
|
||||
#define IDS_LENGTH_X_SECONDS 69
|
||||
#define IDS_PROFILE 70
|
||||
#define IDS_YES 71
|
||||
#define IDS_NO 72
|
||||
#define IDS_ENC_DELAY_ZERO_PADDING 73
|
||||
#define IDS_S_LAYER_X 74
|
||||
#define IDS_X_KBIT 75
|
||||
#define IDS_X_KBIT_APPROX 76
|
||||
#define IDS_X_KBIT_VBR 77
|
||||
#define IDS_X_HZ_S 78
|
||||
#define IDS_COPYRIGHTED 79
|
||||
#define IDS_ORIGINAL 80
|
||||
#define IDS_EMPHASIS 81
|
||||
#define IDS_EXT_MP3_SURROUND 82
|
||||
#define IDS_MP3_HAS_BEEN_MODIFIED_NOT_ALL_MAY_BE_CORRECT 83
|
||||
#define IDS_UPDATING_FILE 84
|
||||
#define IDS_CANNOT_MODIFY_TAG_READ_ONLY 85
|
||||
#define IDS_CANNOT_INSERT_ID3v2_TAG_DRIVE_FULL 86
|
||||
#define IDS_CANNOT_INSERT_ID3v2_TAG_STOP_PLAYBACK 87
|
||||
#define IDS_CANNOT_INSERT_ID3v2_TAG_READ_ONLY 88
|
||||
#define IDS_UPDATING_FILE_REMOVING_TAG 89
|
||||
#define IDS_CANNOT_REMOVE_ID3v2_TAG_DRIVE_FULL 90
|
||||
#define IDS_CANNOT_REMOVE_ID3v2_TAG_STOP_TRY_AGAIN 91
|
||||
#define IDS_CANNOT_REMOVE_ID3v2_TAG_READ_ONLY 92
|
||||
#define IDS_CANNOT_WRITE_ID3v1_TAG 93
|
||||
#define IDS_CANNOT_REMOVE_ID3v1_TAG 94
|
||||
#define IDS_MPEG_AUDIO_FILES 95
|
||||
#define IDS_MPEG_AUDIO_DECODER_SETTINGS 96
|
||||
#define IDS_LATIN_1 97
|
||||
#define IDS_SYSTEM_LANGUAGE 98
|
||||
#define IDS_UNICODE_UTF_16 99
|
||||
#define IDS_SELECT_DIRECTORY_TO_SAVE_TO 100
|
||||
#define ID_YES 101
|
||||
#define IDD_PREFS 103
|
||||
#define IDD_OUTPUT 104
|
||||
#define IDD_STREAM 115
|
||||
#define IDD_HTTP 177
|
||||
#define IDD_TAGOPTS 179
|
||||
#define IDD_HTTPAUTH 182
|
||||
#define IDD_INFO_ID3V1 186
|
||||
#define IDD_INFO_ID3V2 187
|
||||
#define IDD_INFO_LYRICS3 188
|
||||
#define IDS_X_KBIT_ABR 189
|
||||
#define IDD_INFO_APEV2 189
|
||||
#define IDD_ADVANCED_TAGGING 190
|
||||
#define IDS_NAME 191
|
||||
#define IDS_VALUE 192
|
||||
#define IDS_APEV2_RETAIN_HEADER 193
|
||||
#define IDS_APEV2_ADD_HEADER 194
|
||||
#define IDS_APEV2_REMOVE_HEADER 195
|
||||
#define IDS_ERROR_SAVING_METADATA 196
|
||||
#define IDS_METADATA_ERROR_READONLY 197
|
||||
#define IDS_METADATA_ERROR_OPENING_FILE 198
|
||||
#define IDS_METADATA_ERROR_APEV2 199
|
||||
#define IDS_METADATA_ERROR_LYRICS3 200
|
||||
#define IDS_METADATA_ERROR_ID3V1 201
|
||||
#define IDS_METADATA_ERROR_ID3V2 202
|
||||
#define IDS_METADATA_ERROR_UNSPECIFIED 203
|
||||
#define IDS_TIMED_OUT 204
|
||||
#define IDS_FAMILY_STRING_MP3 205
|
||||
#define IDS_FAMILY_STRING_MP2 206
|
||||
#define IDS_FAMILY_STRING_MP1 207
|
||||
#define IDS_FAMILY_STRING_MPEG2_AAC 208
|
||||
#define IDS_FAMILY_STRING_DOLBY 209
|
||||
#define IDS_ABOUT_STRING 210
|
||||
#define IDS_ABOUT_TEXT 210
|
||||
#define IDS_LENGTH_X_PART_SECONDS 211
|
||||
#define IDC_FASTL3EQ 1000
|
||||
#define IDC_BUFFERS_NUMBUFS 1001
|
||||
#define IDC_FASTL12EQ 1002
|
||||
#define IDC_ID3V1 1003
|
||||
#define IDC_ID3V2 1004
|
||||
#define IDC_16BIT 1005
|
||||
#define IDC_PREBUFSLIDER 1011
|
||||
#define IDC_PREBUFSLIDER2 1012
|
||||
#define IDC_STEREO 1046
|
||||
#define IDC_REVSTEREO 1048
|
||||
#define IDC_FULLRATE 1049
|
||||
#define IDC_HALFRATE 1050
|
||||
#define IDC_QRATE 1051
|
||||
#define IDC_PREFS_PRIORITY_DECODE 1076
|
||||
#define IDC_ID3_TITLE 1078
|
||||
#define IDC_ID3_ARTIST 1079
|
||||
#define IDC_ID3_ALBUM 1080
|
||||
#define IDC_ID3_COMMENT 1081
|
||||
#define IDC_ID3_YEAR 1082
|
||||
#define IDC_ID3_GENRE 1083
|
||||
#define IDC_ID3V2_TITLE 1086
|
||||
#define IDC_ID3V2_ARTIST 1087
|
||||
#define IDC_ID3V2_ALBUM 1088
|
||||
#define IDC_ID3V2_YEAR 1089
|
||||
#define IDC_ID3V2_GENRE 1090
|
||||
#define IDC_ID3V2_COMMENT 1091
|
||||
#define IDC_ID3V2_COMPOSER 1092
|
||||
#define IDC_ID3V2_MARTIST 1093
|
||||
#define IDC_ID3V2_RECORD 1094
|
||||
#define IDC_ID3V2_URL 1095
|
||||
#define IDC_ID3V2_ENCODER 1096
|
||||
#define IDC_ID3V2_TRACK 1097
|
||||
#define IDC_ID3V11_TRACK 1098
|
||||
#define IDC_BPM 1098
|
||||
#define IDC_ID3V2_BPM 1098
|
||||
#define IDC_ID3V2_ALBUM_ARTIST 1099
|
||||
#define IDC_ID3V2_ENCODER2 1100
|
||||
#define IDC_ID3V2_DISC 1100
|
||||
#define IDC_ID3V2_ENCODER3 1101
|
||||
#define IDC_ID3V2_PUBLISHER 1101
|
||||
#define IDC_RADIO1 1106
|
||||
#define IDC_RADIO2 1107
|
||||
#define IDC_CHECK1 1108
|
||||
#define IDC_STATUS 1109
|
||||
#define IDC_SC_ARTWORK 1109
|
||||
#define IDC_CHECK2 1110
|
||||
#define IDC_URL 1111
|
||||
#define IDC_BUTTON2 1112
|
||||
#define IDC_ID3FORMAT 1127
|
||||
#define IDC_NORMFORMAT 1128
|
||||
#define IDC_BUFMAX 1136
|
||||
#define IDC_CHECK3 1138
|
||||
#define IDC_ID3V2_STATUS_TEXT 1139
|
||||
#define IDC_EASTER 1143
|
||||
#define IDC_USEID3 1145
|
||||
#define IDC_EDIT1 1146
|
||||
#define IDC_BUTTON1 1147
|
||||
#define IDC_TRACK_GAIN 1147
|
||||
#define IDC_REALM 1148
|
||||
#define IDC_CHECK5 1150
|
||||
#define IDC_READ_LYRICS3 1150
|
||||
#define IDC_WRITE_LOCAL 1151
|
||||
#define IDC_READ_APEV2 1151
|
||||
#define IDC_WRITE_UTF16 1152
|
||||
#define IDC_WRITE_LATIN 1153
|
||||
#define IDC_READ_LATIN 1154
|
||||
#define IDC_READ_LOCAL 1155
|
||||
#define IDC_WRITE_ID3V1 1156
|
||||
#define IDC_CHECK7 1157
|
||||
#define IDC_WRITE_ID3V2 1157
|
||||
#define IDC_READ_ID3V1 1158
|
||||
#define IDC_READ_ID3V2 1159
|
||||
#define IDC_24BIT 1160
|
||||
#define IDC_WRITE_APEV2 1160
|
||||
#define IDC_CREATE_ID3V1 1160
|
||||
#define IDC_SURROUND 1161
|
||||
#define IDC_EDIT_REPLAYGAIN 1162
|
||||
#define IDC_CREATE_ID3V2 1162
|
||||
#define IDC_ALBUM_GAIN 1165
|
||||
#define IDC_COMBO1 1168
|
||||
#define IDC_COMBO2 1169
|
||||
#define IDC_LYRICS3_TITLE 1169
|
||||
#define IDC_LYRICS3_ARTIST 1170
|
||||
#define IDC_LYRICS3_ALBUM 1171
|
||||
#define IDC_LYRICS3V2 1172
|
||||
#define IDC_LYRICS3 1172
|
||||
#define IDC_APEV2 1173
|
||||
#define IDC_APELIST 1177
|
||||
#define IDC_APE_LIST 1177
|
||||
#define IDC_APE_KEY 1178
|
||||
#define IDC_DELETE_ALL 1179
|
||||
#define IDC_APE_VALUE 1180
|
||||
#define IDC_APE_ADD 1181
|
||||
#define IDC_APE_DELETE 1182
|
||||
#define IDC_CREATE_APEV2 1183
|
||||
#define IDC_STATIC_APEV2_HEADER 1184
|
||||
#define IDC_APEV2_HEADER_OPTIONS 1185
|
||||
#define IDC_RATING_EMAIL 1186
|
||||
#define IDC_RATING_EMAIL_RESET 1187
|
||||
#define IDS_NULLSOFT_MPEG_AUDIO_DECODER 65534
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 212
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1188
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 326 B |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,953 @@
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tagz.h"
|
||||
|
||||
#ifdef TAGZ_UNICODE
|
||||
#define _TX(X) L##X
|
||||
#define t_strdup _wcsdup
|
||||
#define t_strlen wcslen
|
||||
#define t_strnicmp _wcsnicmp
|
||||
#define t_strtoul wcstoul
|
||||
#define t_stricmp _wcsicmp
|
||||
#define t_strstr wcsstr
|
||||
|
||||
#define sprintf swprintf
|
||||
#else
|
||||
#define _TX(X) X
|
||||
#define t_strdup _strdup
|
||||
#define t_strlen strlen
|
||||
#define t_strnicmp _strnicmp
|
||||
#define t_strtoul strtoul
|
||||
#define t_stricmp _stricmp
|
||||
#define t_strstr strstr
|
||||
|
||||
#endif
|
||||
|
||||
int t_atoi(T_CHAR * c)
|
||||
{
|
||||
if (c[0]=='-') return -(int)t_strtoul(c+1,0,10);
|
||||
else return (int)t_strtoul(c,0,10);
|
||||
}
|
||||
|
||||
#define TABSIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
/*
|
||||
char * strndup(char * p1,UINT n)
|
||||
{
|
||||
char * r=(char*)malloc(n+1);
|
||||
if (r)
|
||||
{
|
||||
memcpy(r,p1,n);
|
||||
r[n]=0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
*/
|
||||
|
||||
class String
|
||||
{
|
||||
private:
|
||||
T_CHAR * data;
|
||||
UINT size, used;
|
||||
public:
|
||||
String() {data=0;size=0;used=0;}
|
||||
void AddChar(T_CHAR c)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
data=(T_CHAR*)malloc((size=512)*sizeof(T_CHAR));
|
||||
used=0;
|
||||
}
|
||||
else if (size==used)
|
||||
{
|
||||
UINT old_size = size;
|
||||
T_CHAR * old_data = data;
|
||||
size<<=1;
|
||||
data=(T_CHAR*)realloc((char*)data,size*sizeof(T_CHAR));
|
||||
if (!data)
|
||||
{
|
||||
size = old_size;
|
||||
data = old_data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data) data[used++]=c;
|
||||
}
|
||||
void AddInt(int i)
|
||||
{
|
||||
T_CHAR foo[16];
|
||||
sprintf(foo,_TX("%i"),i);
|
||||
AddString(foo);
|
||||
}
|
||||
void AddString(const T_CHAR * z)
|
||||
{
|
||||
while(*z) {AddChar(*z);z++;}
|
||||
}
|
||||
void AddString(String & s)
|
||||
{
|
||||
AddString(s.Peek());
|
||||
}
|
||||
~String()
|
||||
{
|
||||
if (data) free(data);
|
||||
}
|
||||
T_CHAR * GetBuf()
|
||||
{
|
||||
if (!data) return t_strdup(_TX(""));
|
||||
T_CHAR * r=(T_CHAR*)realloc(data,(used+1)*sizeof(T_CHAR));
|
||||
r[used]=0;
|
||||
data=0;
|
||||
return r;
|
||||
}
|
||||
T_CHAR operator[](UINT i)
|
||||
{
|
||||
if (!data || i>=used) return 0;
|
||||
else return data[i];
|
||||
}
|
||||
UINT Len() {return data ? used : 0;}
|
||||
void Reset()
|
||||
{
|
||||
if (data) {free(data);data=0;}
|
||||
}
|
||||
const T_CHAR * Peek()
|
||||
{
|
||||
AddChar(0);
|
||||
used--;
|
||||
return data;
|
||||
}
|
||||
T_CHAR * _strdup()
|
||||
{
|
||||
return ::t_strdup(Peek());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static int separator(T_CHAR x)
|
||||
{
|
||||
if (!x || x==' ') return 1;
|
||||
if (x=='\'' || x=='_') return 0;
|
||||
#ifdef TAGZ_UNICODE
|
||||
return !iswalnum(x);
|
||||
#else
|
||||
return !isalnum(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sepcmp(T_CHAR* src,T_CHAR* val)
|
||||
{
|
||||
UINT l=t_strlen(val);
|
||||
return !t_strnicmp(src,val,l) && separator(src[l]);
|
||||
}
|
||||
|
||||
static char roman_num[]=
|
||||
{
|
||||
'I','V','X','L','C','D','M'
|
||||
};
|
||||
|
||||
|
||||
static int is_roman(T_CHAR * ptr)//could be more smart i think
|
||||
{
|
||||
if (ptr[0]==']' && ptr[1]=='[' && separator(ptr[2])) return 1;
|
||||
while(!separator(*ptr))
|
||||
{
|
||||
UINT n;
|
||||
bool found=0;
|
||||
for(n=0;n<TABSIZE(roman_num);n++)
|
||||
{
|
||||
if (*ptr==roman_num[n]) {found=1;break;}
|
||||
}
|
||||
if (!found) return 0;
|
||||
ptr++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int need_full(T_CHAR* ptr)
|
||||
{
|
||||
if (is_roman(ptr)) return 1;
|
||||
if (sepcmp(ptr,_TX("RPG"))) return 1;
|
||||
while(!separator(*ptr))
|
||||
{
|
||||
if (*ptr<='0' || *ptr>='9') return 0;
|
||||
ptr++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
class VarList
|
||||
{
|
||||
private:
|
||||
typedef struct tagVAR
|
||||
{
|
||||
tagVAR * next;
|
||||
T_CHAR * name;
|
||||
T_CHAR * value;
|
||||
} VAR;
|
||||
VAR * vars;
|
||||
public:
|
||||
VarList() {vars=0;}
|
||||
~VarList()
|
||||
{
|
||||
VAR * p=vars;
|
||||
while(p)
|
||||
{
|
||||
VAR *n=p->next;
|
||||
free(p->name);
|
||||
free(p->value);
|
||||
delete p;
|
||||
p=n;
|
||||
}
|
||||
}
|
||||
T_CHAR * Get(T_CHAR * name)
|
||||
{
|
||||
VAR * p=vars;
|
||||
while(p)
|
||||
{
|
||||
if (!t_stricmp(name,p->name)) return p->value;
|
||||
p=p->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void Put(T_CHAR * name,T_CHAR* value)
|
||||
{
|
||||
VAR * p=vars;
|
||||
while(p)
|
||||
{
|
||||
if (!t_stricmp(name,p->name))
|
||||
{
|
||||
free(p->value);
|
||||
p->value=t_strdup(value);
|
||||
return;
|
||||
}
|
||||
p=p->next;
|
||||
}
|
||||
p=new VAR;
|
||||
p->next=vars;
|
||||
vars=p;
|
||||
p->value=t_strdup(value);
|
||||
p->name=t_strdup(name);
|
||||
}
|
||||
};
|
||||
|
||||
typedef void (*TEXTFUNC)(UINT n_src,T_CHAR **src,UINT*,String &out,VarList & vars);
|
||||
|
||||
|
||||
#define MAKEFUNC(X) static void X(UINT n_src,T_CHAR ** src,UINT *found_src,String &out,VarList &vars)
|
||||
|
||||
/*
|
||||
void Blah(UINT n_src,T_CHAR ** src,UINT*,String &out)
|
||||
{
|
||||
out.AddString("blah");
|
||||
}
|
||||
|
||||
void Nop(UINT n_src,T_CHAR ** src,UINT *,String &out)
|
||||
{
|
||||
UINT n;
|
||||
for(n=0;n<n_src;n++) out.AddString(src[n]);
|
||||
}
|
||||
*/
|
||||
|
||||
MAKEFUNC(Get)
|
||||
{
|
||||
if (n_src>=1)
|
||||
{
|
||||
T_CHAR * p=vars.Get(src[0]);
|
||||
if (p) out.AddString(p);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Put)
|
||||
{
|
||||
if (n_src>=2)
|
||||
{
|
||||
vars.Put(src[0],src[1]);
|
||||
out.AddString(src[1]);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(PutQ)
|
||||
{
|
||||
if (n_src>=2)
|
||||
{
|
||||
vars.Put(src[0],src[1]);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(If)
|
||||
{
|
||||
if (n_src!=3) out.AddString(_TX("[INVALID $IF SYNTAX]"));
|
||||
else
|
||||
{
|
||||
out.AddString(src[found_src[0] ? 1 : 2]);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Iflonger)
|
||||
{
|
||||
if (n_src!=4) out.AddString(_TX("[INVALID $IFLONGER SYNTAX]"));
|
||||
else
|
||||
{
|
||||
out.AddString(src[(int)t_strlen(src[0])>t_atoi(src[1]) ? 2 : 3]);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Ifgreater)
|
||||
{
|
||||
if (n_src!=4) out.AddString(_TX("[INVALID $IFGREATER SYNTAX]"));
|
||||
else
|
||||
{
|
||||
out.AddString(src[t_atoi(src[0])>t_atoi(src[1]) ? 2 : 3]);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Upper)
|
||||
{
|
||||
if (n_src>=1)
|
||||
{
|
||||
T_CHAR * s=src[0];
|
||||
while(*s)
|
||||
{
|
||||
out.AddChar(toupper(*(s++)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Lower)
|
||||
{
|
||||
if (n_src>=1)
|
||||
{
|
||||
T_CHAR * s=src[0];
|
||||
while(*s)
|
||||
{
|
||||
out.AddChar(tolower(*(s++)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Pad)
|
||||
{
|
||||
if (n_src>=2)
|
||||
{
|
||||
T_CHAR *fill=_TX(" ");
|
||||
if (n_src>=3 && src[2][0]) fill=src[2];
|
||||
int num=t_atoi(src[1]);
|
||||
T_CHAR *p=src[0];
|
||||
while(*p) {out.AddChar(*(p++));num--;}
|
||||
UINT fl=t_strlen(fill);
|
||||
while(num>0) {out.AddChar(fill[(--num)%fl]);}
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Cut)
|
||||
{
|
||||
if (n_src>=2)
|
||||
{
|
||||
UINT num=t_atoi(src[1]);
|
||||
T_CHAR *p=src[0];
|
||||
while(*p && num) {out.AddChar(*(p++));num--;}
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(PadCut)
|
||||
{
|
||||
if (n_src>=2)
|
||||
{
|
||||
T_CHAR *fill=_TX(" ");
|
||||
if (n_src>=3 && src[2][0]) fill=src[3];
|
||||
int num=t_atoi(src[1]);
|
||||
T_CHAR *p=src[0];
|
||||
while(*p && num>0) {out.AddChar(*(p++));num--;}
|
||||
UINT fl=t_strlen(fill);
|
||||
while(num>0) {out.AddChar(fill[(--num)%fl]);}
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Abbr)
|
||||
{//abbr(string,len)
|
||||
if (n_src==0 || (n_src>=2 && (int)t_strlen(src[0])<t_atoi(src[1]))) return;
|
||||
T_CHAR * meta=src[0];
|
||||
bool w=0,r=0;
|
||||
while(*meta)
|
||||
{
|
||||
bool an=!separator(*meta) || *meta==']' || *meta=='[';
|
||||
if (w && !an)
|
||||
{
|
||||
w=0;
|
||||
}
|
||||
else if (!w && an)
|
||||
{
|
||||
w=1;
|
||||
if (!sepcmp(meta,_TX("a")) && !sepcmp(meta,_TX("the")))
|
||||
{
|
||||
r=need_full(meta)?1:0;
|
||||
out.AddChar(*meta);
|
||||
}
|
||||
else r=0;
|
||||
}
|
||||
else if (w && r)
|
||||
{
|
||||
out.AddChar(*meta);
|
||||
}
|
||||
meta++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MAKEFUNC(Caps)
|
||||
{
|
||||
if (n_src<1) return;
|
||||
T_CHAR* sp=src[0];
|
||||
int sep=1;
|
||||
while(*sp)
|
||||
{
|
||||
T_CHAR c=*(sp++);
|
||||
int s = separator(c);
|
||||
if (!s && sep)
|
||||
c=toupper(c);
|
||||
else if (!sep) c=tolower(c);
|
||||
sep=s;
|
||||
out.AddChar(c);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Caps2)
|
||||
{
|
||||
if (n_src<1) return;
|
||||
T_CHAR* sp=src[0];
|
||||
int sep=1;
|
||||
while(*sp)
|
||||
{
|
||||
T_CHAR c=*(sp++);
|
||||
int s = separator(c);
|
||||
if (!s && sep)
|
||||
c=toupper(c);
|
||||
sep=s;
|
||||
out.AddChar(c);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Longest)
|
||||
{
|
||||
T_CHAR * ptr=0;
|
||||
UINT n,m=0;
|
||||
for(n=0;n<n_src;n++)
|
||||
{
|
||||
UINT l=t_strlen(src[n]);
|
||||
if (l>m) {m=l;ptr=src[n];}
|
||||
}
|
||||
if (ptr) out.AddString(ptr);
|
||||
}
|
||||
|
||||
MAKEFUNC(Shortest)
|
||||
{
|
||||
T_CHAR * ptr=0;
|
||||
UINT n,m=(UINT)(-1);
|
||||
for(n=0;n<n_src;n++)
|
||||
{
|
||||
UINT l=t_strlen(src[n]);
|
||||
if (l<m) {m=l;ptr=src[n];}
|
||||
}
|
||||
if (ptr) out.AddString(ptr);
|
||||
}
|
||||
|
||||
MAKEFUNC(Num)
|
||||
{
|
||||
if (n_src==2)
|
||||
{
|
||||
T_CHAR tmp[16];
|
||||
T_CHAR tmp1[16];
|
||||
sprintf(tmp1,_TX("%%0%uu"),t_atoi(src[1]));
|
||||
sprintf(tmp,tmp1,t_atoi(src[0]));
|
||||
out.AddString(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Hex)
|
||||
{
|
||||
if (n_src==2)
|
||||
{
|
||||
T_CHAR tmp[16];
|
||||
T_CHAR tmp1[16];
|
||||
sprintf(tmp1,_TX("%%0%ux"),t_atoi(src[1]));
|
||||
sprintf(tmp,tmp1,t_atoi(src[0]));
|
||||
out.AddString(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(StrChr)
|
||||
{
|
||||
if (n_src==2)
|
||||
{
|
||||
T_CHAR * p=src[0];
|
||||
T_CHAR s=src[1][0];
|
||||
while(*p && *p!=s) p++;
|
||||
if (*p==s) out.AddInt(1+p-src[0]);
|
||||
else out.AddChar('0');
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(StrRChr)
|
||||
{
|
||||
if (n_src==2)
|
||||
{
|
||||
T_CHAR * p=src[0],*p1=0;
|
||||
T_CHAR s=src[1][0];
|
||||
while(*p)
|
||||
{
|
||||
if (*p==s) p1=p;
|
||||
p++;
|
||||
}
|
||||
if (p1) out.AddInt(1+p1-src[0]);
|
||||
else out.AddChar('0');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(StrStr)
|
||||
{
|
||||
if (n_src==2)
|
||||
{
|
||||
T_CHAR * p=t_strstr(src[0],src[1]);
|
||||
if (p) out.AddInt(1+p-src[0]);
|
||||
else out.AddChar('0');
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(SubStr)
|
||||
{
|
||||
int n1,n2;
|
||||
if (n_src<2) return;
|
||||
n1=t_atoi(src[1]);
|
||||
if (n_src>=3)
|
||||
{
|
||||
n2=t_atoi(src[2]);
|
||||
}
|
||||
else n2=n1;
|
||||
if (n1<1) n1=1;
|
||||
if (n2>=n1)
|
||||
{
|
||||
n1--;
|
||||
n2--;
|
||||
while(n1<=n2 && src[0][n1])
|
||||
{
|
||||
out.AddChar(src[0][n1++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Len)
|
||||
{
|
||||
if (n_src>=1) out.AddInt(t_strlen(src[0]));
|
||||
}
|
||||
|
||||
MAKEFUNC(FileName)
|
||||
{
|
||||
if (n_src<1) return;
|
||||
T_CHAR * p=src[0];
|
||||
T_CHAR * p1=0;
|
||||
while(*p)
|
||||
{
|
||||
if (*p=='\\' || *p=='/') p1=p;
|
||||
p++;
|
||||
}
|
||||
if (p1) p1++;
|
||||
else p1=p;
|
||||
while(*p1 && *p1!='.') out.AddChar(*(p1++));
|
||||
}
|
||||
|
||||
MAKEFUNC(Add)
|
||||
{
|
||||
UINT n;
|
||||
int s=0;
|
||||
for(n=0;n<n_src;n++)
|
||||
{
|
||||
s+=t_atoi(src[n]);
|
||||
}
|
||||
out.AddInt(s);
|
||||
}
|
||||
|
||||
MAKEFUNC(Sub)
|
||||
{
|
||||
if (n_src>=1)
|
||||
{
|
||||
UINT n;
|
||||
int s=t_atoi(src[0]);
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
s-=t_atoi(src[n]);
|
||||
}
|
||||
out.AddInt(s);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Mul)
|
||||
{
|
||||
UINT n;
|
||||
int s=1;
|
||||
for(n=0;n<n_src;n++)
|
||||
{
|
||||
s*=t_atoi(src[n]);
|
||||
}
|
||||
out.AddInt(s);
|
||||
}
|
||||
|
||||
MAKEFUNC(Div)
|
||||
{
|
||||
if (n_src>=1)
|
||||
{
|
||||
UINT n;
|
||||
int s=t_atoi(src[0]);
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t) s/=t;
|
||||
else t=0;
|
||||
}
|
||||
out.AddInt(s);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Mod)
|
||||
{
|
||||
if (n_src>=1)
|
||||
{
|
||||
UINT n;
|
||||
int s=t_atoi(src[0]);
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t) s%=t;
|
||||
else t=0;
|
||||
}
|
||||
out.AddInt(s);
|
||||
}
|
||||
}
|
||||
|
||||
MAKEFUNC(Max)
|
||||
{
|
||||
if (!n_src) return;
|
||||
int m=t_atoi(src[0]);
|
||||
UINT n;
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t>m) m=t;
|
||||
}
|
||||
out.AddInt(m);
|
||||
}
|
||||
|
||||
MAKEFUNC(Min)
|
||||
{
|
||||
if (!n_src) return;
|
||||
int m=t_atoi(src[0]);
|
||||
UINT n;
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t<m) m=t;
|
||||
}
|
||||
out.AddInt(m);
|
||||
}
|
||||
|
||||
MAKEFUNC(Null)
|
||||
{
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
TEXTFUNC func;
|
||||
const T_CHAR * name;
|
||||
} FUNCS[]={
|
||||
// Blah,"blah",
|
||||
// Nop,"nop",
|
||||
If,_TX("if"),
|
||||
Upper,_TX("upper"),
|
||||
Lower,_TX("lower"),
|
||||
Pad,_TX("pad"),
|
||||
Cut,_TX("cut"),
|
||||
PadCut,_TX("padcut"),
|
||||
Abbr,_TX("abbr"),
|
||||
Caps,_TX("caps"),
|
||||
Caps2,_TX("caps2"),
|
||||
Longest,_TX("longest"),
|
||||
Shortest,_TX("shortest"),
|
||||
Iflonger,_TX("iflonger"),
|
||||
Ifgreater,_TX("ifgreater"),
|
||||
Num,_TX("num"),Num,_TX("dec"),
|
||||
Hex,_TX("hex"),
|
||||
StrChr,_TX("strchr"),
|
||||
StrChr,_TX("strlchr"),
|
||||
StrRChr,_TX("strrchr"),
|
||||
StrStr,_TX("strstr"),
|
||||
SubStr,_TX("substr"),
|
||||
Len,_TX("len"),
|
||||
Add,_TX("add"),
|
||||
Sub,_TX("sub"),
|
||||
Mul,_TX("mul"),
|
||||
Div,_TX("div"),
|
||||
Mod,_TX("mod"),
|
||||
FileName,_TX("filename"),
|
||||
Min,_TX("min"),
|
||||
Max,_TX("max"),
|
||||
Get,_TX("get"),
|
||||
Put,_TX("put"),
|
||||
PutQ,_TX("puts"),
|
||||
Null,_TX("null"),
|
||||
};
|
||||
|
||||
|
||||
class FMT
|
||||
{
|
||||
private:
|
||||
String str;
|
||||
VarList *vars;
|
||||
T_CHAR * spec;
|
||||
TAGFUNC f;
|
||||
TAGFREEFUNC ff;
|
||||
void * fp;
|
||||
T_CHAR * org_spec;
|
||||
int found;
|
||||
|
||||
void Error(T_CHAR *e=0) {str.Reset();str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]"));}
|
||||
|
||||
T_CHAR * _FMT(T_CHAR * s,UINT *f=0)
|
||||
{
|
||||
FMT fmt(this,s);
|
||||
T_CHAR * c=(T_CHAR*)fmt;
|
||||
if (f) *f=fmt.found;
|
||||
found+=fmt.found;
|
||||
return c;
|
||||
}
|
||||
|
||||
static bool skipshit(T_CHAR** _p,T_CHAR *bl)
|
||||
{
|
||||
T_CHAR * p=*_p;
|
||||
int bc1=0,bc2=0;
|
||||
while(*p)
|
||||
{
|
||||
if (!bc1 && !bc2 && bl)
|
||||
{
|
||||
T_CHAR *z=bl;
|
||||
while(*z)
|
||||
{
|
||||
if (*z==*p) break;
|
||||
z++;
|
||||
}
|
||||
if (*z) break;
|
||||
}
|
||||
if (*p=='\'')
|
||||
{
|
||||
p++;
|
||||
while(*p && *p!='\'') p++;
|
||||
if (!*p) return 0;
|
||||
}
|
||||
else if (*p=='(') bc1++;
|
||||
else if (*p==')')
|
||||
{
|
||||
if (--bc1<0) return 0;
|
||||
}
|
||||
else if (*p=='[') bc2++;
|
||||
else if (*p==']')
|
||||
{
|
||||
if (--bc2<0) return 0;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
*_p=p;
|
||||
return *p && !bc1 && !bc2;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (!spec) {Error();return;}
|
||||
while(*spec)
|
||||
{
|
||||
if (*spec=='%')
|
||||
{
|
||||
spec++;
|
||||
if (*spec=='%') {str.AddChar('%');spec++;continue;}
|
||||
T_CHAR* s1=spec+1;
|
||||
while(*s1 && *s1!='%') s1++;
|
||||
if (!*s1) {Error();break;}
|
||||
*s1=0;
|
||||
T_CHAR * tag=f(spec,fp);
|
||||
*s1='%';
|
||||
//if (!tag) tag=tag_unknown;
|
||||
if (tag && tag[0])
|
||||
{
|
||||
found++;
|
||||
str.AddString(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
str.AddString(_TX("?"));
|
||||
}
|
||||
if (tag && ff) ff(tag,fp);
|
||||
spec=s1+1;
|
||||
}
|
||||
else if (*spec=='$')
|
||||
{
|
||||
spec++;
|
||||
if (*spec=='$') {str.AddChar('$');spec++;continue;}
|
||||
T_CHAR * s1=spec+1;
|
||||
while(*s1 && *s1!='(') s1++;
|
||||
if (!*s1) {Error();break;}
|
||||
T_CHAR * s2=s1+1;
|
||||
if (!skipshit(&s2,_TX(")"))) {Error();break;}
|
||||
if (!*s2) {Error();break;};
|
||||
T_CHAR * p=s1+1;
|
||||
T_CHAR* temp[64];
|
||||
UINT temp_f[64];
|
||||
UINT nt=0;
|
||||
T_CHAR * p1=s1+1;
|
||||
while(p<=s2 && nt<64)
|
||||
{
|
||||
if (!skipshit(&p,_TX(",)"))) {Error();return;}
|
||||
if (p>s2 || (*p!=',' && *p!=')')) {Error(_TX("internal error"));return;}
|
||||
T_CHAR bk=*p;
|
||||
*p=0;
|
||||
temp[nt]=_FMT(p1,&temp_f[nt]);
|
||||
nt++;
|
||||
*p=bk;;
|
||||
p1=p+1;
|
||||
p++;
|
||||
}
|
||||
*s1=0;
|
||||
UINT n;
|
||||
TEXTFUNC fn=0;
|
||||
for(n=0;n<TABSIZE(FUNCS);n++)
|
||||
{
|
||||
if (!t_stricmp(spec,FUNCS[n].name)) {fn=FUNCS[n].func;break;}
|
||||
}
|
||||
*s1='(';
|
||||
if (fn)
|
||||
{
|
||||
fn(nt,temp,temp_f,str,*vars);
|
||||
}
|
||||
else
|
||||
{
|
||||
str.AddString(_TX("[UNKNOWN FUNCTION]"));
|
||||
}
|
||||
for(n=0;n<nt;n++) free(temp[n]);
|
||||
spec=s2+1;
|
||||
}
|
||||
else if (*spec=='\'')
|
||||
{
|
||||
spec++;
|
||||
if (*spec=='\'') {str.AddChar('\'');spec++;continue;}
|
||||
T_CHAR * s1=spec+1;
|
||||
while(*s1 && *s1!='\'') s1++;
|
||||
if (!*s1) {Error();break;}
|
||||
*s1=0;
|
||||
str.AddString(spec);
|
||||
*s1='\'';
|
||||
spec=s1+1;
|
||||
}
|
||||
else if (*spec=='[')
|
||||
{
|
||||
spec++;
|
||||
T_CHAR * s1=spec;
|
||||
if (!skipshit(&s1,_TX("]"))) {Error();break;}
|
||||
T_CHAR bk=*s1;
|
||||
*s1=0;
|
||||
FMT fmt(this,spec);
|
||||
fmt.run();
|
||||
if (fmt.found)
|
||||
{
|
||||
str.AddString(fmt);
|
||||
found+=fmt.found;
|
||||
}
|
||||
*s1=bk;
|
||||
spec=s1+1;
|
||||
}
|
||||
else if (*spec==']' || *spec=='(' || *spec==')') {Error();break;}
|
||||
else
|
||||
{
|
||||
str.AddChar(*spec);
|
||||
spec++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FMT(FMT* base,T_CHAR * _spec)
|
||||
{
|
||||
vars=base->vars;
|
||||
found=0;
|
||||
org_spec=0;
|
||||
f=base->f;
|
||||
ff=base->ff;
|
||||
fp=base->fp;
|
||||
spec=_spec;
|
||||
}
|
||||
public:
|
||||
FMT(T_CHAR * p_spec,TAGFUNC _f,TAGFREEFUNC _ff,void * _fp,VarList * _vars)
|
||||
{
|
||||
vars=_vars;
|
||||
found=0;
|
||||
org_spec=spec=t_strdup(p_spec);
|
||||
f=_f;
|
||||
ff=_ff;
|
||||
fp=_fp;
|
||||
}
|
||||
operator T_CHAR*()
|
||||
{
|
||||
run();
|
||||
return str.GetBuf();
|
||||
}
|
||||
~FMT()
|
||||
{
|
||||
if (org_spec) free(org_spec);
|
||||
}
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
UINT tagz_format(T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void *fp,T_CHAR* out,UINT max)
|
||||
{
|
||||
T_CHAR * zz=tagz_format_r(spec,f,ff,fp);
|
||||
UINT r=0;
|
||||
while(r<max-1 && zz[r])
|
||||
{
|
||||
out[r]=zz[r];
|
||||
r++;
|
||||
}
|
||||
out[r]=0;
|
||||
free(zz);
|
||||
return r;
|
||||
}
|
||||
|
||||
T_CHAR * tagz_format_r(T_CHAR* spec,TAGFUNC f,TAGFREEFUNC ff,void * fp)
|
||||
{
|
||||
VarList vars;
|
||||
return FMT(spec,f,ff,fp,&vars);
|
||||
}
|
||||
|
||||
//char tagz_manual[]="TODO: WTFM";
|
||||
|
||||
char tagz_manual[]="Syntax reference: \n"
|
||||
"\n"
|
||||
"* %tagname% - inserts field named <tagname>, eg. \"%artist%\"\n"
|
||||
"* $abbr(x) - inserts abbreviation of x, eg. \"$abbr(%album%)\" - will convert album name of \"Final Fantasy VI\" to \"FFVI\"\n"
|
||||
"* $abbr(x,y) - inserts abbreviation of x if x is longer than y characters; otherwise inserts full value of x, eg. \"$abbr(%album%,10)\"\n"
|
||||
"* $lower(x), $upper(x) - converts x to in lower/uppercase, eg. \"$upper(%title%)\"\n"
|
||||
"* $num(x,y) - displays x number and pads with zeros up to y characters (useful for track numbers), eg. $num(%tracknumber%,2)\n"
|
||||
"* $caps(x) - converts first letter in every word of x to uppercase, and all other letters to lowercase, eg. \"blah BLAH\" -> \"Blah Blah\"\n"
|
||||
"* $caps2(x) - similar to $caps, but leaves uppercase letters as they are, eg. \"blah BLAH\" -> \"Blah BLAH\"\n"
|
||||
"* $if(A,B,C) - if A contains at least one valid tag, displays B, otherwise displays C; eg. \"$if(%artist%,%artist%,unknown artist)\" will display artist name if present; otherwise will display \"unknown artist\"; note that \"$if(A,A,)\" is equivalent to \"[A]\" (see below)\n"
|
||||
"* $longest(A,B,C,....) - compares lengths of output strings produced by A,B,C... and displays the longest one, eg. \"$longest(%title%,%comment%)\" will display either title if it's longer than comment; otherwise it will display comment\n"
|
||||
"* $pad(x,y) - pads x with spaces up to y characters\n"
|
||||
"* $cut(x,y) - truncates x to y characters\n"
|
||||
"* $padcut(x,y) - pads x to y characters and truncates to y if longer\n"
|
||||
"* [ .... ] - displays contents of brackets only if at least one of fields referenced inside has been found, eg. \"%artist% - [%album% / ]%title%\" will hide [] block if album field is not present\n"
|
||||
"* \' (single quotation mark) - outputs raw text without parsing, eg, \'blah$blah%blah[][]\' will output the contained string and ignore all reserved characters (%,$,[,]) in it; you can use this feature to insert square brackets for an example.\n"
|
||||
"\n"
|
||||
"eg. \"[%artist% - ][$abbr(%album%,10)[ %tracknumber%] / ]%title%[ %streamtitle%]\"\n";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef UINT
|
||||
typedef unsigned int UINT;
|
||||
#endif
|
||||
|
||||
#ifdef TAGZ_UNICODE
|
||||
typedef unsigned short T_CHAR;
|
||||
#else
|
||||
#define T_CHAR char
|
||||
#endif
|
||||
|
||||
typedef T_CHAR* (*TAGFUNC)(T_CHAR * tag,void * p); //return 0 if not found
|
||||
typedef void (*TAGFREEFUNC)(T_CHAR * tag,void * p);
|
||||
|
||||
|
||||
UINT tagz_format(T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void *fp,T_CHAR * out,UINT max);
|
||||
T_CHAR * tagz_format_r(T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void * fp);
|
||||
|
||||
extern char tagz_manual[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "main.h"
|
||||
|
||||
TITLELISTTYPE TitleListTerminator;
|
||||
TITLELISTTYPE *TitleLinkedList = &TitleListTerminator;
|
||||
|
||||
void initTitleList(void)
|
||||
{
|
||||
TitleListTerminator.Next = NULL;
|
||||
TitleListTerminator.timer = 0;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------------------------
|
||||
Adds an entry in the list
|
||||
-----------------------------------------------------------------------------------------------*/
|
||||
TITLELISTTYPE *newTitleListEntry(void)
|
||||
{
|
||||
TITLELISTTYPE *TitleObject = (TITLELISTTYPE *)calloc(1,sizeof(TITLELISTTYPE)); /* Allocate new entry */
|
||||
TitleObject->Next = (void *)TitleLinkedList; /* New entry's next is old list _head */
|
||||
TitleLinkedList = TitleObject; /* new _head is new entry */
|
||||
return TitleObject; /* return pointer to new entry */
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------------------------
|
||||
Removes an entry from the list
|
||||
-----------------------------------------------------------------------------------------------*/
|
||||
void removeTitleListEntry(TITLELISTTYPE *Entry)
|
||||
{
|
||||
TITLELISTTYPE *TitleObject = TitleLinkedList;
|
||||
|
||||
if (TitleObject == &TitleListTerminator) return; /* List is empty */
|
||||
|
||||
if (TitleObject == (void *)Entry)
|
||||
{
|
||||
TitleLinkedList = (TITLELISTTYPE *)TitleObject->Next;
|
||||
free(TitleObject);
|
||||
}
|
||||
else
|
||||
while (TitleObject->Next) /* While not terminator */
|
||||
{
|
||||
if ((TITLELISTTYPE *)(TitleObject->Next) == (void *)Entry) /* If next entry is what we're looking for */
|
||||
{
|
||||
TitleObject->Next = ((TITLELISTTYPE *)(TitleObject->Next))->Next; /* Skip one entry */
|
||||
free(Entry); /* free the entry we dont' want anymore */
|
||||
}
|
||||
TitleObject = (TITLELISTTYPE *)TitleObject->Next; /* Get next entry */
|
||||
}
|
||||
}
|
||||
|
||||
void clearTitleList()
|
||||
{
|
||||
TITLELISTTYPE *TitleObject = TitleLinkedList;
|
||||
if (TitleObject == &TitleListTerminator) return; /* List is empty */
|
||||
|
||||
while (TitleObject->Next) /* While not terminator */
|
||||
{
|
||||
TITLELISTTYPE *KillMe=TitleObject;
|
||||
TitleObject = (TITLELISTTYPE *)KillMe->Next; /* Get next entry */
|
||||
free(KillMe);
|
||||
}
|
||||
TitleLinkedList = &TitleListTerminator;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
Changes:
|
||||
* [a7] made seeking work (slightly) better on realshitbox encoded mp3s (with broken
|
||||
VBR headers)
|
||||
* [a7] made save http file location persistent when turned off
|
||||
* [a7] fixed id3v2 bug (1 character strings not being displayed correctly)
|
||||
* [a6] fixed stupid file association bug (oops)
|
||||
* [a5] fixed crash when repeating a non-existing file bug (added a Sleep())
|
||||
* [a5] fixed shoutcast disk writer issue
|
||||
* [a5] fixed SendMessage() potential issues (using postmessage and SendMessageTimeout() now)
|
||||
* [a5] added new format-for-non-id3 files, added 'use id3 tag' option, which lets you disable them
|
||||
completely
|
||||
* [a5] return of the file association list
|
||||
* [a5] made temp file handling slightly better-- checks for read only, better error messages.
|
||||
* [a4] fixed lots of potential (and a few serious) bugs in id3lib.
|
||||
* [a4] fixed pause right after playback starts bug
|
||||
* fixed crash/hang/freeze when reading some mp3 files with a weird id3v2 tag (as found in some
|
||||
real jukebox generated mp3s, etc...)
|
||||
* this one will break a few things (i.e. windowshade vis), because justin is updating it to go
|
||||
with winamp 2.7
|
||||
* all code is now win32 file io
|
||||
* %a will now display id3v1.1/v2 track #
|
||||
* fixed id3v1 reading bug that added year field in album field
|
||||
* why does it ask me to stop the currently playing file when updating an id3v2 tag ?
|
||||
answer:
|
||||
whenever you strip or update an id3v2 tag, it creates a tmp file (FILENAME.new),
|
||||
writes it out, and if it wrote it out correctly, then it renames the original
|
||||
to FILENAME.bak, and renames the new one to FILENAME, and if that was successful,
|
||||
then deletes FILENAME.bak). this is required because of the implementation of the
|
||||
id3v2 protocol.
|
||||
|
||||
* [2.666b] fixed crash when using crossfading output plugin
|
||||
* [2.666b] fixed the issue that files with large id3v2 tags don't seek correctly
|
||||
* [2.666b] added id3v1.1 track field editing
|
||||
* [2.666b] simplified id3 edit box (removed all save/remove buttons, all is done via
|
||||
update button now)
|
||||
* [2.666b] fixed some more stuff in id3 edit box... should be more reliable
|
||||
* [2.666b] contains devil easter egg
|
||||
|
||||
* winamp 2.666 release
|
||||
|
||||
* [a18] dll is smaller
|
||||
* [a18] fixed vbr header reading on some musicmatch/crap generated files
|
||||
* [a18] id3v1.1 track # reading support (who cares about id3v1.1 writing?)
|
||||
* [a18] crc checking is now activable in prefs box
|
||||
* [a18] "show average on VBR files" is now activable in prefs box
|
||||
* [a17] "update tags" button only saves selected tags now
|
||||
* [a17] vbr-division-by-0-bug-on-edit fixed
|
||||
* [a17] long id3v1 tags reading corrected
|
||||
* [a17] id3v2 url tag will now interact with the minibrowser
|
||||
* [a17] added id3v2 variables to id3 title formatting
|
||||
* [a16] corrected crash/bug in id3v2 genre reading
|
||||
* [a16] corrected id3v2 comment editor to support multiple lines :)
|
||||
* [a16] new "stop track" button in id3v2 editor so you don't have to retype everything
|
||||
when id3v2 can't be updated because file is locked
|
||||
* [a16] added track number id3v2 field
|
||||
* [a16] id3v2 warnings no more appear under id3 tag editor
|
||||
* [a10] streaming info improvements/fixes
|
||||
* [a10] made more options for streaming title formatting (for you brennan)
|
||||
* [a10] still needs better id3v2 reading writing. THIS IS ON THE WAY, CHILL.
|
||||
* [a9] improved streaming error notification (i.e. on can't connect, can't resolve, timeout)
|
||||
* [a9] made streaming detect id3v2 tag and skip it (todo: make it look at the id3v2 tag and use it)
|
||||
* [a9] updated id3v2 support to detect invalid id3v2 tags, and autodetect their actual
|
||||
size
|
||||
* [a9] info box now tells you where the first mpeg header was found (useful)
|
||||
* [a8] fixed live365 streaming (they need a space between User-Agent:
|
||||
and the agent string. those assclowns.)
|
||||
* [a8] rescheduled some of the polyphase for a few cycles
|
||||
* [a7] bugfix: vbr headers read when id3v2 tag is present now
|
||||
* [a7] downsampling modes have better vis support
|
||||
* [a7] id3v2 writing support
|
||||
* [a7] stream info box
|
||||
* [a6] mmm.
|
||||
* [a6] return of working id3 code
|
||||
* [a5] optimized bitgetting.
|
||||
* [a5] keen streaming buffer indicators in mini-vis display
|
||||
* [a5] made fast eq modes optional (can use slow pcm eq like wav files)
|
||||
* [a5] fixed fastly-changing-tracks bug
|
||||
* [a4] tuned decode loop more
|
||||
* [a4] optimized huffman decoding
|
||||
* [a3] improved network code. updates status in title area.
|
||||
* [a3] layer 1/2 eq code
|
||||
* [a3] optimized decoder some more. we can still make it a bit faster me thinks.
|
||||
* [a3] moved more code into decode thread.. should act much more asynchronously
|
||||
* [a2] Improved skip robustness
|
||||
* [a2] Optimized decoder for ppro. changed 8 bit mode for speed.
|
||||
* [a2] partial ID3V2 support
|
||||
* Fully ISO compliant decoder (based on FHG's implementation)
|
||||
* Also fully supports MPEG 2.5 low bitrates.
|
||||
* Full MPEG Layer 1 and Layer 2 support
|
||||
* Improved equalizer code
|
||||
* Optimized visualization data generation code
|
||||
* Improved network code (single threaded)
|
||||
* Lots of other cleanups
|
||||
|
||||
todo:
|
||||
make more blip resistant (see pvd.mp3)
|
||||
remove seek-blip
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "uvox_3901.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "api/service/waservicefactory.h"
|
||||
#include <strsafe.h>
|
||||
#include "in2.h"
|
||||
extern In_Module mod;
|
||||
|
||||
#include "FactoryHelper.h"
|
||||
|
||||
|
||||
Ultravox3901::Ultravox3901() : parser(0)
|
||||
{
|
||||
title[0]=album[0]=artist[0]=album_art_url[0]=0;
|
||||
ServiceBuild(parser, obj_xmlGUID);
|
||||
if (parser)
|
||||
{
|
||||
parser->xmlreader_registerCallback(L"metadata\fsong\f*", this);
|
||||
parser->xmlreader_open();
|
||||
}
|
||||
}
|
||||
|
||||
Ultravox3901::~Ultravox3901()
|
||||
{
|
||||
ServiceRelease(parser, obj_xmlGUID);
|
||||
}
|
||||
|
||||
int Ultravox3901::Parse(const char *xml_data)
|
||||
{
|
||||
if (parser)
|
||||
{
|
||||
int ret = parser->xmlreader_feed((void *)xml_data, strlen(xml_data));
|
||||
if (ret != API_XML_SUCCESS)
|
||||
return ret;
|
||||
return parser->xmlreader_feed(0, 0);
|
||||
}
|
||||
return API_XML_FAILURE;
|
||||
}
|
||||
|
||||
void Ultravox3901::TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str)
|
||||
{
|
||||
if (!_wcsicmp(xmltag, L"name"))
|
||||
{
|
||||
StringCbCatW(title, sizeof(title), str);
|
||||
}
|
||||
else if (!_wcsicmp(xmltag, L"album"))
|
||||
{
|
||||
StringCbCatW(album, sizeof(album), str);
|
||||
}
|
||||
else if (!_wcsicmp(xmltag, L"artist"))
|
||||
{
|
||||
StringCbCatW(artist, sizeof(artist), str);
|
||||
}
|
||||
else if (!_wcsicmp(xmltag, L"album_art"))
|
||||
{
|
||||
StringCbCatW(album_art_url, sizeof(album_art_url), str);
|
||||
}
|
||||
}
|
||||
|
||||
int Ultravox3901::GetExtendedData(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!_stricmp(tag, "uvox/title"))
|
||||
StringCchCopy(data, dataLen, title);
|
||||
else if (!_stricmp(tag, "uvox/album"))
|
||||
StringCchCopy(data, dataLen, album);
|
||||
else if (!_stricmp(tag, "uvox/artist"))
|
||||
StringCchCopy(data, dataLen, artist);
|
||||
else if (!_stricmp(tag, "uvox/albumart"))
|
||||
StringCchCopy(data, dataLen, album_art_url);
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define CBCLASS Ultravox3901
|
||||
START_DISPATCH;
|
||||
VCB(ONCHARDATA, TextHandler)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "../xml/obj_xml.h"
|
||||
#include "../xml/ifc_xmlreadercallback.h"
|
||||
|
||||
|
||||
class Ultravox3901 : public ifc_xmlreadercallback
|
||||
{
|
||||
public:
|
||||
Ultravox3901();
|
||||
~Ultravox3901();
|
||||
int Parse(const char *xml_data);
|
||||
int GetExtendedData(const char *tag, wchar_t *data, int dataLen);
|
||||
private:
|
||||
/* XML callbacks */
|
||||
void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str);
|
||||
|
||||
obj_xml *parser;
|
||||
wchar_t title[256],artist[256],album[256],album_art_url[4096];
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "uvox_3902.h"
|
||||
#include "api__in_mp3.h"
|
||||
#include "api/service/waservicefactory.h"
|
||||
#include <strsafe.h>
|
||||
#include "in2.h"
|
||||
extern In_Module mod;
|
||||
|
||||
#include "FactoryHelper.h"
|
||||
|
||||
Ultravox3902::Ultravox3902() : parser(0)
|
||||
{
|
||||
title[0]=album[0]=artist[0]=0;
|
||||
ServiceBuild(parser, obj_xmlGUID);
|
||||
if (parser)
|
||||
{
|
||||
parser->xmlreader_setCaseSensitive();
|
||||
parser->xmlreader_registerCallback(L"metadata\f*", this);
|
||||
parser->xmlreader_open();
|
||||
}
|
||||
}
|
||||
|
||||
Ultravox3902::~Ultravox3902()
|
||||
{
|
||||
if (parser)
|
||||
{
|
||||
parser->xmlreader_unregisterCallback(this);
|
||||
parser->xmlreader_close();
|
||||
}
|
||||
ServiceRelease(parser, obj_xmlGUID);
|
||||
}
|
||||
|
||||
int Ultravox3902::Parse(const char *xml_data)
|
||||
{
|
||||
if (parser)
|
||||
{
|
||||
int ret = parser->xmlreader_feed((void *)xml_data, strlen(xml_data));
|
||||
if (ret != API_XML_SUCCESS)
|
||||
return ret;
|
||||
return parser->xmlreader_feed(0, 0);
|
||||
}
|
||||
return API_XML_FAILURE;
|
||||
}
|
||||
|
||||
void Ultravox3902::TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str)
|
||||
{
|
||||
if (!_wcsicmp(xmlpath, L"metadata\fTIT2"))
|
||||
{
|
||||
StringCbCatW(title, sizeof(title), str);
|
||||
}
|
||||
else if (!_wcsicmp(xmlpath, L"metadata\fTALB"))
|
||||
{
|
||||
StringCbCatW(album, sizeof(album), str);
|
||||
}
|
||||
else if (!_wcsicmp(xmlpath, L"metadata\fTPE1"))
|
||||
{
|
||||
StringCbCatW(artist, sizeof(artist), str);
|
||||
}
|
||||
}
|
||||
|
||||
int Ultravox3902::GetExtendedData(const char *tag, wchar_t *data, int dataLen)
|
||||
{
|
||||
if (!_stricmp(tag, "title"))
|
||||
StringCchCopy(data, dataLen, title);
|
||||
else if (!_stricmp(tag, "album"))
|
||||
StringCchCopy(data, dataLen, album);
|
||||
else if (!_stricmp(tag, "artist"))
|
||||
StringCchCopy(data, dataLen, artist);
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define CBCLASS Ultravox3902
|
||||
START_DISPATCH;
|
||||
VCB(ONCHARDATA, TextHandler)
|
||||
END_DISPATCH;
|
||||
#undef CBCLASS
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "../xml/obj_xml.h"
|
||||
#include "../xml/ifc_xmlreadercallback.h"
|
||||
|
||||
|
||||
class Ultravox3902 : public ifc_xmlreadercallback
|
||||
{
|
||||
public:
|
||||
Ultravox3902();
|
||||
~Ultravox3902();
|
||||
int Parse(const char *xml_data);
|
||||
int GetExtendedData(const char *tag, wchar_t *data, int dataLen);
|
||||
private:
|
||||
/* XML callbacks */
|
||||
void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str);
|
||||
|
||||
obj_xml *parser;
|
||||
wchar_t title[256],artist[256],album[256];
|
||||
RECVS_DISPATCH;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
#include "../../../Winamp/buildType.h"
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,6,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", "4,6,0,0"
|
||||
VALUE "InternalName", "Nullsoft MPEG Audio Decoder"
|
||||
VALUE "LegalCopyright", "Copyright © 1998-2023 Winamp SA"
|
||||
VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
|
||||
VALUE "OriginalFilename", "in_mp3.dll"
|
||||
VALUE "ProductName", "Winamp"
|
||||
VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||