/* HUMp3 WinAmp PlugIn Dmitry Borisov (C)2005 Dmitry Borisov ## This library is free software; you can redistribute it and/or ## modify it under the terms of the GNU Library General Public ## License as published by the Free Software Foundation; either ## version 2 of the License, or (at your option) any later version. ## ## This library is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## Library General Public License for more details. ## ## You should have received a copy of the GNU Library General Public ## License along with this library; if not, write to the Free ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## Dmitry Borisov */ #include "HUMp3.h" #include "winamp.h" #include "plugin.h" #include "Commands.h" #include "usbhu.h" #include "resource.h" #include "ConfigDlg1.h" #include "stdafx.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CHUMp3::CHUMp3() { //AfxEnableControlContainer(); strcpy( this->sVersion, "???" ); } CHUMp3::~CHUMp3() { } // --------------------------------------------------------------------------------- bool CHUMp3::SetTrackInfo() { int iTrack= WinampGetPlaylistPos(NULL); int iTrackTime= WinampGetTrackTime( NULL ); char s[ 255 ]; if( this->iTrackTime!= iTrackTime || this->iTrack!= iTrack ) { sprintf( s, USBHU_TIME_CMD"\t%d\n", iTrackTime ); OutputDebugString( s ); if( !HU_SetState( this->cConn, s ) ) return false; if( !this->bStopped ) this->iPlaylistSeek[ this->iActivePlaylist- 1 ]= iTrackTime; this->iTrackTime= iTrackTime; if( this->iText ) { this->iTicks++; if( this->iTicks>= this->iRefreshInterval || this->iTrack!= iTrack ) { // Send text to the head unit char *s1= WinampGetTrackTitle( (char*)iTrack ); if( s1 ) { char *ss= strchr( s1, '-' ); if( this->iSplit ) { if( ss ) { ss[ 0 ]= 0; sprintf( s, USBHU_SET_TRACK_NAME"\t%s\n"USBHU_SET_ARTIST_NAME"\t%s", ss+ 2, s1 ); } } else if( ss ) sprintf( s, USBHU_SET_TRACK_NAME"\t%s", ( this->iScroll & 1 ) ? s1: ss+ 2 ); else sprintf( s, USBHU_SET_TRACK_NAME"\t%s", s1 ); // Ignore all possible errors OutputDebugString( s ); HU_SetState( this->cConn, s ); } this->iTicks= 0; this->iScroll++; } } } if( this->iTrack!= iTrack ) { sprintf( s, USBHU_DISC_CMD"\t%d\n"USBHU_TRACK_CMD"\t%d", this->iActivePlaylist, iTrack+ 1 ); OutputDebugString( s ); if( !HU_SetState( this->cConn, s ) ) return 0; this->iTrack= iTrack; this->iPlaylistPos[ this->iActivePlaylist- 1 ]= iTrack; } _ftime( &this->stLastTime ); return true; } // --------------------------------------------------------------------------------- bool CHUMp3::ProcessTick() { _timeb t; _ftime( &t ); if( ((float)t.millitm/ 1000 )+ (float)t.time> (float)this->stLastTime.time+ ((float)this->stLastTime.millitm/ 1000 )+ .3 ) { if( !this->SetTrackInfo() ) return false; if( this->iStatus== HUMP3_STATUS_FF ) WinampFF( NULL ); if( this->iStatus== HUMP3_STATUS_REW ) WinampRew( NULL ); } return true; } // --------------------------------------------------------------------------------- bool CHUMp3::SetPlaylist( int i, bool bForce ) { if( ( i<= this->iPlaylists && i ) || bForce ) { // Set the active playlist as directed if( this->iActivePlaylist!= i || bForce ) { // See if playlist file exists FILE* f= fopen( this->sPlaylists[ i- 1 ], "rt" ); if( f ) { fclose( f ); this->iActivePlaylist= i; this->iTrack= -1; WinampPlaylist( this->sPlaylists[ i- 1 ] ); WinampSetPlaylistPos((const char*)this->iPlaylistPos[ i- 1 ] ); WinampPlay( NULL ); WinampSetTrackTime( (const char*)this->iPlaylistSeek[ i- 1 ] ); //HU_SetState( this->cConn, USBHU_SET_SOUND"\t1" ); this->bStopped= false; } } } char s[ 20 ]; sprintf( s, USBHU_DISC_CMD"\t%d\n"USBHU_TRACK_CMD"\t%d", this->iActivePlaylist, this->iPlaylistPos[ this->iActivePlaylist- 1 ]+ 1 ); OutputDebugString( s ); HU_SetState( this->cConn, s ); return true; } // --------------------------------------------------------------------------------- bool CHUMp3::ExecuteCommand( char* s ) { char *t= strchr( s, '\t' ); if( !t ) return false; *t++= 0; int i= atoi( t ); int iLen= WinampPlaylistLen( NULL ); int iPos= WinampGetPlaylistPos(NULL); if( !strcmp( s, USBHU_VERSION )) strcpy( this->sVersion, t ); if( !strcmp( s, USBHU_DISC_CMD )) { this->WriteConfigFile(); return this->SetPlaylist( i, false ); } if( !strcmp( s, USBHU_DISC_NEXT ) ) if( this->iActivePlaylist< this->iPlaylists- 1 ) { this->WriteConfigFile(); return this->SetPlaylist( this->iActivePlaylist+ 1, false ); } if( !strcmp( s, USBHU_DISC_PREV ) ) if( this->iActivePlaylist> 0 ) { this->WriteConfigFile(); return this->SetPlaylist( this->iActivePlaylist- 1, false ); } if( !strcmp( s, USBHU_PLAYBACK_CMD )) { if( !this->iStatus && this->bStopped ) { WinampPlay( NULL ); WinampSetTrackTime( (const char*)this->iPlaylistSeek[ this->iActivePlaylist- 1 ] ); OutputDebugString( s ); //HU_SetState( this->cConn, USBHU_SET_SOUND"\t1" ); this->iStatus= 0; } this->bStopped= false; } if( !strcmp( s, USBHU_PLAYBACK_STOP ) ) { OutputDebugString( s ); //HU_SetState( this->cConn, USBHU_SET_SOUND"\t0" ); this->bStopped= true; this->WriteConfigFile(); WinampStop( NULL ); } if( !strcmp( s, USBHU_PLAYBACK_PAUSE ) ) WinampPause( NULL ); if( !strcmp( s, USBHU_PLAYBACK_FF ) ) if( i ) { WinampFF( NULL ); this->iStatus= HUMP3_STATUS_FF; } else this->iStatus= 0; if( !strcmp( s, USBHU_PLAYBACK_REW ) ) if( i ) { WinampRew( NULL ); this->iStatus= HUMP3_STATUS_REW; } else this->iStatus= 0; if( !strcmp( s, USBHU_TRACK_NEXT ) && iPos< iLen- 1 ) { this->WriteConfigFile(); WinampNextSong( NULL ); if( !this->SetTrackInfo() ) return false; } else if( !strcmp( s, USBHU_TRACK_PREV ) && iPos ) { this->WriteConfigFile(); WinampPrevSong( NULL ); if( !this->SetTrackInfo() ) return false; } if( !strcmp( s, USBHU_TRACK_CMD ) ) { if( i<= iLen && i && ( i- 1 )!= iPos ) { this->WriteConfigFile(); WinampSetPlaylistPos((const char*)( i- 1 )); WinampPlay( NULL ); OutputDebugString( s ); //HU_SetState( this->cConn, USBHU_SET_SOUND"\t1" ); this->bStopped= false; } } // Remap scan to repeat if( !strcmp( s, USBHU_SCAN_CMD ) && this->bScanRepeat ) WinampSetRepeat( (const char*)i ); if( !strcmp( s, USBHU_REPEAT_CMD ) || !strcmp( s, USBHU_REPEAT_ALL_CMD ) ) // Set repeat mode WinampSetRepeat( (const char*)i ); if( !strcmp( s, USBHU_MIX_CMD ) ) // Set shuffle mode WinampSetShuffle( (const char*)i ); if( !strcmp( s, USBHU_TIME_CMD ) ) { // Set playing time int t= ( i / 10000 )* 3600+ ( i/ 100 )* 60+ ( i % 100 ); WinampSetTrackTime( (const char*)t ); } return true; } /* // --------------------------------------------------------------------------------- bool CHUMp3::CheckFileTimestamp( char* sName ) { OFSTRUCT pBuf; pBuf.cBytes= sizeof( pBuf ); HANDLE f= CreateFile( sName, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if( f!= INVALID_HANDLE_VALUE ) { BY_HANDLE_FILE_INFORMATION pInfo; bool bChanged; GetFileInformationByHandle( f, &pInfo ); bChanged= pInfo.ftLastWriteTime.dwLowDateTime!= this->stLastTime.dwLowDateTime; this->stLastTime= pInfo.ftLastWriteTime; CloseHandle( f ); return bChanged; } return false; } */ // --------------------------------------------------------------------------------- // // Thread that gathers input from the HU and parses it // DWORD WINAPI CHUMp3::ThreadProc(LPVOID lpParameter) { CHUMp3* thiz= (CHUMp3*)lpParameter; thiz->hPlaylist = FindWindow(0,"Winamp Playlist Editor"); _ftime( &thiz->stLastTime ); thiz->iStatus= 0; thiz->bStopped= true; bool bQuery= false; if( !thiz->bScanRepeat ) WinampSetRepeat( (const char*)thiz->bAutoRepeat); while (thiz->IsRunning()== 2) { // Get status of the changer adapter if( thiz->IsConnected() ) { if( !bQuery ) { thiz->SetPlaylist( thiz->iActivePlaylist, true ); bQuery= true; } // Observe current state of WinAmp and report it back to HU thiz->ProcessTick(); // Get the current status of the HU char *s= HU_GetEvent( thiz->cConn ); if( s ) { int i= strlen( s ); OutputDebugString( s ); /* // Just testing char ss[ 1024 ]; char *s= ss; if( thiz->CheckFileTimestamp( "c:\\commands.txt" ) ) { FILE *f= fopen( "c:\\commands.txt", "rt" ); int i= fread( &ss, 1, 1024, f ); fclose( f ); ss[ i ]= 0; */ // See if HU presses buttons we aware of if( i ) { char *p= strchr( s, '\n' ); while( s || p ) { // Process commands from the HU over to winamp if( p ) *p++= 0; thiz->ExecuteCommand( s ); s= p; if( s ) p= strchr( s, '\n' ); } } } else if( HU_GetLastError( thiz->cConn ) ) { // Close connection and wait for a while before trying another connection HU_Close( thiz->cConn ); bQuery= false; thiz->cConn= NULL; continue; } } else { // Trying reconnect to the adapter int iRes= HU_Init(); if( iRes> 0 ) thiz->cConn= HU_Open( 0, NULL ); if( thiz->cConn ) { bQuery= false; // Check the version HU_SetState( thiz->cConn, USBHU_VERSION ); if( thiz->iTZ ) { char s[ 20 ]; sprintf( s, USBHU_TEXT_ZONE"\t%d", thiz->iTZ ); OutputDebugString( s ); HU_SetState( thiz->cConn, s ); } } else Sleep(100); } } if( thiz->cConn ) HU_Close( thiz->cConn ); OutputDebugString( "Connection closed\n" ); thiz->cConn= NULL; thiz->bRunning= 0; return 0; } // --------------------------------------------------------------------------------- // bool CHUMp3::WriteConfigFile() { char s[ MAX_PATH ]; sprintf( s, "%s.tmp", this->sPLFile ); FILE *f= fopen( s, "wt" ); if( f ) { fprintf(f, "#%d %d %d %d %d %d %d\n", this->iActivePlaylist, this->bScanRepeat, this->bAutoRepeat, this->iRefreshInterval, this->iText, this->iSplit, this->iTZ ); for( int i= 0; i< this->iPlaylists; i++ ) fprintf(f, "%s\t%d\t%d%s", this->sPlaylists[ i ], this->iPlaylistPos[ i ], this->iPlaylistSeek[ i ], ( i== ( this->iPlaylists -1 )) ? "": "\n" ); fclose( f ); DeleteFile( this->sPLFile ); MoveFile( s, this->sPLFile ); } return f ? true: false; } // --------------------------------------------------------------------------------- // // Get config file path name // bool CHUMp3::ReadConfigFile(LPSTR pszKey) { HKEY hKey; DWORD dwValueType = REG_SZ; LPSTR pszValue= 0; DWORD dwDataSize = MAX_PATH - 1; FILE *f; if( RegOpenKeyEx( HKEY_CURRENT_USER, // handle of open key pszKey, // address of name of subkey to open 0, // reserved KEY_QUERY_VALUE, // security access mask &hKey // address of handle of open key )!= ERROR_SUCCESS ) return false; if( RegQueryValueEx( hKey, // handle of key to query pszValue, // address of name of value to query 0, // reserved &dwValueType,// address of buffer for value type (BYTE*)this->sPLFile, // address of data buffer &dwDataSize // address of data buffer size )!= ERROR_SUCCESS ) return false; this->sPLFile[dwDataSize] = '\0'; RegCloseKey(hKey); // Get all playlists from the file (read line by line) strcat( this->sPLFile, "\\Plugins\\hump3.cfg"); f= fopen( this->sPLFile, "rt" ); this->iPlaylists= 0; this->iTicks= 0; this->iRefreshInterval= 3; this->iTicks= 1000; if( f ) { fscanf( f, "#%d %d %d %d %d %d %d\n", &this->iActivePlaylist, &this->bScanRepeat, &this->bAutoRepeat, &this->iRefreshInterval, &this->iText, &this->iSplit, &this->iTZ ); while( !feof( f ) ) { char s[ MAX_PATH ]; fgets( s, MAX_PATH+ 10, f ); char* ss= strchr( s, '\t' ); if( ss ) { strncpy( this->sPlaylists[ this->iPlaylists ], s, ss- s ); this->sPlaylists[ this->iPlaylists ][ ss- s ]= 0; this->iPlaylistPos[ this->iPlaylists ]= atoi( ss+ 1 ); ss= strchr( ss+ 1, '\t' ); this->iPlaylistSeek[ this->iPlaylists ]= ss ? atoi( ss+ 1 ): 0; } else strcpy( this->sPlaylists[ this->iPlaylists ], s ); if( this->sPlaylists[ this->iPlaylists ][ 0 ]== '#' || !strlen( this->sPlaylists[ this->iPlaylists ] ) ) continue; this->iPlaylists++; if( this->iPlaylists== MAX_PLAYLISTS- 1 ) break; } fclose( f ); this->bJukeBoxMode= false; } else { // Switch over to a jukebox mode. // It will read track names and will try to go to a first removable device for a playlist files this->bJukeBoxMode= true; } // That's fine if the file does not exists. Just play the default playlist. return true; } // --------------------------------------------------------------------------------- // // Start worker thread // void CHUMp3::Start() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); DWORD lpThreadId; int iRes= HU_Init(); this->cConn= NULL; if( iRes> 0 ) { this->cConn= HU_Open( 0, NULL ); if( this->cConn ) HU_SetState( this->cConn, USBHU_VERSION ); } // Load playlist file names this->ReadConfigFile( "Software\\Winamp" ); // start thread if ((h_thread = CreateThread(NULL, 0, ThreadProc, this, 0, &lpThreadId)) == NULL) { AfxMessageBox(strerror(GetLastError()), MB_ICONSTOP, 0); return; } this->bRunning= 2; } // --------------------------------------------------------------------------------- // // Stop worker thread and close CDC emulation // void CHUMp3::Stop() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); this->bRunning= 1; while( this->bRunning== 1 ) Sleep( 50 ); } // --------------------------------------------------------------------------------- // // Open the configure dialog // void CHUMp3::Config() { AFX_MANAGE_STATE(AfxGetStaticModuleState()); Stop(); CConfigDlg1 dlg; for( int i= 0; i< this->iPlaylists; i++ ) dlg.SetPlaylist( i, this->sPlaylists[ i ] ); dlg.SetFormData( this->sVersion, this->bScanRepeat, this->bAutoRepeat, this->iRefreshInterval, this->iText, this->iSplit, this->iTZ ); dlg.DoModal(); for( i= 0; i< 6; i++ ) dlg.UpdatePlaylist( i, this->sPlaylists[ i ] ); dlg.UpdateFormData( &this->bScanRepeat, &this->bAutoRepeat, &this->iRefreshInterval, &this->iText, &this->iSplit, &this->iTZ ); this->WriteConfigFile(); Start(); }