396 lines
8.7 KiB
C++
396 lines
8.7 KiB
C++
#include "../studio/services/svc_mediaconverter.h"
|
|
#include "../studio/wac.h"
|
|
#include "../common/rootcomp.h"
|
|
#include "../studio/services/svc_action.h"
|
|
#include "../unpack/unpack_helper.h"
|
|
#include "main.h"
|
|
|
|
#define WACNAME WACcnv_midi
|
|
|
|
class WACNAME : public WAComponentClient{
|
|
public:
|
|
WACNAME();
|
|
virtual ~WACNAME();
|
|
|
|
virtual const char *getName() { return NAME; };
|
|
virtual GUID getGUID();
|
|
|
|
virtual void onCreate();
|
|
virtual void onDestroy();
|
|
|
|
virtual int getDisplayComponent() { return FALSE; };
|
|
|
|
virtual CfgItem *getCfgInterface(int n) { return this; }
|
|
|
|
private:
|
|
};
|
|
|
|
|
|
static WACNAME wac;
|
|
WAComponentClient *the = &wac;
|
|
|
|
|
|
// {28FDCD38-26A2-482c-A691-55901A355D9E}
|
|
static const GUID guid =
|
|
{ 0x28fdcd38, 0x26a2, 0x482c, { 0xa6, 0x91, 0x55, 0x90, 0x1a, 0x35, 0x5d, 0x9e } };
|
|
|
|
GUID WACNAME::getGUID() {
|
|
return guid;
|
|
}
|
|
|
|
static void update_extensions()
|
|
{
|
|
static int old_mask;
|
|
int new_mask = cfg_ext_mask;
|
|
int n;
|
|
for(n=0;n<MIDI_core::FileTypes_GetNum();n++)
|
|
{
|
|
int bit = 1<<n;
|
|
if ( (new_mask & bit) && !(old_mask & bit) )
|
|
api->core_registerExtension(StringPrintf("*.%s",MIDI_core::FileTypes_GetExtension(n)),MIDI_core::FileTypes_GetDescription(n),"Audio");
|
|
else if ( !(new_mask & bit) && (old_mask & bit) )
|
|
{
|
|
api->core_unregisterExtension(StringPrintf("*.%s",MIDI_core::FileTypes_GetExtension(n)));
|
|
}
|
|
}
|
|
old_mask = new_mask;
|
|
}
|
|
|
|
void WACNAME::onCreate()
|
|
{
|
|
// {EDAA0599-3E43-4eb5-A65D-C0A0484240E7}
|
|
static const GUID cfg_audio_guid =
|
|
{ 0xedaa0599, 0x3e43, 0x4eb5, { 0xa6, 0x5d, 0xc0, 0xa0, 0x48, 0x42, 0x40, 0xe7 } };
|
|
|
|
registerSkinFile("xml/midi-prefs.xml");
|
|
|
|
api->preferences_registerGroup("winamp.preferences.midi", "MIDI playback", guid, cfg_audio_guid);
|
|
|
|
MIDI_core::GlobalInit();
|
|
|
|
|
|
update_extensions();
|
|
}
|
|
|
|
void WACNAME::onDestroy() {
|
|
MIDI_core::GlobalQuit();
|
|
}
|
|
|
|
static void check_messages()
|
|
{
|
|
MSG msg;
|
|
while(PeekMessage(&msg,0,0,0,PM_REMOVE))
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
//note: multiinstance support is NOT working, and will never be; it makes no sense anyway. also, multiinstance safety was totally fuct in directmusic drivers last time i bothered trying.
|
|
|
|
class cnv_MIDI : public svc_mediaConverterI {
|
|
private:
|
|
static critical_section core_owner_sync;
|
|
static cnv_MIDI * core_owner;
|
|
|
|
DWORD thread_id;
|
|
|
|
MemBlock<char> sample_buffer;
|
|
|
|
MIDI_file * file;
|
|
|
|
int is_open;
|
|
int eof_flag;
|
|
|
|
void core_reset()
|
|
{
|
|
core_owner_sync.enter();
|
|
if (core_owner==this) {core_owner=0;MIDI_core::Close();}
|
|
core_owner_sync.leave();
|
|
if (file) {file->Free();file=0;}
|
|
is_open=0;
|
|
eof_flag=0;
|
|
}
|
|
|
|
int core_takeover()
|
|
{
|
|
core_owner_sync.enter();
|
|
if (core_owner!=this)
|
|
{
|
|
if (core_owner!=0) {core_owner_sync.leave();return 0;}
|
|
core_owner=this;
|
|
thread_id = GetCurrentThreadId();
|
|
MIDI_core::Init();
|
|
}
|
|
core_owner_sync.leave();
|
|
return 1;
|
|
}
|
|
|
|
int check_file(MediaInfo * infos)
|
|
{
|
|
if (file && !STRICMP(file->path,infos->getFilename())) return 1;
|
|
core_reset();
|
|
MemBlock<char> data;
|
|
int size;
|
|
|
|
try {
|
|
svc_fileReader * reader = infos->getReader();
|
|
if (!reader) return 0;
|
|
size = reader->getLength();
|
|
if (size<=0) return 0;
|
|
reader->seek(0);
|
|
int firstread = size > 256 ? 256 : size;
|
|
data.setSize(firstread);
|
|
if (reader->read(data.getMemory(),firstread)!=firstread) return 0;
|
|
if (MIDI_file::HeaderTest(data.getMemory(),size))
|
|
{
|
|
if (firstread != size)
|
|
{
|
|
if (data.setSize(size)==0) return 0;
|
|
if (reader->read(data.getMemory()+firstread,size-firstread)!=size-firstread) return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
void * unpack = unpack_helper::unpack_getHandle(reader);
|
|
if (!unpack) return 0;
|
|
size = api->fileGetFileSize(unpack);
|
|
firstread = size > 256 ? 256 : size;
|
|
data.setSize(firstread);
|
|
if (api->fileRead(data.getMemory(),firstread,unpack)!=firstread) {api->fileClose(unpack);return 0;}
|
|
if (!MIDI_file::HeaderTest(data.getMemory(),size)) {api->fileClose(unpack);return 0;}
|
|
|
|
if (firstread != size)
|
|
{
|
|
if (data.setSize(size)==0) {api->fileClose(unpack);return 0;}
|
|
if (api->fileRead(data.getMemory()+firstread,size-firstread,unpack)!=size-firstread) {api->fileClose(unpack);return 0;}
|
|
}
|
|
|
|
api->fileClose(unpack);
|
|
}
|
|
file = MIDI_file::Create(infos->getFilename(),data.getMemory(),size);
|
|
|
|
return !!file;
|
|
}
|
|
catch(...)
|
|
{
|
|
file = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
static const char *getServiceName() { return NAME; }
|
|
|
|
cnv_MIDI()
|
|
{
|
|
file=0;
|
|
is_open=0;
|
|
eof_flag=0;
|
|
thread_id=0;
|
|
}
|
|
|
|
~cnv_MIDI()
|
|
{
|
|
core_reset();
|
|
}
|
|
|
|
virtual int canConvertFrom(svc_fileReader *reader, const char *name, const char *chunktype)
|
|
{
|
|
return reader && !chunktype && name && MIDI_core::IsOurFile(name);
|
|
}
|
|
|
|
virtual const char *getConverterTo()
|
|
{
|
|
if (!core_takeover()) return "FINAL";
|
|
return MIDI_core::UsesOutput() ? "PCM" : "FINAL";
|
|
}
|
|
|
|
virtual int getInfos(MediaInfo *infos)
|
|
{
|
|
if (!check_file(infos)) return 0;
|
|
infos->setTitle(Std::filename(file->path));
|
|
infos->setLength(file->len);
|
|
|
|
infos->setInfo(
|
|
StringPrintf("%sMIDI %i channels",
|
|
file->info.e_type ? StringPrintf("%s ",file->info.e_type) : ""
|
|
,file->info.channels)
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
virtual int processData(MediaInfo *infos, ChunkList *chunk_list, bool *killswitch)
|
|
{
|
|
if (!check_file(infos)) return 0;
|
|
if (!core_takeover()) return 0;
|
|
|
|
if (!is_open)
|
|
{
|
|
MIDI_core::SetVolume(api->core_getVolume(m_coretoken));
|
|
MIDI_core::SetPan(api->core_getPan(m_coretoken));
|
|
|
|
if (!MIDI_core::OpenFile(file))
|
|
return 0;
|
|
is_open=1;
|
|
eof_flag=0;
|
|
}
|
|
|
|
check_messages();
|
|
|
|
if (!MIDI_core::HavePCM()) {Sleep(1);check_messages();return eof_flag ? 0 : 1;}
|
|
else
|
|
{
|
|
int srate,nch,bps;
|
|
int size;
|
|
MIDI_core::GetPCM(&srate,&nch,&bps);
|
|
size = 576 * nch * (bps/8);
|
|
if (sample_buffer.getSize()<size) sample_buffer.setSize(size);
|
|
size = MIDI_core::GetSamples(sample_buffer.getMemory(),size,(char*)killswitch);
|
|
if (size<=0)
|
|
return 0;
|
|
|
|
ChunkInfosI *ci=new ChunkInfosI();
|
|
ci->addInfo("srate",srate);
|
|
ci->addInfo("bps",bps);
|
|
ci->addInfo("nch",nch);
|
|
|
|
chunk_list->setChunk("PCM",sample_buffer.getMemory(),size,ci);
|
|
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
virtual int getLatency(void) { return 0; }
|
|
|
|
// callbacks
|
|
virtual int corecb_onSeeked(int newpos)
|
|
{
|
|
if (core_owner==this) MIDI_core::SetPosition(newpos);
|
|
return 0;
|
|
}
|
|
|
|
int getPosition(void)
|
|
{
|
|
if (core_owner==this && !MIDI_core::UsesOutput()) return MIDI_core::GetPosition();
|
|
return -1;
|
|
}
|
|
|
|
virtual int corecb_onVolumeChange(int v)
|
|
{
|
|
if (core_owner==this) MIDI_core::SetVolume(v);
|
|
return 0;
|
|
}
|
|
virtual int corecb_onPanChange(int v)
|
|
{
|
|
if (core_owner==this) MIDI_core::SetPan(v);
|
|
return 0;
|
|
}
|
|
virtual int corecb_onAbortCurrentSong() {return 0;};
|
|
virtual int corecb_onPaused()
|
|
{
|
|
if (core_owner==this) MIDI_core::Pause(1);
|
|
return 0;
|
|
}
|
|
virtual int corecb_onUnpaused()
|
|
{
|
|
if (core_owner==this) MIDI_core::Pause(0);
|
|
return 0;
|
|
}
|
|
|
|
static void notify_eof()
|
|
{
|
|
core_owner_sync.enter();
|
|
if (core_owner) core_owner->eof_flag=1;
|
|
core_owner_sync.leave();
|
|
}
|
|
|
|
static DWORD get_core_thread()
|
|
{
|
|
DWORD ret = 0;
|
|
core_owner_sync.enter();
|
|
if (core_owner) ret = core_owner->thread_id;
|
|
core_owner_sync.leave();
|
|
return ret;
|
|
}
|
|
|
|
};
|
|
|
|
cnv_MIDI * cnv_MIDI::core_owner=0;
|
|
critical_section cnv_MIDI::core_owner_sync;
|
|
|
|
static waServiceFactoryT<svc_mediaConverter, cnv_MIDI> midi_svc;
|
|
|
|
#define ACTIONID_CONFIG "MIDI:CONFIG"
|
|
|
|
class MIDI_actions : public svc_actionI {
|
|
public:
|
|
MIDI_actions() {
|
|
registerAction(ACTIONID_CONFIG, 0);
|
|
}
|
|
virtual ~MIDI_actions() { }
|
|
|
|
virtual int onActionId(int id, const char *action, const char *param,int,int,void*,int,RootWnd*) {
|
|
switch (id) {
|
|
case 0:
|
|
if (!_stricmp(action,ACTIONID_CONFIG))
|
|
{
|
|
if (MIDI_core::Config(MIDI_callback::GetMainWindow()))
|
|
{
|
|
update_extensions();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
static const char *getServiceName() { return "MIDI Player Actions Service"; }
|
|
};
|
|
|
|
|
|
static waServiceFactoryTSingle<svc_actionI, MIDI_actions> actions;
|
|
|
|
WACNAME::WACNAME() {
|
|
#ifdef FORTIFY
|
|
FortifySetName("cnv_midi.wac");
|
|
FortifyEnterScope();
|
|
#endif
|
|
registerService(&midi_svc);
|
|
registerService(&actions);
|
|
}
|
|
|
|
WACNAME::~WACNAME() {
|
|
#ifdef FORTIFY
|
|
FortifyLeaveScope();
|
|
#endif
|
|
}
|
|
|
|
|
|
void MIDI_callback::NotifyEOF() {cnv_MIDI::notify_eof();}
|
|
HWND MIDI_callback::GetMainWindow() {return api->main_getRootWnd()->gethWnd();}
|
|
HINSTANCE MIDI_callback::GetInstance() {return wac.gethInstance();}
|
|
void MIDI_callback::Error(const char * tx) {}
|
|
|
|
void MIDI_callback::Idle(int ms)
|
|
{
|
|
int core_thread = (GetCurrentThreadId() == cnv_MIDI::get_core_thread());
|
|
int start = timeGetTime();
|
|
do {
|
|
if (core_thread) check_messages();
|
|
Sleep(1);
|
|
} while((int)timeGetTime() - start < ms);
|
|
if (core_thread) check_messages();
|
|
}
|
|
|
|
extern "C" {
|
|
BOOL APIENTRY DllMain(HANDLE hMod,DWORD r,void*)
|
|
{
|
|
if (r==DLL_PROCESS_ATTACH)
|
|
{
|
|
DisableThreadLibraryCalls((HMODULE)hMod);
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
}
|