Skip to content

Commit b25b5f9

Browse files
committed
bass: allow encoding to disk
1 parent 341d27f commit b25b5f9

File tree

7 files changed

+301
-7
lines changed

7 files changed

+301
-7
lines changed

source/detours.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
SymbolFinder Detour::symfinder;
1414
void* Detour::GetFunction(void* pModule, Symbol pSymbol)
1515
{
16+
if (!pModule)
17+
return NULL;
18+
1619
return symfinder.Resolve(pModule, pSymbol.name.c_str(), pSymbol.length);
1720
}
1821

source/detours.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
#define DLL_LoadModule(name, _) LoadLibrary(name)
2323
#define DLL_UnloadModule(handle) FreeLibrary((DLL_Handle)handle)
2424
#define DLL_GetAddress(handle, name) GetProcAddress((DLL_Handle)handle, name)
25+
#define DLL_LASTERROR "LINUXONLY"
2526
#else
2627
#include <dlfcn.h>
2728
#define DLL_Handle void*
2829
#define DLL_LoadModule(name, type) dlopen(name, type)
2930
#define DLL_UnloadModule(handle) dlclose(handle)
3031
#define DLL_GetAddress(handle, name) dlsym(handle, name)
32+
#define DLL_LASTERROR dlerror()
3133
#endif
3234
#endif
3335

source/modules/bass.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,23 @@ LUA_FUNCTION_STATIC(IGModAudioChannel_Restart)
334334
return 0;
335335
}
336336

337+
LUA_FUNCTION_STATIC(IGModAudioChannel_EncodeToDisk)
338+
{
339+
IGModAudioChannel* channel = Get_IGModAudioChannel(LUA, 1, true);
340+
341+
const char* pErrorMsg = channel->EncodeToDisk(LUA->CheckString(1), LUA->CheckString(2), (unsigned long)LUA->CheckNumber(3));
342+
if (!pErrorMsg)
343+
{ // Success
344+
LUA->PushBool(true);
345+
LUA->PushNil();
346+
return 2;
347+
}
348+
349+
LUA->PushBool(false);
350+
LUA->PushString(pErrorMsg);
351+
return 2;
352+
}
353+
337354
LUA_FUNCTION_STATIC(bass_PlayFile)
338355
{
339356
const char* filePath = LUA->CheckString(1);
@@ -478,6 +495,9 @@ void CBassModule::LuaInit(GarrysMod::Lua::ILuaInterface* pLua, bool bServerInit)
478495
Util::AddFunc(pLua, IGModAudioChannel_NotImplemented, "SetPos");
479496
Util::AddFunc(pLua, IGModAudioChannel_NotImplemented, "Get3DEnabled");
480497
Util::AddFunc(pLua, IGModAudioChannel_NotImplemented, "Set3DEnabled");
498+
499+
// HolyLib specific
500+
Util::AddFunc(pLua, IGModAudioChannel_EncodeToDisk, "EncodeToDisk");
481501
pLua->Pop(1);
482502

483503
Util::StartTable(pLua);

source/sourcesdk/IGmod_Audio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class IGModAudioChannel
5656
virtual void Set3DEnabled( bool ) = 0;
5757
virtual bool Get3DEnabled() = 0;
5858
virtual void Restart() = 0;
59+
// HolyLib specific
60+
virtual const char* EncodeToDisk( const char* pFileName, const char* pCommand, unsigned long nFlags ) = 0; // Uses the "DATA" path for writes! Returns NULL on success, else the error message
5961
};
6062

6163
class IAudioStreamEvent;

