123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- #define PLUGIN_VERSION L"2.70"
- #include "Main.h"
- #include <windows.h>
- #include <stdio.h>
- #include <locale.h>
- #include "resource.h"
- #include "../Winamp/in2.h"
- #include "../Winamp/wa_ipc.h"
- #include "../nu/AutoChar.h"
- #include "api__in_mp4.h"
- #include <api/service/waservicefactory.h>
- #pragma warning(disable:4786)
- #include "mpeg4audio.h"
- #include <shlwapi.h>
- #include <malloc.h>
- #include "VirtualIO.h"
- #include "AlbumArt.h"
- #include <assert.h>
- #include "../in_wmvdrm/Remaining.h"
- #include "VideoThread.h"
- #include "RawMediaReader.h"
- #include "../nu/Singleton.h"
- #include <strsafe.h>
- Remaining remaining;
- nu::VideoClock video_clock;
- AlbumArtFactory albumArtFactory;
- wchar_t m_ini[MAX_PATH] = {0};
- int infoDlg(const wchar_t *fn, HWND hwnd);
- #define WM_WA_IPC WM_USER
- #define WM_WA_MPEG_EOF WM_USER+2
- HANDLE hThread;
- static DWORD WINAPI playProc(LPVOID lpParameter);
- static int paused, m_kill;
- HANDLE killEvent, seekEvent, pauseEvent;
- static int m_opened;
- static RawMediaReaderService raw_media_reader_service;
- static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
- static void stop();
- // wasabi based services for localisation support
- api_language *WASABI_API_LNG = 0;
- HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
- api_config *AGAVE_API_CONFIG = 0;
- api_memmgr *WASABI_API_MEMMGR = 0;
- api_application *WASABI_API_APP = 0;
- int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
- {
- MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
- msgbx.lpszText = message;
- msgbx.lpszCaption = title;
- msgbx.lpszIcon = MAKEINTRESOURCE(102);
- msgbx.hInstance = GetModuleHandle(0);
- msgbx.dwStyle = MB_USERICON;
- msgbx.hwndOwner = parent;
- return MessageBoxIndirect(&msgbx);
- }
- void about(HWND hwndParent)
- {
- wchar_t message[1024] = {0}, text[1024] = {0};
- WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_MPEG4_AUDIO_DECODER_OLD,text,1024);
- StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
- mod.description, TEXT(__DATE__));
- DoAboutMessageBox(hwndParent,text,message);
- }
- static const wchar_t defaultExtensions_nonpro[] = {L"M4A;MP4"};
- static const wchar_t defaultExtensions_pro[] = {L"M4A;MP4;M4V"};
- const wchar_t *defaultExtensions = defaultExtensions_pro;
- // the return pointer has been malloc'd. Use free() when you are done.
- char *BuildExtensions(const char *extensions)
- {
- char name[64] = {0};
- WASABI_API_LNGSTRING_BUF(IDS_MP4_FILE,name,64);
- size_t length = strlen(extensions) + 1 + strlen(name) + 2;
- char *newExt = (char *)calloc(length, sizeof(char));
- char *ret = newExt; // save because we modify newExt
- // copy extensions
- StringCchCopyExA(newExt, length, extensions, &newExt, &length, 0);
- newExt++;
- length--;
- // copy description
- StringCchCopyExA(newExt, length, name, &newExt, &length, 0);
- newExt++;
- length--;
- // double null terminate
- assert(length == 1);
- *newExt = 0;
- return ret;
- }
- int init()
- {
- if (!IsWindow(mod.hMainWindow))
- return IN_INIT_FAILURE;
- killEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- seekEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- pauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
- mod.service->service_register(&albumArtFactory);
- raw_factory.Register(mod.service, &raw_media_reader_service);
- waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
- if (sf)
- AGAVE_API_CONFIG = (api_config *)sf->getInterface();
- sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);
- if (sf)
- WASABI_API_APP = (api_application *)sf->getInterface();
- sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
- if (sf)
- WASABI_API_MEMMGR = (api_memmgr *)sf->getInterface();
- sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);
- if (sf)
- WAC_API_DOWNLOADMANAGER = (api_downloadManager *)sf->getInterface();
- sf = mod.service->service_getServiceByGuid(ThreadPoolGUID);
- if (sf)
- WASABI_API_THREADPOOL = (api_threadpool *)sf->getInterface();
- // loader so that we can get the localisation service api for use
- sf = mod.service->service_getServiceByGuid(languageApiGUID);
- if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
- // need to have this initialised before we try to do anything with localisation features
- WASABI_API_START_LANG(mod.hDllInstance,InMp4LangGUID);
- static wchar_t szDescription[256];
- StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MPEG4_AUDIO_DECODER),PLUGIN_VERSION);
- mod.description = (char*)szDescription;
- const wchar_t *inipath = (wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
- PathCombineW(m_ini, inipath, L"Plugins");
- CreateDirectoryW(m_ini, NULL);
- PathAppendW(m_ini, L"in_mp4.ini");
- wchar_t exts[1024] = {0};
- GetPrivateProfileStringW(L"in_mp4", L"extensionlist", defaultExtensions, exts, 1024, m_ini);
- mod.FileExtensions = BuildExtensions(AutoChar(exts));
- return IN_INIT_SUCCESS;
- }
- void quit()
- {
- CloseHandle(killEvent);
- CloseHandle(seekEvent);
- raw_factory.Deregister(mod.service);
- waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
- if (sf)
- sf->releaseInterface(AGAVE_API_CONFIG);
- sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);
- if (sf)
- sf->releaseInterface(WASABI_API_APP);
- sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);
- if (sf)
- sf->releaseInterface( WAC_API_DOWNLOADMANAGER );
- mod.service->service_deregister(&albumArtFactory);
- free(mod.FileExtensions);
- }
- int isourfile(const wchar_t *fn)
- {
- return 0;
- }
- void config(HWND hwndParent);
- void setoutputtime(int time_in_ms)
- {
- m_needseek = time_in_ms;
- SetEvent(seekEvent);
- }
- MP4TrackId GetVideoTrack(MP4FileHandle infile)
- {
- int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
- for (int i = 0; i < numTracks; i++)
- {
- MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
- const char* trackType = MP4GetTrackType(infile, trackId);
- if (!lstrcmpA(trackType, MP4_VIDEO_TRACK_TYPE))
- return trackId;
- }
- /* can't decode this */
- return MP4_INVALID_TRACK_ID;
- }
- MP4TrackId GetAudioTrack(MP4FileHandle infile)
- {
- int ret = MP4_INVALID_TRACK_ID;
- __try
- {
- /* find AAC track */
- int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
- for (int i = 0; i < numTracks; i++)
- {
- MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
- if (trackId != MP4_INVALID_TRACK_ID)
- {
- const char* trackType = MP4GetTrackType(infile, trackId);
- if (trackType && !lstrcmpA(trackType, MP4_AUDIO_TRACK_TYPE))
- return trackId;
- }
- }
- /* can't decode this */
- return MP4_INVALID_TRACK_ID;
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- return MP4_INVALID_TRACK_ID;
- }
- return ret;
- }
- MP4SampleId numSamples, numVideoSamples;
- MP4FileHandle MP4hFile;
- MP4TrackId audio_track, video_track;
- double m_length;
- volatile int m_needseek = -1;
- unsigned int audio_srate, audio_nch, audio_bps, audio_bitrate=0;
- unsigned int video_bitrate=0;
- wchar_t lastfn[MAX_PATH*4] = L"";
- MP4AudioDecoder *audio = 0;
- waServiceFactory *audioFactory = 0, *videoFactory = 0;
- MP4VideoDecoder *video = 0;
- uint32_t m_timescale = 0, m_video_timescale = 0;
- static void *reader = 0;
- bool audio_chunk = false;
- enum
- {
- READER_UNICODE=0,
- READER_HTTP=1,
- };
- int reader_type=READER_UNICODE;
- bool open_mp4(const wchar_t *fn)
- {
- audio = 0;
- video = 0;
- if (!_wcsnicmp(fn, L"http://", 7) || !_wcsnicmp(fn, L"https://", 8))
- {
- reader = CreateReader(fn, killEvent);
- reader_type=READER_HTTP;
- MP4hFile = MP4ReadEx(fn, reader, &HTTPIO);
- }
- else
- {
- reader = CreateUnicodeReader(fn);
- if (!reader)
- return false;
- reader_type=READER_UNICODE;
- MP4hFile = MP4ReadEx(fn, reader, &UnicodeIO);
- }
- if (!MP4hFile)
- {
- return false;
- }
- m_opened = 1;
- unsigned int output_bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
- if (output_bits >= 24)
- output_bits = 24;
- else
- output_bits = 16;
- unsigned int max_channels;
- // get max channels
- if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true))
- max_channels = 6;
- else if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false))
- max_channels = 1;
- else
- max_channels = 2;
- audio_track = GetAudioTrack(MP4hFile);
- if (audio_track == MP4_INVALID_TRACK_ID || !CreateDecoder(MP4hFile, audio_track, audio, audioFactory) || audio->OpenMP4(MP4hFile, audio_track, output_bits, max_channels, false) != MP4_SUCCESS)
- {
- audio = 0;
- video_clock.Start();
- }
- video_track = GetVideoTrack(MP4hFile);
- if (video_track != MP4_INVALID_TRACK_ID)
- {
- CreateVideoDecoder(MP4hFile, video_track, video, videoFactory);
- if (video)
- video->Open(MP4hFile, video_track);
- }
- else
- video=0;
- if (!audio && !video)
- {
- return false;
- }
- numVideoSamples = MP4GetTrackNumberOfSamples(MP4hFile, video_track);
- m_video_timescale = MP4GetTrackTimeScale(MP4hFile, video_track);
- unsigned __int64 trackDuration;
- double lengthAudio = 0;
- double lengthVideo = 0;
-
- if (audio_track != MP4_INVALID_TRACK_ID)
- {
- if (audio)
- {
- ConfigureDecoderASC(MP4hFile, audio_track, audio);
- audio_chunk = !!audio->RequireChunks();
- }
- else
- audio_chunk = false;
- numSamples = audio_chunk?MP4GetTrackNumberOfChunks(MP4hFile, audio_track):MP4GetTrackNumberOfSamples(MP4hFile, audio_track);
- m_timescale = MP4GetTrackTimeScale(MP4hFile, audio_track);
- trackDuration = MP4GetTrackDuration(MP4hFile, audio_track);
- lengthAudio = (double)(__int64)trackDuration / (double)m_timescale;
- }
- else
- {
- numSamples = numVideoSamples;
- trackDuration = MP4GetTrackDuration(MP4hFile, video_track);
- lengthVideo = (double)(__int64)trackDuration / (double)m_video_timescale;
- }
-
- /* length in Sec. */
- m_length = max(lengthAudio, lengthVideo); //(double)(__int64)trackDuration / (double)m_timescale;
- audio_bitrate = MP4GetTrackBitRate(MP4hFile, audio_track) / 1000;
- if (video)
- video_bitrate = MP4GetTrackBitRate(MP4hFile, video_track) / 1000;
- else
- video_bitrate = 0;
- if (audio && audio->SetGain(GetGain(MP4hFile)) == MP4_SUCCESS)
- mod.UsesOutputPlug |= 8;
- else
- mod.UsesOutputPlug &= ~8;
- return true;
- }
- int play(const wchar_t *fn)
- {
- video_clock.Reset();
- if (!videoOutput) // grab this now while we're on the main thread
- videoOutput = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
- audio = 0;
- video = 0;
- paused = 0;
- m_kill = 0;
- ResetEvent(killEvent);
- m_length = 0;
- ResetEvent(seekEvent);
- m_needseek = -1;
- SetEvent(pauseEvent);
- if (m_force_seek != -1)
- {
- setoutputtime(m_force_seek);
- }
- m_opened = 0;
- lstrcpynW(lastfn, fn, MAX_PATH*4);
- DWORD thread_id;
- HANDLE threadCreatedEvent = CreateEvent(0, FALSE, FALSE, 0);
- hThread = CreateThread(NULL, NULL, PlayProc, (LPVOID)threadCreatedEvent, NULL, &thread_id);
- SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
- WaitForSingleObject(threadCreatedEvent, INFINITE);
- CloseHandle(threadCreatedEvent);
- return 0;
- }
- static inline wchar_t *IncSafe(wchar_t *val, int x)
- {
- while (x--)
- {
- if (*val)
- val++;
- }
- return val;
- }
- void GetGaps(MP4FileHandle mp4, unsigned __int32 &pre, unsigned __int32 &post)
- {
- wchar_t gap_data[128] = {0};
- if (GetCustomMetadata(mp4, "iTunSMPB", gap_data, 128) && gap_data[0])
- {
- wchar_t *itr = IncSafe(gap_data, 9);
- pre = wcstoul(itr, 0, 16);
- itr = IncSafe(itr, 9);
- post = wcstoul(itr, 0, 16);
- // don't care about total number of samples, really
- /*
- itr+=9;
- unsigned int numSamples = wcstoul(itr, 0, 16);*/
- }
- else
- {
- pre = 0;
- post = 0;
- }
- }
- float GetGain(MP4FileHandle mp4, bool allowDefault)
- {
- if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
- {
- float dB = 0, peak = 1.0f;
- wchar_t gain[128] = L"", peakVal[128] = L"";
- _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
- switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
- {
- case 0: // track
- if ((!GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128) || !gain[0])
- && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128);
- if ((!GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128) || !peakVal[0])
- && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128);
- break;
- case 1:
- if ((!GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128) || !gain[0])
- && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128);
- if ((!GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128) || !peakVal[0])
- && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128);
- break;
- }
- if (gain[0])
- {
- if (gain[0] == L'+')
- dB = (float)_wtof_l(&gain[1],C_locale);
- else
- dB = (float)_wtof_l(gain,C_locale);
- }
- else if (allowDefault)
- {
- dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
- return powf(10.0f, dB / 20.0f);
- }
- if (peakVal[0])
- {
- peak = (float)_wtof_l(peakVal,C_locale);
- }
- switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
- {
- case 0: // apply gain
- return powf(10.0f, dB / 20.0f);
- case 1: // apply gain, but don't clip
- return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
- case 2: // normalize
- return 1.0f / peak;
- case 3: // prevent clipping
- if (peak > 1.0f)
- return 1.0f / peak;
- else
- return 1.0f;
- }
- }
- return 1.0f; // no gain
- }
- bool first;
- void pause()
- {
- paused = 1;
- if (audio)
- {
- mod.outMod->Pause(1);
- }
- else
- {
- video_clock.Pause();
- }
- ResetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state
- }
- void unpause()
- {
- paused = 0;
- if (audio)
- {
- mod.outMod->Pause(0);
- }
- else
- {
- video_clock.Unpause();
- }
- SetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state
- }
- int ispaused()
- {
- return paused;
- }
- void stop()
- {
- if (reader && reader_type==READER_HTTP) StopReader(reader);
- lastfn[0] = 0;
- SetEvent(killEvent);
- m_kill = 1;
- WaitForSingleObject(hThread, INFINITE);
- mod.outMod->Close();
- mod.SAVSADeInit();
- if (m_opened) MP4Close(MP4hFile);
- MP4hFile = 0;
- m_opened = 0;
- if (audio)
- {
- audio->Close();
- audioFactory->releaseInterface(audio);
- }
- audioFactory = 0;
- audio = 0;
- if (video)
- {
- video->Close();
- videoFactory->releaseInterface(video);
- }
- videoFactory=0;
- video = 0;
- if (reader)
- {
- if (reader_type == READER_HTTP)
- DestroyReader(reader);
- else
- DestroyUnicodeReader(reader);
- }
- reader = 0;
- }
- int getlength()
- {
- return (int)(m_length*1000);
- }
- int getoutputtime()
- {
- if (m_needseek == -1)
- return (int)GetClock();
- else
- return m_needseek; // this prevents the seekbar from jumping around while the playthread is seeking
- }
- void setvolume(int volume)
- {
- mod.outMod->SetVolume(volume);
- }
- void setpan(int pan)
- {
- mod.outMod->SetPan(pan);
- }
- /*
- void FillInfo(HWND hwndDlg, MP4FileHandle hMp4);
- void CALLBACK CurrentlyPlayingInfoBox(ULONG_PTR param)
- {
- ThreadInfoBox *threadInfo = (ThreadInfoBox *)param;
- FillInfo(threadInfo->hwndDlg, MP4hFile);
- SetEvent(threadInfo->completionEvent);
- }
- */
- void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms)
- {
- if (!filename || !*filename) // currently playing file
- {
- if (length_in_ms) *length_in_ms = getlength();
- if (title) // get non-path portion.of filename
- {
- lstrcpynW(title, lastfn, GETFILEINFO_TITLE_LENGTH);
- PathStripPathW(title);
- PathRemoveExtensionW(title);
- }
- }
- else // some other file
- {
- if (length_in_ms) // calculate length
- {
- *length_in_ms = -1000; // the default is unknown file length (-1000).
- MP4FileHandle hMp4 = MP4Read(filename);
- if (hMp4)
- {
- double lengthAudio = 0;
- double lengthVideo = 0;
- MP4TrackId audio_track = GetAudioTrack(hMp4);
- if (audio_track != -1)
- {
- int timescale = MP4GetTrackTimeScale(hMp4, audio_track);
- unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, audio_track);
- lengthAudio = (double)(__int64)trackDuration / (double)timescale;
- }
- MP4TrackId video_track = GetVideoTrack(hMp4);
- if (video_track != -1)
- {
- int timescale = MP4GetTrackTimeScale(hMp4, video_track);
- unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, video_track);
- lengthVideo = (double)(__int64)trackDuration / (double)timescale;
- }
- *length_in_ms = (int)(max(lengthAudio, lengthVideo) * 1000);
- MP4Close(hMp4);
- }
- }
- if (title) // get non path portion of filename
- {
- lstrcpynW(title, filename, GETFILEINFO_TITLE_LENGTH);
- PathStripPathW(title);
- PathRemoveExtensionW(title);
- }
- }
- }
- void eq_set(int on, char data[10], int preamp)
- {}
- // module definition.
- In_Module mod =
- {
- IN_VER_RET, // defined in IN2.H
- "nullsoft(in_mp4.dll)", //"Nullsoft MPEG-4 Audio Decoder v1.22"
- 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.
- 1, // is_seekable
- 1, // uses output plug-in system
- config,
- about,
- init,
- quit,
- getfileinfo,
- infoDlg,
- 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
- };
- extern "C"
- {
- __declspec(dllexport) In_Module * winampGetInModule2()
- {
- return &mod;
- }
- }
|