summaryrefslogtreecommitdiff
path: root/engine/audio/private/snd_wave_temp.cpp
blob: b1c67023d3be483e8a5edac851b02ae52bf7ebf1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Create an output wave stream.  Used to record audio for in-engine movies or
// mixer debugging.
//
//=====================================================================================//

#include "audio_pch.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

extern IFileSystem *g_pFileSystem;
// FIXME: shouldn't this API be part of IFileSystem?
extern bool COM_CopyFile( const char *netpath, const char *cachepath );

// Create a wave file
void WaveCreateTmpFile( const char *filename, int rate, int bits, int nChannels )
{
	char tmpfilename[MAX_PATH];
	Q_StripExtension( filename, tmpfilename, sizeof( tmpfilename ) );
	Q_DefaultExtension( tmpfilename, ".WAV", sizeof( tmpfilename ) );

	FileHandle_t file;
	file = g_pFileSystem->Open( tmpfilename, "wb" );
	if ( file == FILESYSTEM_INVALID_HANDLE )
		return;

	int chunkid = LittleLong( RIFF_ID );
	int chunksize = LittleLong( 0 );
	g_pFileSystem->Write( &chunkid, sizeof(int), file );
	g_pFileSystem->Write( &chunksize, sizeof(int), file );

	chunkid = LittleLong( RIFF_WAVE );
	g_pFileSystem->Write( &chunkid, sizeof(int), file );

	// create a 16-bit PCM stereo output file
	PCMWAVEFORMAT fmt = { { 0 } };
	fmt.wf.wFormatTag = LittleWord( (short)WAVE_FORMAT_PCM );
	fmt.wf.nChannels = LittleWord( (short)nChannels );
	fmt.wf.nSamplesPerSec = LittleDWord( rate );
	fmt.wf.nAvgBytesPerSec = LittleDWord( rate * bits * nChannels / 8 );
	fmt.wf.nBlockAlign = LittleWord( (short)( 2 * nChannels) );
	fmt.wBitsPerSample = LittleWord( (short)bits );

	chunkid = LittleLong( WAVE_FMT );
	chunksize = LittleLong( sizeof(fmt) );
	g_pFileSystem->Write( &chunkid, sizeof(int), file );
	g_pFileSystem->Write( &chunksize, sizeof(int), file );
	g_pFileSystem->Write( &fmt, sizeof( PCMWAVEFORMAT ), file );

	chunkid = LittleLong( WAVE_DATA );
	chunksize = LittleLong( 0 );
	g_pFileSystem->Write( &chunkid, sizeof(int), file );
	g_pFileSystem->Write( &chunksize, sizeof(int), file );

	g_pFileSystem->Close( file );
}

void WaveAppendTmpFile( const char *filename, void *pBuffer, int sampleBits, int numSamples )
{
	char tmpfilename[MAX_PATH];
	Q_StripExtension( filename, tmpfilename, sizeof( tmpfilename ) );
	Q_DefaultExtension( tmpfilename, ".WAV", sizeof( tmpfilename ) );

	FileHandle_t file;
	file = g_pFileSystem->Open( tmpfilename, "r+b" );
	if ( file == FILESYSTEM_INVALID_HANDLE )
		return;

	g_pFileSystem->Seek( file, 0, FILESYSTEM_SEEK_TAIL );

	if ( IsX360() && sampleBits == 16 )
	{
		short *pSwapped = (short * )_alloca( numSamples * sampleBits/8 );
		for ( int i=0; i<numSamples; i++ )
		{
			pSwapped[i] = LittleShort( ((short*)pBuffer)[i] );
		}
		g_pFileSystem->Write( pSwapped, numSamples * sizeof( short ), file );
	}
	else
	{
		g_pFileSystem->Write( pBuffer, numSamples * sampleBits/8, file );
	}

	g_pFileSystem->Close( file );
}

void WaveFixupTmpFile( const char *filename )
{
	char tmpfilename[MAX_PATH];
	Q_StripExtension( filename, tmpfilename, sizeof( tmpfilename ) );
	Q_DefaultExtension( tmpfilename, ".WAV", sizeof( tmpfilename ) );

	FileHandle_t file;
	file = g_pFileSystem->Open( tmpfilename, "r+b" );
	if ( FILESYSTEM_INVALID_HANDLE == file )
	{
		Warning( "WaveFixupTmpFile( '%s' ) failed to open file for editing\n", tmpfilename );
		return;
	}
	
	// file size goes in RIFF chunk
	int size = g_pFileSystem->Size( file ) - 2*sizeof( int );
	// offset to data chunk
	int headerSize = (sizeof(int)*5 + sizeof(PCMWAVEFORMAT));
	// size of data chunk
	int dataSize = size - headerSize;

	size = LittleLong( size );
	g_pFileSystem->Seek( file, sizeof( int ), FILESYSTEM_SEEK_HEAD );
	g_pFileSystem->Write( &size, sizeof( int ), file );

	// skip the header and the 4-byte chunk tag and write the size
	dataSize = LittleLong( dataSize );
	g_pFileSystem->Seek( file, headerSize+sizeof( int ), FILESYSTEM_SEEK_HEAD );
	g_pFileSystem->Write( &dataSize, sizeof( int ), file );

	g_pFileSystem->Close( file );
}

CON_COMMAND( movie_fixwave, "Fixup corrupted .wav file if engine crashed during startmovie/endmovie, etc." )
{
	if ( args.ArgC() != 2 )
	{
		Msg ("Usage: movie_fixwave wavname\n");
		return;
	}

	char const *wavname = args.Arg( 1 );
	if ( !g_pFileSystem->FileExists( wavname ) )
	{
		Warning( "movie_fixwave: File '%s' does not exist\n", wavname );
		return;
	}

	char tmpfilename[256];
	Q_StripExtension( wavname, tmpfilename, sizeof( tmpfilename ) );
	Q_strncat( tmpfilename, "_fixed", sizeof( tmpfilename ), COPY_ALL_CHARACTERS );
	Q_DefaultExtension( tmpfilename, ".wav", sizeof( tmpfilename ) );

	// Now copy the file
	Msg( "Copying '%s' to '%s'\n", wavname, tmpfilename );
	COM_CopyFile( wavname, tmpfilename );

	Msg( "Performing fixup on '%s'\n", tmpfilename );
	WaveFixupTmpFile( tmpfilename );
}