source/sourcesdk/bassenc.h

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
BASSenc 2.4 C/C++ header file
3+
Copyright (c) 2003-2022 Un4seen Developments Ltd.
4+
5+
See the BASSENC.CHM file for more detailed documentation
6+
*/
7+
8+
#ifndef BASSENC_H
9+
#define BASSENC_H
10+
11+
#include "bass.h"
12+
13+
#if BASSVERSION!=0x204
14+
#error conflicting BASS and BASSenc versions
15+
#endif
16+
17+
#ifdef __OBJC__
18+
typedef int BOOL32;
19+
#define BOOL BOOL32 // override objc's BOOL
20+
#endif
21+
22+
#ifdef __cplusplus
23+
extern "C" {
24+
#endif
25+
26+
#ifndef BASSENCDEF
27+
#define BASSENCDEF(f) WINAPI f
28+
#endif
29+
30+
typedef DWORD HENCODE; // encoder handle
31+
32+
// Additional error codes returned by BASS_ErrorGetCode
33+
#define BASS_ERROR_ACM_CANCEL 2000 // ACM codec selection cancelled
34+
#define BASS_ERROR_CAST_DENIED 2100 // access denied (invalid password)
35+
#define BASS_ERROR_SERVER_CERT 2101 // missing/invalid certificate
36+
37+
// Additional BASS_SetConfig options
38+
#define BASS_CONFIG_ENCODE_PRIORITY 0x10300
39+
#define BASS_CONFIG_ENCODE_QUEUE 0x10301
40+
#define BASS_CONFIG_ENCODE_CAST_TIMEOUT 0x10310
41+
42+
// Additional BASS_SetConfigPtr options
43+
#define BASS_CONFIG_ENCODE_ACM_LOAD 0x10302
44+
#define BASS_CONFIG_ENCODE_CAST_PROXY 0x10311
45+
#define BASS_CONFIG_ENCODE_CAST_BIND 0x10312
46+
#define BASS_CONFIG_ENCODE_SERVER_CERT 0x10320
47+
#define BASS_CONFIG_ENCODE_SERVER_KEY 0x10321
48+
49+
// BASS_Encode_Start flags
50+
#define BASS_ENCODE_NOHEAD 1 // don't send a WAV header to the encoder
51+
#define BASS_ENCODE_FP_8BIT 2 // convert floating-point sample data to 8-bit integer
52+
#define BASS_ENCODE_FP_16BIT 4 // convert floating-point sample data to 16-bit integer
53+
#define BASS_ENCODE_FP_24BIT 6 // convert floating-point sample data to 24-bit integer
54+
#define BASS_ENCODE_FP_32BIT 8 // convert floating-point sample data to 32-bit integer
55+
#define BASS_ENCODE_FP_AUTO 14 // convert floating-point sample data back to channel's format
56+
#define BASS_ENCODE_BIGEND 16 // big-endian sample data
57+
#define BASS_ENCODE_PAUSE 32 // start encording paused
58+
#define BASS_ENCODE_PCM 64 // write PCM sample data (no encoder)
59+
#define BASS_ENCODE_RF64 128 // send an RF64 header
60+
#define BASS_ENCODE_MONO 0x100 // convert to mono (if not already)
61+
#define BASS_ENCODE_QUEUE 0x200 // queue data to feed encoder asynchronously
62+
#define BASS_ENCODE_WFEXT 0x400 // WAVEFORMATEXTENSIBLE "fmt" chunk
63+
#define BASS_ENCODE_CAST_NOLIMIT 0x1000 // don't limit casting data rate
64+
#define BASS_ENCODE_LIMIT 0x2000 // limit data rate to real-time
65+
#define BASS_ENCODE_AIFF 0x4000 // send an AIFF header rather than WAV
66+
#define BASS_ENCODE_DITHER 0x8000 // apply dither when converting floating-point sample data to integer
67+
#define BASS_ENCODE_AUTOFREE 0x40000 // free the encoder when the channel is freed
68+
69+
// BASS_Encode_GetACMFormat flags
70+
#define BASS_ACM_DEFAULT 1 // use the format as default selection
71+
#define BASS_ACM_RATE 2 // only list formats with same sample rate as the source channel
72+
#define BASS_ACM_CHANS 4 // only list formats with same number of channels (eg. mono/stereo)
73+
#define BASS_ACM_SUGGEST 8 // suggest a format (HIWORD=format tag)
74+
75+
// BASS_Encode_GetCount counts
76+
#define BASS_ENCODE_COUNT_IN 0 // sent to encoder
77+
#define BASS_ENCODE_COUNT_OUT 1 // received from encoder
78+
#define BASS_ENCODE_COUNT_CAST 2 // sent to cast server
79+
#define BASS_ENCODE_COUNT_QUEUE 3 // queued
80+
#define BASS_ENCODE_COUNT_QUEUE_LIMIT 4 // queue limit
81+
#define BASS_ENCODE_COUNT_QUEUE_FAIL 5 // failed to queue
82+
#define BASS_ENCODE_COUNT_IN_FP 6 // sent to encoder before floating-point conversion
83+
84+
// BASS_Encode_CastInit content MIME types
85+
#define BASS_ENCODE_TYPE_MP3 "audio/mpeg"
86+
#define BASS_ENCODE_TYPE_OGG "audio/ogg"
87+
#define BASS_ENCODE_TYPE_AAC "audio/aacp"
88+
89+
// BASS_Encode_CastInit flags
90+
#define BASS_ENCODE_CAST_PUBLIC 1 // add to public directory
91+
#define BASS_ENCODE_CAST_PUT 2 // use PUT method
92+
#define BASS_ENCODE_CAST_SSL 4 // use SSL/TLS encryption
93+
94+
// BASS_Encode_CastGetStats types
95+
#define BASS_ENCODE_STATS_SHOUT 0 // Shoutcast stats
96+
#define BASS_ENCODE_STATS_ICE 1 // Icecast mount-point stats
97+
#define BASS_ENCODE_STATS_ICESERV 2 // Icecast server stats
98+
99+
// BASS_Encode_ServerInit flags
100+
#define BASS_ENCODE_SERVER_NOHTTP 1 // no HTTP headers
101+
#define BASS_ENCODE_SERVER_META 2 // Shoutcast metadata
102+
#define BASS_ENCODE_SERVER_SSL 4 // support SSL/TLS encryption
103+
#define BASS_ENCODE_SERVER_SSLONLY 8 // require SSL/TLS encryption
104+
105+
typedef void (CALLBACK ENCODEPROC)(HENCODE handle, DWORD channel, const void *buffer, DWORD length, void *user);
106+
/* Encoding callback function.
107+
handle : The encoder
108+
channel: The channel handle
109+
buffer : Buffer containing the encoded data
110+
length : Number of bytes
111+
user : The 'user' parameter value given when starting the encoder */
112+
113+
typedef void (CALLBACK ENCODEPROCEX)(HENCODE handle, DWORD channel, const void *buffer, DWORD length, QWORD offset, void *user);
114+
/* Encoding callback function with offset info.
115+
handle : The encoder
116+
channel: The channel handle
117+
buffer : Buffer containing the encoded data
118+
length : Number of bytes
119+
offset : File offset of the data
120+
user : The 'user' parameter value given when starting the encoder */
121+
122+
typedef DWORD (CALLBACK ENCODERPROC)(HENCODE handle, DWORD channel, void *buffer, DWORD length, DWORD maxout, void *user);
123+
/* Encoder callback function.
124+
handle : The encoder
125+
channel: The channel handle
126+
buffer : Buffer containing the PCM data (input) and receiving the encoded data (output)
127+
length : Number of bytes in (-1=closing)
128+
maxout : Maximum number of bytes out
129+
user : The 'user' parameter value given when calling BASS_Encode_StartUser
130+
RETURN : The amount of encoded data (-1=stop) */
131+
132+
typedef BOOL (CALLBACK ENCODECLIENTPROC)(HENCODE handle, BOOL connect, const char *client, char *headers, void *user);
133+
/* Client connection notification callback function.
134+
handle : The encoder
135+
connect: TRUE/FALSE=client is connecting/disconnecting
136+
client : The client's address (xxx.xxx.xxx.xxx:port)
137+
headers: Request headers (optionally response headers on return)
138+
user : The 'user' parameter value given when calling BASS_Encode_ServerInit
139+
RETURN : TRUE/FALSE=accept/reject connection (ignored if connect=FALSE) */
140+
141+
typedef void (CALLBACK ENCODENOTIFYPROC)(HENCODE handle, DWORD status, void *user);
142+
/* Encoder death notification callback function.
143+
handle : The encoder
144+
status : Notification (BASS_ENCODE_NOTIFY_xxx)
145+
user : The 'user' parameter value given when calling BASS_Encode_SetNotify */
146+
147+
// Encoder notifications
148+
#define BASS_ENCODE_NOTIFY_ENCODER 1 // encoder died
149+
#define BASS_ENCODE_NOTIFY_CAST 2 // cast server connection died
150+
#define BASS_ENCODE_NOTIFY_SERVER 3 // server died
151+
#define BASS_ENCODE_NOTIFY_CAST_TIMEOUT 0x10000 // cast timeout
152+
#define BASS_ENCODE_NOTIFY_QUEUE_FULL 0x10001 // queue is out of space
153+
#define BASS_ENCODE_NOTIFY_FREE 0x10002 // encoder has been freed
154+
155+
typedef DWORD BASSENCDEF(BASS_Encode_GetVersion)(void);
156+
157+
typedef HENCODE BASSENCDEF(BASS_Encode_Start)(DWORD handle, const char *cmdline, DWORD flags, ENCODEPROC *proc, void *user);
158+
typedef HENCODE BASSENCDEF(BASS_Encode_StartLimit)(DWORD handle, const char *cmdline, DWORD flags, ENCODEPROC *proc, void *user, DWORD limit);
159+
typedef HENCODE BASSENCDEF(BASS_Encode_StartUser)(DWORD handle, const char *filename, DWORD flags, ENCODERPROC *proc, void *user);
160+
typedef BOOL BASSENCDEF(BASS_Encode_AddChunk)(HENCODE handle, const char *id, const void *buffer, DWORD length);
161+
typedef BOOL BASSENCDEF(BASS_Encode_Write)(DWORD handle, const void *buffer, DWORD length);
162+
typedef BOOL BASSENCDEF(BASS_Encode_Stop)(DWORD handle);
163+
typedef BOOL BASSENCDEF(BASS_Encode_StopEx)(DWORD handle, BOOL queue);
164+
typedef BOOL BASSENCDEF(BASS_Encode_SetPaused)(DWORD handle, BOOL paused);
165+
typedef DWORD BASSENCDEF(BASS_Encode_IsActive)(DWORD handle);
166+
typedef BOOL BASSENCDEF(BASS_Encode_SetNotify)(DWORD handle, ENCODENOTIFYPROC *proc, void *user);
167+
typedef QWORD BASSENCDEF(BASS_Encode_GetCount)(HENCODE handle, DWORD count);
168+
typedef BOOL BASSENCDEF(BASS_Encode_SetChannel)(DWORD handle, DWORD channel);
169+
typedef DWORD BASSENCDEF(BASS_Encode_GetChannel)(HENCODE handle);
170+
typedef BOOL BASSENCDEF(BASS_Encode_UserOutput)(HENCODE handle, QWORD offset, const void *buffer, DWORD length);
171+
172+
#ifdef _WIN32
173+
typedef DWORD BASSENCDEF(BASS_Encode_GetACMFormat)(DWORD handle, void *form, DWORD formlen, const char *title, DWORD flags);
174+
typedef HENCODE BASSENCDEF(BASS_Encode_StartACM)(DWORD handle, const void *form, DWORD flags, ENCODEPROC *proc, void *user);
175+
typedef HENCODE BASSENCDEF(BASS_Encode_StartACMFile)(DWORD handle, const void *form, DWORD flags, const char *filename);
176+
#endif
177+
178+
#ifdef __APPLE__
179+
typedef HENCODE BASSENCDEF(BASS_Encode_StartCA)(DWORD handle, DWORD ftype, DWORD atype, DWORD flags, DWORD bitrate, ENCODEPROCEX *proc, void *user);
180+
typedef HENCODE BASSENCDEF(BASS_Encode_StartCAFile)(DWORD handle, DWORD ftype, DWORD atype, DWORD flags, DWORD bitrate, const char *filename);
181+
typedef void *BASSENCDEF(BASS_Encode_GetCARef)(DWORD handle);
182+
#endif
183+
184+
#ifndef _WIN32_WCE
185+
typedef BOOL BASSENCDEF(BASS_Encode_CastInit)(HENCODE handle, const char *server, const char *pass, const char *content, const char *name, const char *url, const char *genre, const char *desc, const char *headers, DWORD bitrate, DWORD flags);
186+
typedef BOOL BASSENCDEF(BASS_Encode_CastSetTitle)(HENCODE handle, const char *title, const char *url);
187+
typedef BOOL BASSENCDEF(BASS_Encode_CastSendMeta)(HENCODE handle, DWORD type, const void *data, DWORD length);
188+
typedef const char *BASSENCDEF(BASS_Encode_CastGetStats)(HENCODE handle, DWORD type, const char *pass);
189+
190+
typedef DWORD BASSENCDEF(BASS_Encode_ServerInit)(HENCODE handle, const char *port, DWORD buffer, DWORD burst, DWORD flags, ENCODECLIENTPROC *proc, void *user);
191+
typedef BOOL BASSENCDEF(BASS_Encode_ServerKick)(HENCODE handle, const char *client);
192+
#endif
193+
194+
#ifdef __cplusplus
195+
}
196+
#endif
197+
198+
#ifdef __OBJC__
199+
#undef BOOL
200+
#endif
201+
202+
#endif

