You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1182 lines
25 KiB
1182 lines
25 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#include "cbase.h" |
|
#include <windows.h> |
|
#include "resource.h" |
|
#include "wavefile.h" |
|
#include "wavebrowser.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "ifaceposersound.h" |
|
#include "snd_wave_source.h" |
|
#include "filesystem.h" |
|
#include "tabwindow.h" |
|
#include "inputproperties.h" |
|
#include "choreowidgetdrawhelper.h" |
|
#include "ifileloader.h" |
|
#include "tier2/riff.h" |
|
#include "UtlBuffer.h" |
|
#include "ChoreoEvent.h" |
|
|
|
CWaveBrowser *g_pWaveBrowser = NULL; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Implements the RIFF i/o interface on stdio |
|
//----------------------------------------------------------------------------- |
|
class StdIOReadBinary : public IFileReadBinary |
|
{ |
|
public: |
|
int open( const char *pFileName ) |
|
{ |
|
return (int)filesystem->Open( pFileName, "rb" ); |
|
} |
|
|
|
int read( void *pOutput, int size, int file ) |
|
{ |
|
if ( !file ) |
|
return 0; |
|
|
|
return filesystem->Read( pOutput, size, (FileHandle_t)file ); |
|
} |
|
|
|
void seek( int file, int pos ) |
|
{ |
|
if ( !file ) |
|
return; |
|
|
|
filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD ); |
|
} |
|
|
|
unsigned int tell( int file ) |
|
{ |
|
if ( !file ) |
|
return 0; |
|
|
|
return filesystem->Tell( (FileHandle_t)file ); |
|
} |
|
|
|
unsigned int size( int file ) |
|
{ |
|
if ( !file ) |
|
return 0; |
|
|
|
return filesystem->Size( (FileHandle_t)file ); |
|
} |
|
|
|
void close( int file ) |
|
{ |
|
if ( !file ) |
|
return; |
|
|
|
filesystem->Close( (FileHandle_t)file ); |
|
} |
|
}; |
|
|
|
class StdIOWriteBinary : public IFileWriteBinary |
|
{ |
|
public: |
|
int create( const char *pFileName ) |
|
{ |
|
return (int)filesystem->Open( pFileName, "wb" ); |
|
} |
|
|
|
int write( void *pData, int size, int file ) |
|
{ |
|
return filesystem->Write( pData, size, (FileHandle_t)file ); |
|
} |
|
|
|
void close( int file ) |
|
{ |
|
filesystem->Close( (FileHandle_t)file ); |
|
} |
|
|
|
void seek( int file, int pos ) |
|
{ |
|
filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD ); |
|
} |
|
|
|
unsigned int tell( int file ) |
|
{ |
|
return filesystem->Tell( (FileHandle_t)file ); |
|
} |
|
}; |
|
|
|
static StdIOReadBinary io_in; |
|
static StdIOWriteBinary io_out; |
|
|
|
#define RIFF_WAVE MAKEID('W','A','V','E') |
|
#define WAVE_FMT MAKEID('f','m','t',' ') |
|
#define WAVE_DATA MAKEID('d','a','t','a') |
|
#define WAVE_FACT MAKEID('f','a','c','t') |
|
#define WAVE_CUE MAKEID('c','u','e',' ') |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : &walk - |
|
//----------------------------------------------------------------------------- |
|
static void SceneManager_ParseSentence( CSentence& sentence, IterateRIFF &walk ) |
|
{ |
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); |
|
|
|
buf.EnsureCapacity( walk.ChunkSize() ); |
|
walk.ChunkRead( buf.Base() ); |
|
buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() ); |
|
|
|
sentence.InitFromDataChunk( buf.Base(), buf.TellPut() ); |
|
} |
|
|
|
bool SceneManager_LoadSentenceFromWavFileUsingIO( char const *wavfile, CSentence& sentence, IFileReadBinary& io ) |
|
{ |
|
sentence.Reset(); |
|
|
|
InFileRIFF riff( wavfile, io ); |
|
|
|
// UNDONE: Don't use printf to handle errors |
|
if ( riff.RIFFName() != RIFF_WAVE ) |
|
{ |
|
return false; |
|
} |
|
|
|
// set up the iterator for the whole file (root RIFF is a chunk) |
|
IterateRIFF walk( riff, riff.RIFFSize() ); |
|
|
|
// This chunk must be first as it contains the wave's format |
|
// break out when we've parsed it |
|
bool found = false; |
|
while ( walk.ChunkAvailable() && !found ) |
|
{ |
|
switch( walk.ChunkName() ) |
|
{ |
|
case WAVE_VALVEDATA: |
|
{ |
|
found = true; |
|
SceneManager_ParseSentence( sentence, walk ); |
|
} |
|
break; |
|
} |
|
walk.ChunkNext(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool SceneManager_LoadSentenceFromWavFile( char const *wavfile, CSentence& sentence ) |
|
{ |
|
return SceneManager_LoadSentenceFromWavFileUsingIO( wavfile, sentence, io_in ); |
|
} |
|
|
|
enum |
|
{ |
|
// Controls |
|
IDC_SB_LISTVIEW = 101, |
|
IDC_SB_FILETREE, |
|
|
|
// Messages |
|
IDC_SB_PLAY = 1000, |
|
}; |
|
|
|
enum |
|
{ |
|
COL_WAV = 0, |
|
COL_DUCKED, |
|
COL_PHONEMES, |
|
COL_SENTENCE |
|
}; |
|
|
|
class CWaveList : public mxListView |
|
{ |
|
public: |
|
CWaveList( mxWindow *parent, int id = 0 ) |
|
: mxListView( parent, 0, 0, 0, 0, id ) |
|
{ |
|
// Add column headers |
|
insertTextColumn( COL_WAV, 300, "WAV" ); |
|
insertTextColumn( COL_DUCKED, 50, "Ducked" ); |
|
insertTextColumn( COL_PHONEMES, 120, "Words [ Phonemes ]" ); |
|
insertTextColumn( COL_SENTENCE, 300, "Sentence Text" ); |
|
} |
|
}; |
|
|
|
class CWaveFileTree : public mxTreeView |
|
{ |
|
public: |
|
CWaveFileTree( mxWindow *parent, int id = 0 ) : mxTreeView( parent, 0, 0, 0, 0, id ), |
|
m_Paths( 0, 0, FileTreeLessFunc ) |
|
{ |
|
} |
|
|
|
void Clear() |
|
{ |
|
removeAll(); |
|
m_Paths.RemoveAll(); |
|
} |
|
|
|
void FindOrAddSubdirectory( char const *subdir ) |
|
{ |
|
FileTreePath fp; |
|
Q_strcpy( fp.path, subdir ); |
|
|
|
if ( m_Paths.Find( fp ) != m_Paths.InvalidIndex() ) |
|
return; |
|
|
|
m_Paths.Insert( fp ); |
|
} |
|
|
|
mxTreeViewItem *FindOrAddChildItem( mxTreeViewItem *parent, char const *child ) |
|
{ |
|
mxTreeViewItem *p = getFirstChild( parent ); |
|
if ( !p ) |
|
{ |
|
return add( parent, child ); |
|
} |
|
|
|
while ( p ) |
|
{ |
|
if ( !Q_stricmp( getLabel( p ), child ) ) |
|
return p; |
|
|
|
p = getNextChild( p ); |
|
} |
|
|
|
return add( parent, child ); |
|
} |
|
|
|
void _PopulateTree( int pathId, char const *path ) |
|
{ |
|
char sz[ 512 ]; |
|
Q_strcpy( sz, path ); |
|
char *p = sz; |
|
|
|
// Start at root |
|
mxTreeViewItem *cur = NULL; |
|
|
|
// Tokenize path |
|
while ( p && p[0] ) |
|
{ |
|
char *slash = Q_strstr( p, "/" ); |
|
if ( !slash ) |
|
{ |
|
slash = Q_strstr( p, "\\" ); |
|
} |
|
|
|
char *check = p; |
|
|
|
if ( slash ) |
|
{ |
|
*slash = 0; |
|
|
|
// see if a child of current already exists with this name |
|
p = slash + 1; |
|
} |
|
else |
|
{ |
|
p = NULL; |
|
} |
|
|
|
Assert( check ); |
|
|
|
cur = FindOrAddChildItem( cur, check ); |
|
} |
|
|
|
setUserData( cur, (void *)pathId ); |
|
} |
|
|
|
char const *GetSelectedPath( void ) |
|
{ |
|
mxTreeViewItem *tvi = getSelectedItem(); |
|
unsigned int id = (unsigned int)getUserData( tvi ); |
|
|
|
if ( id < 0 || id >= m_Paths.Count() ) |
|
{ |
|
Assert( 0 ); |
|
return ""; |
|
} |
|
return m_Paths[ id ].path; |
|
} |
|
|
|
void PopulateTree() |
|
{ |
|
int i; |
|
for ( i = m_Paths.FirstInorder(); i != m_Paths.InvalidIndex(); i = m_Paths.NextInorder( i ) ) |
|
{ |
|
_PopulateTree( i, m_Paths[ i ].path ); |
|
} |
|
|
|
mxTreeViewItem *p = getFirstChild( NULL ); |
|
setOpen( p, true ); |
|
} |
|
|
|
struct FileTreePath |
|
{ |
|
char path[ MAX_PATH ]; |
|
}; |
|
|
|
static bool FileTreeLessFunc( const FileTreePath &lhs, const FileTreePath &rhs ) |
|
{ |
|
return Q_stricmp( lhs.path, rhs.path ) < 0; |
|
} |
|
|
|
CUtlRBTree< FileTreePath, int > m_Paths; |
|
}; |
|
#pragma optimize( "", off ) |
|
class CWaveOptionsWindow : public mxWindow |
|
{ |
|
typedef mxWindow BaseClass; |
|
public: |
|
enum |
|
{ |
|
IDC_PLAYSOUND = 1000, |
|
IDC_STOP_SOUNDS, |
|
IDC_SEARCH, |
|
IDC_CANCELSEARCH, |
|
}; |
|
|
|
CWaveOptionsWindow( CWaveBrowser *browser ) : BaseClass( browser, 0, 0, 0, 0 ), m_pBrowser( browser ) |
|
{ |
|
FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); |
|
|
|
m_szSearchString[0]=0; |
|
|
|
m_pPlay = new mxButton( this, 0, 0, 0, 0, "Play", IDC_PLAYSOUND ); |
|
|
|
m_pStopSounds = new mxButton( this, 0, 0, 0, 0, "Stop Sounds", IDC_STOP_SOUNDS ); |
|
|
|
m_pSearch = new mxLineEdit( this, 0, 0, 0, 0, "", IDC_SEARCH ); |
|
|
|
m_pCancelSearch = new mxButton( this, 0, 0, 0, 0, "Cancel", IDC_CANCELSEARCH ); |
|
} |
|
|
|
bool PaintBackground( void ) |
|
{ |
|
redraw(); |
|
return false; |
|
} |
|
|
|
|
|
virtual void redraw() |
|
{ |
|
CChoreoWidgetDrawHelper drawHelper( this, GetSysColor( COLOR_BTNFACE ) ); |
|
} |
|
virtual int handleEvent( mxEvent *event ) |
|
{ |
|
int iret = 0; |
|
switch ( event->event ) |
|
{ |
|
default: |
|
break; |
|
case mxEvent::Size: |
|
{ |
|
iret = 1; |
|
|
|
int split = 120; |
|
|
|
int x = 1; |
|
|
|
m_pPlay->setBounds( x, 1, split, h2() - 2 ); |
|
|
|
x += split + 10; |
|
|
|
m_pStopSounds->setBounds( x, 1, split, h2()-2 ); |
|
|
|
x += split + 10; |
|
|
|
m_pCancelSearch->setBounds( x, 1, split, h2() - 2 ); |
|
|
|
x += split + 10; |
|
|
|
m_pSearch->setBounds( x, 0, split * 3, h2() - 1 ); |
|
|
|
x += split * 3 + 10; |
|
} |
|
break; |
|
case mxEvent::KeyDown: |
|
switch ( event->action ) |
|
{ |
|
default: |
|
break; |
|
case IDC_SEARCH: |
|
{ |
|
if ( event->event == mxEvent::KeyDown ) |
|
{ |
|
OnSearch(); |
|
} |
|
iret = 1; |
|
}; |
|
break; |
|
} |
|
break; |
|
case mxEvent::Action: |
|
{ |
|
switch ( event->action ) |
|
{ |
|
case IDC_STOP_SOUNDS: |
|
{ |
|
iret = 1; |
|
sound->StopAll(); |
|
} |
|
break; |
|
case IDC_SEARCH: |
|
iret = 1; |
|
break; |
|
case IDC_PLAYSOUND: |
|
{ |
|
iret = 1; |
|
m_pBrowser->OnPlay(); |
|
} |
|
break; |
|
case IDC_CANCELSEARCH: |
|
{ |
|
iret = 1; |
|
OnCancelSearch(); |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
break; |
|
} |
|
|
|
return iret; |
|
} |
|
|
|
char const *GetSearchString() |
|
{ |
|
return m_szSearchString; |
|
} |
|
|
|
void OnSearch() |
|
{ |
|
m_pSearch->getText( m_szSearchString, sizeof( m_szSearchString ) ); |
|
|
|
m_pBrowser->OnSearch(); |
|
} |
|
|
|
void OnCancelSearch() |
|
{ |
|
m_szSearchString[ 0 ] = 0; |
|
m_pSearch->clear(); |
|
|
|
m_pBrowser->OnCancelSearch(); |
|
} |
|
|
|
private: |
|
|
|
mxButton *m_pStopSounds; |
|
mxButton *m_pPlay; |
|
mxLineEdit *m_pSearch; |
|
mxButton *m_pCancelSearch; |
|
|
|
CWaveBrowser *m_pBrowser; |
|
|
|
char m_szSearchString[ 256 ]; |
|
}; |
|
|
|
#pragma optimize( "", on ) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *parent - |
|
//----------------------------------------------------------------------------- |
|
CWaveBrowser::CWaveBrowser( mxWindow *parent ) |
|
: IFacePoserToolWindow( "WaveBrowser", "Waves" ), mxWindow( parent, 0, 0, 0, 0 ) |
|
{ |
|
SetAutoProcess( false ); |
|
|
|
m_bTextSearch = false; |
|
m_nPrevProcessed = -1; |
|
|
|
m_pListView = new CWaveList( this, IDC_SB_LISTVIEW ); |
|
m_pOptions = new CWaveOptionsWindow( this ); |
|
m_pFileTree = new CWaveFileTree( this, IDC_SB_FILETREE ); |
|
|
|
//HIMAGELIST list = CreateImageList(); |
|
|
|
// Associate the image list with the tree-view control. |
|
//m_pListView->setImageList( (void *)list ); |
|
|
|
LoadAllSounds(); |
|
|
|
PopulateTree( NULL ); |
|
} |
|
|
|
#define CX_ICON 16 |
|
#define CY_ICON 16 |
|
|
|
HIMAGELIST CWaveBrowser::CreateImageList() |
|
{ |
|
HIMAGELIST list; |
|
|
|
list = ImageList_Create( CX_ICON, CY_ICON, |
|
FALSE, NUM_IMAGES, 0 ); |
|
|
|
// Load the icon resources, and add the icons to the image list. |
|
HICON hicon; |
|
int slot; |
|
#if defined( DBGFLAG_ASSERT ) |
|
int c = 0; |
|
#endif |
|
|
|
/* |
|
hicon = LoadIcon( GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WORKSPACE)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon( GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WORKSPACE_CHECKEDOUT)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_PROJECT)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_PROJECT_CHECKEDOUT)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SCENE)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
*/ |
|
|
|
// hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SCENE_CHECKEDOUT)); |
|
// slot = ImageList_AddIcon(list, hicon); |
|
// Assert( slot == c++ ); |
|
// DeleteObject( hicon ); |
|
|
|
/* |
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_VCD)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_VCD_CHECKEDOUT )); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
*/ |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WAV)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
/* |
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WAV_CHECKEDOUT)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SPEAK)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
|
|
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SPEAK_CHECKEDOUT)); |
|
slot = ImageList_AddIcon(list, hicon); |
|
Assert( slot == c++ ); |
|
DeleteObject( hicon ); |
|
*/ |
|
|
|
return list; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWaveBrowser::OnDelete() |
|
{ |
|
RemoveAllSounds(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *event - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CWaveBrowser::handleEvent( mxEvent *event ) |
|
{ |
|
int iret = 0; |
|
|
|
if ( HandleToolEvent( event ) ) |
|
{ |
|
return iret; |
|
} |
|
|
|
switch ( event->event ) |
|
{ |
|
default: |
|
break; |
|
case mxEvent::Action: |
|
{ |
|
iret = 1; |
|
switch ( event->action ) |
|
{ |
|
default: |
|
{ |
|
iret = 0; |
|
} |
|
break; |
|
case IDC_SB_LISTVIEW: |
|
{ |
|
SetActiveTool( this ); |
|
|
|
bool rightmouse = ( event->flags == mxEvent::RightClicked ) ? true : false; |
|
bool doubleclicked = ( event->flags == mxEvent::DoubleClicked ) ? true : false; |
|
|
|
if ( rightmouse ) |
|
{ |
|
ShowContextMenu(); |
|
} |
|
else if ( doubleclicked ) |
|
{ |
|
if ( m_pListView->getNumSelected() == 1 ) |
|
{ |
|
int index = m_pListView->getNextSelectedItem( -1 ); |
|
if ( index >= 0 ) |
|
{ |
|
CWaveFile *wav = (CWaveFile *)m_pListView->getUserData( index, 0 ); |
|
if ( wav ) |
|
{ |
|
wav->Play(); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
case IDC_SB_FILETREE: |
|
{ |
|
SetActiveTool( this ); |
|
|
|
PopulateTree( m_pFileTree->GetSelectedPath() ); |
|
} |
|
break; |
|
case IDC_SB_PLAY: |
|
{ |
|
OnPlay(); |
|
} |
|
break; |
|
} |
|
} |
|
break; |
|
case mxEvent::Size: |
|
{ |
|
int optionsh = 20; |
|
|
|
m_pOptions->setBounds( 0, 0, w2(), optionsh ); |
|
|
|
int filetreewidth = 175; |
|
|
|
m_pFileTree->setBounds( 0, optionsh, filetreewidth, h2() - optionsh ); |
|
m_pListView->setBounds( filetreewidth, optionsh, w2() - filetreewidth, h2() - optionsh ); |
|
|
|
iret = 1; |
|
} |
|
break; |
|
case mxEvent::Close: |
|
{ |
|
iret = 1; |
|
} |
|
break; |
|
} |
|
|
|
return iret; |
|
} |
|
|
|
static bool NameLessFunc( CWaveFile *const& name1, CWaveFile *const& name2 ) |
|
{ |
|
if ( Q_stricmp( name1->GetName(), name2->GetName() ) < 0 ) |
|
return true; |
|
return false; |
|
} |
|
|
|
#define SOUND_PREFIX_LEN 6 |
|
//----------------------------------------------------------------------------- |
|
// Finds all .wav files in a particular directory |
|
//----------------------------------------------------------------------------- |
|
bool CWaveBrowser::LoadWaveFilesInDirectory( CUtlDict< CWaveFile *, int >& soundlist, char const* pDirectoryName, int nDirectoryNameLen ) |
|
{ |
|
Assert( Q_strnicmp( pDirectoryName, "sound", 5 ) == 0 ); |
|
|
|
char *pWildCard; |
|
pWildCard = ( char * )stackalloc( nDirectoryNameLen + 7 ); |
|
Q_snprintf( pWildCard, nDirectoryNameLen + 7, "%s/*.wav", pDirectoryName ); |
|
|
|
if ( !filesystem ) |
|
{ |
|
return false; |
|
} |
|
|
|
FileFindHandle_t findHandle; |
|
const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle ); |
|
while( pFileName ) |
|
{ |
|
if( !filesystem->FindIsDirectory( findHandle ) ) |
|
{ |
|
// Strip off the 'sound/' part of the name. |
|
char *pFileNameWithPath; |
|
int nAllocSize = nDirectoryNameLen + Q_strlen(pFileName) + 2; |
|
pFileNameWithPath = (char *)stackalloc( nAllocSize ); |
|
Q_snprintf( pFileNameWithPath, nAllocSize, "%s/%s", &pDirectoryName[SOUND_PREFIX_LEN], pFileName ); |
|
Q_strnlwr( pFileNameWithPath, nAllocSize ); |
|
|
|
CWaveFile *wav = new CWaveFile( pFileNameWithPath ); |
|
soundlist.Insert( pFileNameWithPath, wav ); |
|
|
|
/* |
|
if ( !(soundlist.Count() % 500 ) ) |
|
{ |
|
Con_Printf( "CWaveBrowser: loaded %i sounds\n", soundlist.Count() ); |
|
} |
|
*/ |
|
} |
|
pFileName = filesystem->FindNext( findHandle ); |
|
} |
|
|
|
m_pFileTree->FindOrAddSubdirectory( &pDirectoryName[ SOUND_PREFIX_LEN ] ); |
|
|
|
filesystem->FindClose( findHandle ); |
|
return true; |
|
} |
|
|
|
bool CWaveBrowser::InitDirectoryRecursive( CUtlDict< CWaveFile *, int >& soundlist, char const* pDirectoryName ) |
|
{ |
|
// Compute directory name length |
|
int nDirectoryNameLen = Q_strlen( pDirectoryName ); |
|
|
|
if (!LoadWaveFilesInDirectory( soundlist, pDirectoryName, nDirectoryNameLen ) ) |
|
return false; |
|
|
|
char *pWildCard = ( char * )stackalloc( nDirectoryNameLen + 4 ); |
|
strcpy(pWildCard, pDirectoryName); |
|
strcat(pWildCard, "/*."); |
|
int nPathStrLen = nDirectoryNameLen + 1; |
|
|
|
FileFindHandle_t findHandle; |
|
const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle ); |
|
while( pFileName ) |
|
{ |
|
if ((pFileName[0] != '.') || (pFileName[1] != '.' && pFileName[1] != 0)) |
|
{ |
|
if( filesystem->FindIsDirectory( findHandle ) ) |
|
{ |
|
int fileNameStrLen = Q_strlen( pFileName ); |
|
char *pFileNameWithPath = ( char * )stackalloc( nPathStrLen + fileNameStrLen + 1 ); |
|
memcpy( pFileNameWithPath, pWildCard, nPathStrLen ); |
|
pFileNameWithPath[nPathStrLen] = '\0'; |
|
strcat( pFileNameWithPath, pFileName ); |
|
|
|
if (!InitDirectoryRecursive( soundlist, pFileNameWithPath )) |
|
return false; |
|
} |
|
} |
|
pFileName = filesystem->FindNext( findHandle ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CWaveBrowser::LoadAllSounds() |
|
{ |
|
RemoveAllSounds(); |
|
|
|
Con_Printf( "Building list of all .wavs in sound/ folder\n" ); |
|
|
|
InitDirectoryRecursive( m_AllSounds, "sound" ); |
|
|
|
m_pFileTree->PopulateTree(); |
|
} |
|
|
|
void CWaveBrowser::RemoveAllSounds() |
|
{ |
|
int c = m_AllSounds.Count(); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
CWaveFile *wav = m_AllSounds[ i ]; |
|
delete wav; |
|
} |
|
|
|
m_AllSounds.RemoveAll(); |
|
m_Scripts.RemoveAll(); |
|
m_CurrentSelection.RemoveAll(); |
|
|
|
m_pFileTree->Clear(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWaveBrowser::PopulateTree( char const *subdirectory ) |
|
{ |
|
int i; |
|
|
|
CUtlRBTree< CWaveFile *, int > m_Sorted( 0, 0, NameLessFunc ); |
|
|
|
bool check_load_sentence_data = false; |
|
|
|
char const *texttofind = NULL; |
|
|
|
if ( m_bTextSearch ) |
|
{ |
|
subdirectory = NULL; |
|
texttofind = GetSearchString(); |
|
} |
|
|
|
int len = 0; |
|
if ( subdirectory ) |
|
{ |
|
len = Q_strlen( subdirectory ); |
|
check_load_sentence_data = ( Q_strstr( subdirectory, "/" ) || subdirectory[0] ) ? true : false; |
|
} |
|
|
|
int c = m_AllSounds.Count(); |
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
CWaveFile *wav = m_AllSounds[ i ]; |
|
char const *name = wav->GetName(); |
|
|
|
if ( subdirectory ) |
|
{ |
|
if ( Q_strnicmp( subdirectory, wav->GetName(), len ) ) |
|
continue; |
|
} |
|
|
|
if ( m_bTextSearch && texttofind ) |
|
{ |
|
if ( !Q_stristr( name, texttofind ) ) |
|
continue; |
|
} |
|
|
|
m_Sorted.Insert( wav ); |
|
} |
|
|
|
char prevSelectedName[ 512 ]; |
|
prevSelectedName[ 0 ] = 0; |
|
if ( m_pListView->getNumSelected() == 1 ) |
|
{ |
|
int selectedItem = m_pListView->getNextSelectedItem( 0 ); |
|
if ( selectedItem >= 0 ) |
|
{ |
|
// Grab wave name of previously selected item |
|
Q_strcpy( prevSelectedName, m_pListView->getLabel( selectedItem, 0 ) ); |
|
} |
|
} |
|
|
|
// Repopulate tree |
|
m_pListView->removeAll(); |
|
|
|
int loadcount = 0; |
|
|
|
m_pListView->setDrawingEnabled( false ); |
|
|
|
int selectedSlot = -1; |
|
|
|
CUtlVector< CWaveFile * > list; |
|
|
|
|
|
for ( i = m_Sorted.FirstInorder(); i != m_Sorted.InvalidIndex(); i = m_Sorted.NextInorder( i ) ) |
|
{ |
|
CWaveFile *wav = m_Sorted[ i ]; |
|
char const *name = wav->GetName(); |
|
|
|
int slot = m_pListView->add( name ); |
|
|
|
if ( !Q_stricmp( prevSelectedName, name ) ) |
|
{ |
|
selectedSlot = slot; |
|
} |
|
|
|
if ( ( check_load_sentence_data || m_bTextSearch ) && |
|
!wav->HasLoadedSentenceInfo() && !wav->IsAsyncLoading() ) |
|
{ |
|
wav->SetAsyncLoading( true ); |
|
list.AddToTail( wav ); |
|
} |
|
|
|
// m_pListView->setImage( slot, COL_WAV, wav->GetIconIndex() ); |
|
m_pListView->setUserData( slot, COL_WAV, (void *)wav ); |
|
|
|
if ( wav->HasLoadedSentenceInfo() ) |
|
{ |
|
m_pListView->setLabel( slot, COL_DUCKED, wav->GetVoiceDuck() ? "yes" : "no" ); |
|
m_pListView->setLabel( slot, COL_PHONEMES, wav->GetPhonemeCount() || wav->GetWordCount() ? va( "%i [ %i ]", wav->GetWordCount(), wav->GetPhonemeCount() ) : "" ); |
|
m_pListView->setLabel( slot, COL_SENTENCE, wav->GetSentenceText() ); |
|
} |
|
else |
|
{ |
|
m_pListView->setLabel( slot, COL_SENTENCE, "(loading...)" ); |
|
} |
|
|
|
++loadcount; |
|
} |
|
|
|
m_pListView->setDrawingEnabled( true ); |
|
|
|
if ( selectedSlot != -1 ) |
|
{ |
|
m_pListView->setSelected( selectedSlot, true ); |
|
m_pListView->scrollToItem( selectedSlot ); |
|
} |
|
|
|
if ( list.Count() > 0 ) |
|
{ |
|
fileloader->AddWaveFilesToThread( list ); |
|
} |
|
|
|
// Con_Printf( "CWaveBrowser: selected %i sounds\n", loadcount ); |
|
} |
|
|
|
void CWaveBrowser::RepopulateTree() |
|
{ |
|
PopulateTree( m_pFileTree->GetSelectedPath() ); |
|
} |
|
|
|
void CWaveBrowser::BuildSelectionList( CUtlVector< CWaveFile * >& selected ) |
|
{ |
|
selected.RemoveAll(); |
|
|
|
int idx = -1; |
|
do |
|
{ |
|
idx = m_pListView->getNextSelectedItem( idx ); |
|
if ( idx != -1 ) |
|
{ |
|
CWaveFile *wav = (CWaveFile *)m_pListView->getUserData( idx, 0 ); |
|
if ( wav ) |
|
{ |
|
selected.AddToTail( wav ); |
|
} |
|
} |
|
} while ( idx != -1 ); |
|
|
|
} |
|
|
|
void CWaveBrowser::ShowContextMenu( void ) |
|
{ |
|
SetActiveTool( this ); |
|
|
|
BuildSelectionList( m_CurrentSelection ); |
|
if ( m_CurrentSelection.Count() <= 0 ) |
|
return; |
|
|
|
POINT pt; |
|
GetCursorPos( &pt ); |
|
ScreenToClient( (HWND)getHandle(), &pt ); |
|
|
|
// New scene, edit comments |
|
mxPopupMenu *pop = new mxPopupMenu(); |
|
|
|
if ( m_CurrentSelection.Count() == 1 ) |
|
{ |
|
pop->add ("&Play", IDC_SB_PLAY ); |
|
// pop->addSeparator(); |
|
} |
|
|
|
// pop->add( "Import Sentence Data", IDC_SB_IMPORTSENTENCE ); |
|
// pop->add( "Export Sentence Data", IDC_SB_EXPORTSENTENCE ); |
|
|
|
pop->popup( this, pt.x, pt.y ); |
|
} |
|
|
|
void CWaveBrowser::OnPlay() |
|
{ |
|
SetActiveTool( this ); |
|
|
|
BuildSelectionList( m_CurrentSelection ); |
|
if ( m_CurrentSelection.Count() == 1 ) |
|
{ |
|
CWaveFile *wav = m_CurrentSelection[ 0 ]; |
|
if ( wav ) |
|
{ |
|
wav->Play(); |
|
} |
|
} |
|
} |
|
|
|
static void SplitFileName( char const *in, char *path, int maxpath, char *filename, int maxfilename ) |
|
{ |
|
char drive[_MAX_DRIVE]; |
|
char dir[_MAX_DIR]; |
|
char fname[_MAX_FNAME]; |
|
char ext[_MAX_EXT]; |
|
|
|
_splitpath( in, drive, dir, fname, ext ); |
|
|
|
if ( dir[0] ) |
|
{ |
|
Q_snprintf( path, maxpath, "\\%s", dir ); |
|
} |
|
else |
|
{ |
|
path[0] = 0; |
|
} |
|
Q_snprintf( filename, maxfilename, "%s%s", fname, ext ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *se - |
|
//----------------------------------------------------------------------------- |
|
void CWaveBrowser::JumpToItem( CWaveFile *wav ) |
|
{ |
|
|
|
SetActiveTool( this ); |
|
|
|
char path[ 256 ]; |
|
char filename[ 256 ]; |
|
|
|
SplitFileName( wav->GetFileName(), path, sizeof( path ), filename, sizeof( filename ) ); |
|
|
|
char *usepath = path + Q_strlen( "/sound/" ); |
|
PopulateTree( usepath ); |
|
|
|
int idx = 0; |
|
int c = m_pListView->getItemCount(); |
|
for ( ; idx < c; idx++ ) |
|
{ |
|
CWaveFile *item = (CWaveFile *)m_pListView->getUserData( idx, 0 ); |
|
if ( !Q_stricmp( item->GetFileName(), wav->GetFileName() ) ) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
if ( idx < c ) |
|
{ |
|
m_pListView->scrollToItem( idx ); |
|
} |
|
} |
|
|
|
CWaveFile *CWaveBrowser::FindEntry( char const *wavname, bool jump /*= false*/ ) |
|
{ |
|
int idx = m_AllSounds.Find( wavname ); |
|
if ( idx != m_AllSounds.InvalidIndex() ) |
|
{ |
|
CWaveFile *wav = m_AllSounds[ idx ]; |
|
if ( jump ) |
|
{ |
|
JumpToItem( wav ); |
|
} |
|
|
|
return wav; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
int CWaveBrowser::GetSoundCount() const |
|
{ |
|
return m_AllSounds.Count(); |
|
} |
|
|
|
CWaveFile *CWaveBrowser::GetSound( int index ) |
|
{ |
|
if ( index < 0 || index >= (int)m_AllSounds.Count() ) |
|
return NULL; |
|
|
|
return m_AllSounds[ index ]; |
|
} |
|
|
|
|
|
void CWaveBrowser::OnSearch() |
|
{ |
|
SetActiveTool( this ); |
|
|
|
m_bTextSearch = true; |
|
|
|
PopulateTree( GetSearchString()); |
|
} |
|
|
|
void CWaveBrowser::OnCancelSearch() |
|
{ |
|
SetActiveTool( this ); |
|
|
|
m_bTextSearch = false; |
|
|
|
PopulateTree( m_pFileTree->GetSelectedPath() ); |
|
} |
|
|
|
char const *CWaveBrowser::GetSearchString() |
|
{ |
|
return m_pOptions->GetSearchString(); |
|
} |
|
|
|
void CWaveBrowser::Think( float dt ) |
|
{ |
|
int pending = fileloader->GetPendingLoadCount(); |
|
if ( pending != m_nPrevProcessed ) |
|
{ |
|
m_nPrevProcessed = pending; |
|
|
|
// Put into suffix of window title |
|
if ( pending == 0 ) |
|
{ |
|
SetSuffix( "" ); |
|
} |
|
else |
|
{ |
|
SetSuffix( va( " - %i", pending ) ); |
|
} |
|
} |
|
|
|
int c = fileloader->ProcessCompleted(); |
|
if ( c > 0 ) |
|
{ |
|
RepopulateTree(); |
|
} |
|
} |
|
|
|
void CWaveBrowser::SetEvent( CChoreoEvent *event ) |
|
{ |
|
if ( event->GetType() != CChoreoEvent::SPEAK ) |
|
return; |
|
|
|
SetCurrent( FacePoser_TranslateSoundName( event->GetParameters() ) ); |
|
} |
|
|
|
void CWaveBrowser::SetCurrent( char const *filename ) |
|
{ |
|
// Get sound name and look up .wav from it |
|
char const *p = filename; |
|
if ( p && |
|
( !Q_strnicmp( p, "sound/", 6 ) || !Q_strnicmp( p, "sound\\", 6 ) ) ) |
|
{ |
|
p += 6; |
|
} |
|
|
|
char fn[ 512 ]; |
|
Q_strncpy( fn, p, sizeof( fn ) ); |
|
Q_FixSlashes( fn ); |
|
|
|
int i; |
|
int c = m_pListView->getItemCount(); |
|
|
|
for ( i = 0; i < c; ++i ) |
|
{ |
|
CWaveFile *wav = reinterpret_cast< CWaveFile * >( m_pListView->getUserData( i, COL_WAV ) ); |
|
if ( !wav ) |
|
continue; |
|
|
|
char fixed[ 512 ]; |
|
Q_strncpy( fixed, wav->GetName(), sizeof( fixed ) ); |
|
Q_FixSlashes( fixed ); |
|
|
|
if ( !Q_stricmp( fixed, fn ) ) |
|
{ |
|
m_pListView->scrollToItem( i ); |
|
m_pListView->setSelected( i, true ); |
|
} |
|
else |
|
{ |
|
m_pListView->setSelected( i, false ); |
|
} |
|
} |
|
}
|
|
|