123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- /*
- * in_openmpt.cpp
- * --------------
- * Purpose: libopenmpt winamp input plugin implementation
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #ifndef NO_WINAMP
- #if defined(_MFC_VER) || 1
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #if !defined(WINVER) && !defined(_WIN32_WINDOWS)
- #ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP
- #endif
- #endif
- #if !defined(MPT_BUILD_RETRO)
- #if defined(_MSC_VER)
- #define MPT_WITH_MFC
- #endif
- #else
- #if defined(_WIN32_WINNT)
- #if (_WIN32_WINNT >= 0x0501)
- #if defined(_MSC_VER)
- #define MPT_WITH_MFC
- #endif
- #endif
- #endif
- #endif
- #if defined(MPT_WITH_MFC)
- #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Avoid binary bloat from linking unused MFC controls
- #endif // MPT_WITH_MFC
- #ifndef NOMINMAX
- #define NOMINMAX
- #endif
- #if defined(MPT_WITH_MFC)
- #include <afxwin.h>
- #include <afxcmn.h>
- #endif // MPT_WITH_MFC
- #include <windows.h>
- #endif // _MFC_VER
- #ifdef LIBOPENMPT_BUILD_DLL
- #undef LIBOPENMPT_BUILD_DLL
- #endif
- #ifdef _MSC_VER
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #ifndef _SCL_SECURE_NO_WARNINGS
- #define _SCL_SECURE_NO_WARNINGS
- #endif
- #endif // _MSC_VER
- #include "libopenmpt.hpp"
- #include "libopenmpt_plugin_settings.hpp"
- #include "libopenmpt_plugin_gui.hpp"
- #include "svn_version.h"
- #if defined(OPENMPT_VERSION_REVISION)
- static const char * in_openmpt_string = "in_openmpt " OPENMPT_API_VERSION_STRING "." OPENMPT_API_VERSION_STRINGIZE(OPENMPT_VERSION_REVISION);
- #else
- static const char * in_openmpt_string = "in_openmpt " OPENMPT_API_VERSION_STRING;
- #endif
- #ifndef NOMINMAX
- #define NOMINMAX
- #endif
- #include <windows.h>
- #ifdef UNICODE
- #define UNICODE_INPUT_PLUGIN
- #endif
- #ifndef _MSC_VER
- #define _MSC_VER 1300
- #endif
- #include "winamp/Winamp/IN2.H"
- #include "winamp/Winamp/wa_ipc.h"
- #include <algorithm>
- #include <fstream>
- #include <iostream>
- #include <iterator>
- #include <sstream>
- #include <cstring>
- #include <tchar.h>
- #define BPS 16
- #define WINAMP_DSP_HEADROOM_FACTOR 2
- #define WINAMP_BUFFER_SIZE_FRAMES 576
- #define WM_OPENMPT_SEEK (WM_USER+3)
- #define SHORT_TITLE "in_openmpt"
- static void apply_options();
- static std::string StringEncode( const std::wstring &src, UINT codepage )
- {
- int required_size = WideCharToMultiByte( codepage, 0, src.c_str(), -1, NULL, 0, NULL, NULL );
- if(required_size <= 0)
- {
- return std::string();
- }
- std::vector<CHAR> encoded_string( required_size );
- WideCharToMultiByte( codepage, 0, src.c_str(), -1, &encoded_string[0], encoded_string.size(), NULL, NULL );
- return &encoded_string[0];
- }
- static std::wstring StringDecode( const std::string & src, UINT codepage )
- {
- int required_size = MultiByteToWideChar( codepage, 0, src.c_str(), -1, NULL, 0 );
- if(required_size <= 0)
- {
- return std::wstring();
- }
- std::vector<WCHAR> decoded_string( required_size );
- MultiByteToWideChar( codepage, 0, src.c_str(), -1, &decoded_string[0], decoded_string.size() );
- return &decoded_string[0];
- }
- #if defined(UNICODE)
- static std::wstring StringToWINAPI( const std::wstring & src )
- {
- return src;
- }
- #else
- static std::string StringToWINAPI( const std::wstring & src )
- {
- return StringEncode( src, CP_ACP );
- }
- #endif
- template <typename Tstring, typename Tstring2, typename Tstring3>
- static inline Tstring StringReplace( Tstring str, const Tstring2 & oldStr_, const Tstring3 & newStr_ ) {
- std::size_t pos = 0;
- const Tstring oldStr = oldStr_;
- const Tstring newStr = newStr_;
- while ( ( pos = str.find( oldStr, pos ) ) != Tstring::npos ) {
- str.replace( pos, oldStr.length(), newStr );
- pos += newStr.length();
- }
- return str;
- }
- struct self_winamp_t {
- std::vector<char> filetypes_string;
- libopenmpt::plugin::settings settings;
- int samplerate;
- int channels;
- std::basic_string<TCHAR> cached_filename;
- std::basic_string<TCHAR> cached_title;
- int cached_length;
- std::basic_string<TCHAR> cached_infotext;
- std::int64_t decode_position_frames;
- openmpt::module * mod;
- HANDLE PlayThread;
- DWORD PlayThreadID;
- bool paused;
- std::vector<std::int16_t> buffer;
- std::vector<std::int16_t> interleaved_buffer;
- self_winamp_t() : settings(TEXT(SHORT_TITLE), true) {
- filetypes_string.clear();
- settings.changed = apply_options;
- settings.load();
- std::vector<std::string> extensions = openmpt::get_supported_extensions();
- for ( std::vector<std::string>::iterator ext = extensions.begin(); ext != extensions.end(); ++ext ) {
- std::copy( (*ext).begin(), (*ext).end(), std::back_inserter( filetypes_string ) );
- filetypes_string.push_back('\0');
- std::copy( SHORT_TITLE, SHORT_TITLE + std::strlen(SHORT_TITLE), std::back_inserter( filetypes_string ) );
- filetypes_string.push_back('\0');
- }
- filetypes_string.push_back('\0');
- samplerate = settings.samplerate;
- channels = settings.channels;
- cached_filename = std::basic_string<TCHAR>();
- cached_title = std::basic_string<TCHAR>();
- cached_length = 0;
- cached_infotext = std::basic_string<TCHAR>();
- decode_position_frames = 0;
- mod = 0;
- PlayThread = 0;
- PlayThreadID = 0;
- paused = false;
- buffer.resize( WINAMP_BUFFER_SIZE_FRAMES * channels );
- interleaved_buffer.resize( WINAMP_BUFFER_SIZE_FRAMES * channels * WINAMP_DSP_HEADROOM_FACTOR );
- }
- ~self_winamp_t() {
- return;
- }
- };
- static self_winamp_t * self = 0;
- static void apply_options() {
- if ( self->mod ) {
- self->mod->set_repeat_count( self->settings.repeatcount );
- self->mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, self->settings.mastergain_millibel );
- self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation );
- self->mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, self->settings.interpolationfilterlength );
- self->mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, self->settings.ramping );
- self->mod->ctl_set_boolean( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? true : false );
- switch ( self->settings.amiga_filter_type ) {
- case 0:
- self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "auto" );
- break;
- case 1:
- self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "unfiltered" );
- break;
- case 0xA500:
- self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a500" );
- break;
- case 0xA1200:
- self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a1200" );
- break;
- }
- }
- self->settings.save();
- }
- extern In_Module inmod;
- static DWORD WINAPI DecodeThread( LPVOID );
- static std::basic_string<TCHAR> generate_infotext( const std::basic_string<TCHAR> & filename, const openmpt::module & mod ) {
- std::basic_ostringstream<TCHAR> str;
- str << TEXT("filename: ") << filename << std::endl;
- str << TEXT("duration: ") << mod.get_duration_seconds() << TEXT("seconds") << std::endl;
- std::vector<std::string> metadatakeys = mod.get_metadata_keys();
- for ( std::vector<std::string>::iterator key = metadatakeys.begin(); key != metadatakeys.end(); ++key ) {
- if ( *key == "message_raw" ) {
- continue;
- }
- str << StringToWINAPI( StringDecode( *key, CP_UTF8 ) ) << TEXT(": ") << StringToWINAPI( StringDecode( mod.get_metadata(*key), CP_UTF8 ) ) << std::endl;
- }
- return str.str();
- }
- static void config( HWND hwndParent ) {
- #if 1
- libopenmpt::plugin::gui_edit_settings( &self->settings, hwndParent, TEXT(SHORT_TITLE) );
- #else
- static_cast<void>(hwndParent);
- #endif
- apply_options();
- }
- static void about( HWND hwndParent ) {
- std::ostringstream about;
- about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl;
- about << " Copyright (c) 2013-2022 OpenMPT Project Developers and Contributors (https://lib.openmpt.org/)" << std::endl;
- about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl;
- about << std::endl;
- about << openmpt::string::get( "contact" ) << std::endl;
- about << std::endl;
- about << "Show full credits?" << std::endl;
- if ( MessageBox( hwndParent, StringToWINAPI( StringDecode( about.str(), CP_UTF8 ) ).c_str(), TEXT(SHORT_TITLE), MB_ICONINFORMATION | MB_YESNOCANCEL | MB_DEFBUTTON1 ) != IDYES ) {
- return;
- }
- std::ostringstream credits;
- credits << openmpt::string::get( "credits" );
- #if 1
- libopenmpt::plugin::gui_show_file_info( hwndParent, TEXT(SHORT_TITLE), StringToWINAPI( StringReplace( StringDecode( credits.str(), CP_UTF8 ), L"\n", L"\r\n" ) ) );
- #else
- MessageBox( hwndParent, StringToWINAPI( StringReplace(StringDecode(credits.str(), CP_UTF8 ), L"\n", L"\r\n" ) ).c_str(), TEXT(SHORT_TITLE), MB_OK );
- #endif
- }
- static void init() {
- if ( !self ) {
- self = new self_winamp_t();
- inmod.FileExtensions = &(self->filetypes_string[0]);
- }
- }
- static void quit() {
- if ( self ) {
- inmod.FileExtensions = NULL;
- delete self;
- self = 0;
- }
- }
- static int isourfile( const in_char * /* fn */ ) {
- return 0;
- }
- static int play( const in_char * fn ) {
- if ( !fn ) {
- return -1;
- }
- try {
- std::ifstream s( fn, std::ios::binary );
- std::map< std::string, std::string > ctls;
- ctls["seek.sync_samples"] = "1";
- self->mod = new openmpt::module( s, std::clog, ctls );
- self->cached_filename = fn;
- self->cached_title = StringToWINAPI( StringDecode( self->mod->get_metadata( "title" ), CP_UTF8 ) );
- self->cached_length = static_cast<int>( self->mod->get_duration_seconds() * 1000.0 );
- self->cached_infotext = generate_infotext( self->cached_filename, *self->mod );
- apply_options();
- self->samplerate = self->settings.samplerate;
- self->channels = self->settings.channels;
- int maxlatency = inmod.outMod->Open( self->samplerate, self->channels, BPS, -1, -1 );
- std::ostringstream str;
- str << maxlatency;
- inmod.SetInfo( self->mod->get_num_channels(), self->samplerate/1000, self->channels, 1 );
- inmod.SAVSAInit( maxlatency, self->samplerate );
- inmod.VSASetInfo( self->channels, self->samplerate );
- inmod.outMod->SetVolume( -666 );
- inmod.outMod->SetPan( 0 );
- self->paused = false;
- self->decode_position_frames = 0;
- self->PlayThread = CreateThread( NULL, 0, DecodeThread, NULL, 0, &self->PlayThreadID );
- return 0;
- } catch ( ... ) {
- if ( self->mod ) {
- delete self->mod;
- self->mod = 0;
- }
- return -1;
- }
- }
- static void pause() {
- self->paused = true;
- inmod.outMod->Pause( 1 );
- }
- static void unpause() {
- self->paused = false;
- inmod.outMod->Pause( 0 );
- }
- static int ispaused() {
- return self->paused ? 1 : 0;
- }
- static void stop() {
- PostThreadMessage( self->PlayThreadID, WM_QUIT, 0, 0 );
- WaitForSingleObject( self->PlayThread, INFINITE );
- CloseHandle( self->PlayThread );
- self->PlayThread = 0;
- self->PlayThreadID = 0;
- delete self->mod;
- self->mod = 0;
- inmod.outMod->Close();
- inmod.SAVSADeInit();
- }
- static int getlength() {
- return self->cached_length;
- }
- static int getoutputtime() {
- //return (int)( self->decode_position_frames * 1000 / self->mod->get_render_param( openmpt::module::RENDER_SAMPLERATE_HZ ) /* + ( inmod.outMod->GetOutputTime() - inmod.outMod->GetWrittenTime() ) */ );
- return inmod.outMod->GetOutputTime();
- }
- static void setoutputtime( int time_in_ms ) {
- PostThreadMessage( self->PlayThreadID, WM_OPENMPT_SEEK, 0, time_in_ms );
- }
- static void setvolume( int volume ) {
- inmod.outMod->SetVolume( volume );
- }
- static void setpan( int pan ) {
- inmod.outMod->SetPan( pan );
- }
- static int infobox( const in_char * fn, HWND hWndParent ) {
- if ( fn && fn[0] != '\0' && self->cached_filename != std::basic_string<TCHAR>(fn) ) {
- try {
- std::ifstream s( fn, std::ios::binary );
- openmpt::module mod( s );
- #if 1
- libopenmpt::plugin::gui_show_file_info( hWndParent, TEXT(SHORT_TITLE), StringReplace( generate_infotext( fn, mod ), TEXT("\n"), TEXT("\r\n") ) );
- #else
- MessageBox( hWndParent, StringReplace( generate_infotext( fn, mod ), TEXT("\n"), TEXT("\r\n") ).c_str(), TEXT(SHORT_TITLE), MB_OK );
- #endif
- } catch ( ... ) {
- }
- } else {
- #if 1
- libopenmpt::plugin::gui_show_file_info( hWndParent, TEXT(SHORT_TITLE), StringReplace( self->cached_infotext, TEXT("\n"), TEXT("\r\n") ) );
- #else
- MessageBox( hWndParent, StringReplace( self->cached_infotext, TEXT("\n"), TEXT("\r\n") ).c_str(), TEXT(SHORT_TITLE), MB_OK );
- #endif
- }
- return INFOBOX_UNCHANGED;
- }
- static void getfileinfo( const in_char * filename, in_char * title, int * length_in_ms ) {
- if ( !filename || *filename == '\0' ) {
- if ( length_in_ms ) {
- *length_in_ms = self->cached_length;
- }
- if ( title ) {
- std::basic_string<TCHAR> truncated_title = self->cached_title;
- if ( truncated_title.length() >= GETFILEINFO_TITLE_LENGTH ) {
- truncated_title.resize( GETFILEINFO_TITLE_LENGTH - 1 );
- }
- _tcscpy( title, truncated_title.c_str() );
- }
- } else {
- try {
- std::ifstream s( filename, std::ios::binary );
- openmpt::module mod( s );
- if ( length_in_ms ) {
- *length_in_ms = static_cast<int>( mod.get_duration_seconds() * 1000.0 );
- }
- if ( title ) {
- std::basic_string<TCHAR> truncated_title = StringToWINAPI( StringDecode( mod.get_metadata("title"), CP_UTF8 ) );
- if ( truncated_title.length() >= GETFILEINFO_TITLE_LENGTH ) {
- truncated_title.resize( GETFILEINFO_TITLE_LENGTH - 1 );
- }
- _tcscpy( title, truncated_title.c_str() );
- }
- } catch ( ... ) {
- }
- }
- }
- static void eq_set( int /* on */ , char /* data */ [10], int /* preamp */ ) {
- return;
- }
- static DWORD WINAPI DecodeThread( LPVOID ) {
- MSG msg;
- PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE );
- bool eof = false;
- while ( true ) {
- bool quit = false;
- while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
- if ( msg.message == WM_QUIT ) {
- quit = true;
- } else if ( msg.message == WM_OPENMPT_SEEK ) {
- double pos_seconds = self->mod->set_position_seconds( msg.lParam * 0.001 );
- self->decode_position_frames = (std::int64_t)( pos_seconds * (double)self->samplerate);
- eof = false;
- inmod.outMod->Flush( (int)( pos_seconds * 1000.0 ) );
- }
- }
- if ( quit ) {
- break;
- }
- if ( eof ) {
- inmod.outMod->CanWrite(); // update output plugin state
- if ( !inmod.outMod->IsPlaying() ) {
- PostMessage( inmod.hMainWindow, WM_WA_MPEG_EOF, 0, 0 );
- return 0;
- }
- Sleep( 10 );
- } else {
- bool dsp_active = inmod.dsp_isactive() ? true : false;
- if ( inmod.outMod->CanWrite() >= (int)( WINAMP_BUFFER_SIZE_FRAMES * self->channels * sizeof( signed short ) ) * ( dsp_active ? WINAMP_DSP_HEADROOM_FACTOR : 1 ) ) {
- int frames = 0;
- switch ( self->channels ) {
- case 1:
- frames = self->mod->read( self->samplerate, WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+0*WINAMP_BUFFER_SIZE_FRAMES );
- for ( int frame = 0; frame < frames; frame++ ) {
- self->interleaved_buffer[frame*1+0] = self->buffer[0*WINAMP_BUFFER_SIZE_FRAMES+frame];
- }
- break;
- case 2:
- frames = self->mod->read( self->samplerate, WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+0*WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+1*WINAMP_BUFFER_SIZE_FRAMES );
- for ( int frame = 0; frame < frames; frame++ ) {
- self->interleaved_buffer[frame*2+0] = self->buffer[0*WINAMP_BUFFER_SIZE_FRAMES+frame];
- self->interleaved_buffer[frame*2+1] = self->buffer[1*WINAMP_BUFFER_SIZE_FRAMES+frame];
- }
- break;
- case 4:
- frames = self->mod->read( self->samplerate, WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+0*WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+1*WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+2*WINAMP_BUFFER_SIZE_FRAMES, (&(self->buffer[0]))+3*WINAMP_BUFFER_SIZE_FRAMES );
- for ( int frame = 0; frame < frames; frame++ ) {
- self->interleaved_buffer[frame*4+0] = self->buffer[0*WINAMP_BUFFER_SIZE_FRAMES+frame];
- self->interleaved_buffer[frame*4+1] = self->buffer[1*WINAMP_BUFFER_SIZE_FRAMES+frame];
- self->interleaved_buffer[frame*4+2] = self->buffer[2*WINAMP_BUFFER_SIZE_FRAMES+frame];
- self->interleaved_buffer[frame*4+3] = self->buffer[3*WINAMP_BUFFER_SIZE_FRAMES+frame];
- }
- break;
- }
- if ( frames == 0 ) {
- eof = true;
- } else {
- self->decode_position_frames += frames;
- std::int64_t decode_pos_ms = (self->decode_position_frames * 1000 / self->samplerate );
- inmod.SAAddPCMData( &( self->interleaved_buffer[0] ), self->channels, BPS, (int)decode_pos_ms );
- inmod.VSAAddPCMData( &( self->interleaved_buffer[0] ), self->channels, BPS, (int)decode_pos_ms );
- if ( dsp_active ) {
- frames = inmod.dsp_dosamples( &( self->interleaved_buffer[0] ), frames, BPS, self->channels, self->samplerate );
- }
- int bytes = frames * self->channels * sizeof( signed short );
- inmod.outMod->Write( (char*)&( self->interleaved_buffer[0] ), bytes );
- }
- } else {
- Sleep( 10 );
- }
- }
- }
- return 0;
- }
- #if defined(__GNUC__)
- extern In_Module inmod;
- #endif
- In_Module inmod = {
- IN_VER,
- const_cast< char * >( in_openmpt_string ), // SHORT_TITLE,
- 0, // hMainWindow
- 0, // hDllInstance
- NULL, // filled later in Init() "mptm\0ModPlug Tracker Module (*.mptm)\0",
- 1, // is_seekable
- 1, // uses output
- config,
- about,
- init,
- quit,
- getfileinfo,
- infobox,
- isourfile,
- play,
- pause,
- unpause,
- ispaused,
- stop,
- getlength,
- getoutputtime,
- setoutputtime,
- setvolume,
- setpan,
- 0,0,0,0,0,0,0,0,0, // vis
- 0,0, // dsp
- eq_set,
- NULL, // setinfo
- 0 // out_mod
- };
- extern "C" __declspec(dllexport) In_Module * winampGetInModule2();
- extern "C" __declspec(dllexport) In_Module * winampGetInModule2() {
- return &inmod;
- }
- #if defined(MPT_WITH_MFC)
- #ifdef _MFC_VER
- namespace libopenmpt {
- namespace plugin {
- void DllMainAttach() {
- // nothing
- }
- void DllMainDetach() {
- // nothing
- }
- } // namespace plugin
- } // namespace libopenmpt
- #else
- // nothing
- #endif
- #endif // MPT_WITH_MFC
- #endif // NO_WINAMP
|