source/sourcesdk/cgmod_audio.cpp

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// HOLYLIB_REQUIRES_MODULE=bass
22

3+
#define DLL_TOOLS
34
#include "interface.h"
45
#include "cgmod_audio.h"
56
#include <stdio.h>
@@ -8,6 +9,7 @@
89
#include <filesystem.h>
910
#include "Platform.hpp"
1011
#include <detours.h>
12+
#include "bassenc.h"
1113

1214
// memdbgon must be the last include file in a .cpp file!!!
1315
#include "tier0/memdbgon.h"
@@ -207,20 +209,21 @@ IGMod_Audio* g_pGModAudio = &g_CGMod_Audio;
207209

208210
CGMod_Audio::~CGMod_Audio()
209211
{
210-
211212
}
212213

214+
// Functions of BASSENC as we do not want to link against them
215+
// (we then couldn't load if they were missing!)
216+
static BASS_Encode_Start* func_BASS_Encode_Start;
217+
218+
#define GetBassEncFunc(name) \
219+
func_##name = (name*)Detour::GetFunction(bassenc.GetModule(), Symbol::FromName(#name)); \
220+
Detour::CheckFunction((void*)func_##name, #name);
221+
213222
bool CGMod_Audio::Init(CreateInterfaceFn interfaceFactory)
214223
{
215224
ConnectTier1Libraries( &interfaceFactory, 1 );
216225
ConnectTier2Libraries( &interfaceFactory, 1 );
217226

218-
if (GetVersion() == 33821184L)
219-
{
220-
g_bUsesLatestBass = true;
221-
Msg(PROJECT_NAME " - CGMod_Audio::Init found latets bass version :3\n");
222-
}
223-
224227
BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10);
225228

226229
#ifdef DEDICATED
@@ -263,6 +266,22 @@ bool CGMod_Audio::Init(CreateInterfaceFn interfaceFactory)
263266
BASS_SetConfig(BASS_CONFIG_SRC, 2);
264267
BASS_Set3DFactors(0.0680416f, 7.0f, 5.2f);
265268

269+
if (GetVersion() == 33821184L)
270+
{
271+
g_bUsesLatestBass = true;
272+
Msg(PROJECT_NAME " - CGMod_Audio::Init found latets bass version :3\n");
273+
274+
if (LoadDLL("bassenc"))
275+
{
276+
SourceSDK::ModuleLoader bassenc(DLL_PREEXTENSION"bassenc");
277+
if (bassenc.IsValid())
278+
{
279+
Msg(PROJECT_NAME " - CGMod_Audio::Init found bassenc, loading functions :3\n");
280+
GetBassEncFunc(BASS_Encode_Start);
281+
}
282+
}
283+
}
284+
266285
return 1;
267286
}
268287

@@ -289,6 +308,12 @@ void CGMod_Audio::Shutdown()
289308
{
290309
BASS_Free();
291310
BASS_PluginFree(0);
311+
312+
for (void* pLoadedDLL : m_pLoadedDLLs)
313+
{
314+
DLL_UnloadModule((DLL_Handle)pLoadedDLL);
315+
}
316+
m_pLoadedDLLs.clear();
292317
}
293318

294319
IBassAudioStream* CGMod_Audio::CreateAudioStream(IAudioStreamEvent* event)
@@ -483,6 +508,16 @@ bool CGMod_Audio::LoadPlugin(const char* pluginName)
483508
return true;
484509
}
485510

511+
bool CGMod_Audio::LoadDLL(const char* pDLLName)
512+
{
513+
DLL_Handle handle = DLL_LoadModule(DLL_PREEXTENSION"bassenc" LIBRARY_EXTENSION, RTLD_LAZY);
514+
if (!handle)
515+
return false;
516+
517+
m_pLoadedDLLs.push_back((void*)handle);
518+
return true;
519+
}
520+
486521
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGMod_Audio, IGMod_Audio, "IGModAudio001", g_CGMod_Audio);
487522

488523

@@ -805,4 +840,28 @@ bool CGModAudioChannel::Get3DEnabled()
805840
void CGModAudioChannel::Restart()
806841
{
807842
BASS_ChannelPlay(m_pHandle, true);
843+
}
844+
845+
void CALLBACK EncodeCallback(HENCODE handle, DWORD channel, const void *buffer, DWORD length, void *user) {
846+
g_pFullFileSystem->Write(buffer, length, (FileHandle_t)user);
847+
}
848+
849+
const char* CGModAudioChannel::EncodeToDisk(const char* pFileName, const char* pCommand, unsigned long nFlags)
850+
{
851+
FileHandle_t pHandle = g_pFullFileSystem->Open(pFileName, "rb", "DATA");
852+
if (!pHandle)
853+
return g_CGMod_Audio.GetErrorString(BASS_ERROR_FILEOPEN);
854+
855+
if (!func_BASS_Encode_Start)
856+
return "Missing BASSENC";
857+
858+
HENCODE encoder = func_BASS_Encode_Start(m_pHandle, pCommand, nFlags, EncodeCallback, pHandle);
859+
if (!encoder) {
860+
int err = BASS_ErrorGetCode();
861+
g_pFullFileSystem->Close(pHandle);
862+
return g_CGMod_Audio.GetErrorString(BASS_ErrorGetCode());
863+
}
864+
865+
g_pFullFileSystem->Close(pHandle);
866+
return NULL;
808867
}

0 commit comments

Comments
 (0)