-
Notifications
You must be signed in to change notification settings - Fork 0
/
MikMod.cpp
546 lines (487 loc) · 14.6 KB
/
MikMod.cpp
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
//---------------------------------------------------------------------------
#include <map>
#pragma hdrstop
#include "MikMod.h"
#include "MikModThread.h"
#include "MikModContnrs.h"
#include "include/mikmod.h"
#include "include/mikmod_internals.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#if defined(DRV_DS)
#pragma comment(lib, "dsound") // Needed for the DirectSound driver
#endif // DRV_DS
typedef struct _MOD_READER
{
MREADER Core; /**< A MREADER struct. */
System::Classes::TStream *Stream; /**< A stream. */
} MOD_READER;
static BOOL GST_READER_Eof(MREADER *reader);
static BOOL GST_READER_Read(MREADER *reader, void *ptr, size_t size);
static int GST_READER_Get(MREADER *reader);
static int GST_READER_Seek(MREADER *reader, long offset, int whence);
static long GST_READER_Tell(MREADER *reader);
/**
* Constructor.
*/
__fastcall TVoice::TVoice(Int8 AVoiceNumber) :
System::TObject(),
FVoiceNumber(AVoiceNumber)
{
}
/**
* Destructor.
*/
__fastcall TVoice::~TVoice()
{
}
/**
* This function returns the volume of the sample currently playing on the specified voice.
* @return The current volume of the sample playing on the specified voice, or zero if no sample is currently playing on the voice.
*/
unsigned short __fastcall TVoice::GetVolume()
{
return Voice_GetVolume(FVoiceNumber);
}
/**
* This function returns the frequency of the sample currently playing on the specified voice.
* @return The current frequency of the sample playing on the specified voice, or zero if no sample is currently playing on the voice.
*/
unsigned long __fastcall TVoice::GetFrequency()
{
return Voice_GetFrequency(FVoiceNumber);
}
/**
* This function returns the actual playing volume of the specified voice.
* @return The real volume of the voice when the function was called, in the range 0-65535.
*/
unsigned long __fastcall TVoice::GetRealVolume()
{
return Voice_RealVolume(FVoiceNumber);
}
/**
* This function returns the panning position of the sample currently playing on the specified voice.
* @return The current panning position of the sample playing on the specified voice, or PAN_CENTER if no sample is currently playing on the voice.
*/
unsigned long __fastcall TVoice::GetPanning()
{
return Voice_GetPanning(FVoiceNumber);
}
/**
* Constructor.
* @param ADriver The driver to use. If not specify, mdNoSound will be used.
*/
__fastcall TMikMod::TMikMod(TModuleDriver ADriver) :
System::TObject(),
FModule(NULL),
FVolume(128),
FWrap(true),
FLoop(true),
FFadeOut(false),
FVoiceCount(0)
{
std::map<TModuleDriver, MDRIVER*> DriverList;
DriverList[TModuleDriver::NoSound] = &drv_nos;
#ifdef DRV_DS
DriverList[TModuleDriver::DirectSound] = &drv_ds;
#endif /* DRV_DS */
#ifdef DRV_WIN
DriverList[TModuleDriver::Windows] = &drv_win;
#endif /* DRV_WIN */
#ifdef DRV_OSX
DriverList[TModuleDriver::MacOSX] = &drv_osx;
#endif /* DRV_OSX */
#ifdef DRV_OSLES
DriverList[TModuleDriver::OpenSLES] = &drv_osles;
#endif /* DRV_OSLES */
#ifdef DRV_SDL
DriverList[TModuleDriver::SDL] = &drv_sdl;
#endif /* DRV_SDL */
#ifdef DRV_OPENAL
DriverList[TModuleDriver::OpenAL] = &drv_openal;
#endif /* DRV_OPENAL */
#ifdef DRV_RAW
DriverList[TModuleDriver::Raw] = &drv_raw;
#endif /* DRV_RAW */
#ifdef DRV_WAV
DriverList[TModuleDriver::WAV] = &drv_wav;
#endif /* DRV_WAV */
#ifdef DRV_AIFF
DriverList[TModuleDriver::AIFF] = &drv_aiff;
#endif /* DRV_AIFF */
#ifdef DRV_STDOUT
DriverList[TModuleDriver::StandardOutput] = &drv_stdout;
#endif /* DRV_STDOUT */
if(DriverList[ADriver] == NULL)
{
throw Exception("Invalid driver.");
}
// Register a specific driver
MikMod_RegisterDriver(DriverList[ADriver]);
// Register all the module loaders
MikMod_RegisterAllLoaders();
// Only one device is used, this is needed to use command line
md_device = 1;
// Set compact disc quality (default value)
md_mixfreq = 44100;
std::string CommandLine; // Do not use AnsiString, there is a problem with OS X
if(ADriver == TModuleDriver::DirectSound)
{
CommandLine = "globalfocus,"; // Play if window does not have the focus
CommandLine += "buffer=15"; // Size of the buffer
}
// Initialize the library
if(MikMod_Init(CommandLine.c_str()) != 0)
{
String LException = "An error occurred during initialization.";
String LError = MikMod_strerror(MikMod_errno); // Get last error
if(LError.IsEmpty() == false)
{
LException += " " + LError + ".";
}
throw Exception(LException, MikMod_errno);
}
// This should be at the end of the constructor in case an exception is thrown
FMikModThread = new Mikmod::TMikModThread();
FMikModThread->OnModuleCompleted = ModuleCompleted;
FVoiceList = new Mikmod::TVoiceList();
FIsThreadSafe = MikMod_InitThreads();
}
/**
* Destructor.
*/
__fastcall TMikMod::~TMikMod()
{
// The thread must be deleted first in order to stop updating the sound
delete FMikModThread;
UnLoad();
MikMod_Exit();
delete FVoiceList;
}
/**
* Set a module.
* @param AModule The module to set.
*/
void __fastcall TMikMod::SetModule(MODULE* AModule)
{
if(AModule == NULL)
{
throw Exception("Module did not load properly.");
}
if(FModule != NULL)
{
UnLoad();
}
FModule = AModule;
FModule->wrap = FWrap; // The module will restart when it's finished
FModule->loop = FLoop; // Allow module to loop
FModule->fadeout = FFadeOut; // The module will fadeout while running lastpos if true
MikMod_Lock();
FVoiceCount = md_numchn;
MikMod_Unlock();
for(int i = 0; i < FVoiceCount; ++i)
{
FVoiceList->Add(new TVoice(i));
}
}
/**
* This function loads a music module from a file.
* @param AFileName The name of the module file.
* @param Maxchan The maximum number of channels the song is allowed to request from the mixer.
* @param Curious The curiosity level to use.
*/
void __fastcall TMikMod::LoadFromFile(const System::UnicodeString AFileName, int Maxchan, bool Curious)
{
TFileStream *FileStream = NULL;
try
{
FileStream = new TFileStream(AFileName, fmOpenRead);
LoadFromStream(FileStream, Maxchan, Curious);
}
__finally
{
delete FileStream;
}
}
/**
* This function loads a music module from a stream.
* @param ASream The stream to load.
* @param Maxchan The maximum number of channels the song is allowed to request from the mixer.
* @param Curious The curiosity level to use.
*/
void __fastcall TMikMod::LoadFromStream(System::Classes::TStream *ASream, int Maxchan, bool Curious)
{
MOD_READER Reader;
Reader.Stream = ASream;
Reader.Core.Eof = &GST_READER_Eof;
Reader.Core.Read = &GST_READER_Read;
Reader.Core.Get = &GST_READER_Get;
Reader.Core.Seek = &GST_READER_Seek;
Reader.Core.Tell = &GST_READER_Tell;
MODULE *Module = Player_LoadGeneric((MREADER *)&Reader, Maxchan, Curious);
SetModule(Module);
}
/**
* This function loads a music module from resource.
* @param Instance The Instance parameter is the instance handle associated with the executable or shared library that contains the resource.
* @param ResName The ResName is the string associated with the resource in the .rc file that was compiled with the application.
* @param Maxchan The maximum number of channels the song is allowed to request from the mixer.
* @param Curious The curiosity level to use.
*/
void __fastcall TMikMod::LoadFromResourceName(NativeUInt Instance, const System::UnicodeString ResName, int Maxchan, bool Curious)
{
TResourceStream *ResStream = NULL;
try
{
ResStream = new TResourceStream(Instance, ResName, (System::WideChar *)RT_RCDATA);
LoadFromStream(ResStream, Maxchan, Curious);
}
__finally
{
delete ResStream;
}
}
/**
* Unload a MOD file.
*/
void __fastcall TMikMod::UnLoad()
{
if(FModule != NULL)
{
Player_Free(FModule);
FModule = NULL;
FVoiceCount = 0;
FVoiceList->Clear();
}
}
/**
* This function sets the module volume.
* @param AVolume The new overall module playback volume, in the range 0-128.
*/
void __fastcall TMikMod::SetVolume(int AVolume)
{
FVolume = (AVolume < 0) ? 0 : (AVolume > 128) ? 128 : AVolume;
Player_SetVolume(FVolume);
}
/**
* This function sets the module wrap property.
* @param AWrap The new wrap setup.
*/
void __fastcall TMikMod::SetWrap(bool AWrap)
{
FWrap = AWrap;
if(FModule != NULL)
{ // If a module is loaded, set it to the current module
FModule->wrap = FWrap;
}
}
/**
* This function sets the module loop property.
* @param ALoop The new loop setup.
*/
void __fastcall TMikMod::SetLoop(bool ALoop)
{
FLoop = ALoop;
if(FModule != NULL)
{ // If a module is loaded, set it to the current module
FModule->loop = FLoop;
}
}
/**
* This function sets the module fadeout property.
* @param AFadeOut The new fadeout setup.
*/
void __fastcall TMikMod::SetFadeOut(bool AFadeOut)
{
FFadeOut = AFadeOut;
if(FModule != NULL)
{ // If a module is loaded, set it to the current module
FModule->fadeout = FFadeOut;
}
}
/**
* This function stops the currently playing module.
* @see Start
*/
void __fastcall TMikMod::Stop()
{
Player_SetPosition(0); // Position must be changed before player stops
FMikModThread->Suspended = true;
Player_Stop();
}
/**
* This function starts the specified module playback.
* @see Stop
*/
void __fastcall TMikMod::Start()
{
CheckIfOpen();
Player_Start(FModule);
Player_SetVolume(FVolume); // Volume must be changed after player is started
FMikModThread->Suspended = false;
}
/**
* This function toggles the playing/paused status of the module.
*/
void __fastcall TMikMod::Pause()
{
Player_TogglePause();
}
/**
* This function returns the version number of the library.
* @return The version number, encoded as follows: (maj<<16)|(min<<8)|(rev), where maj is the major version number, min is the minor version number, and rev is the revision number.
*/
long __fastcall TMikMod::GetVersion()
{
return MikMod_GetVersion();
}
/**
* Make sure a module is open, else raise exception.
*/
void __fastcall TMikMod::CheckIfOpen()
{
if(FModule == NULL)
{
throw Exception("Load a module first.");
}
}
/**
* Event call when the module is completed.
*/
void __fastcall TMikMod::ModuleCompleted(System::TObject* Sender)
{
Stop();
}
/**
* Returns the song title.
* @return The song title.
*/
String __fastcall TMikMod::GetSongTitle()
{
CheckIfOpen();
return FModule->songname;
}
/**
* Returns the name of the tracker used to create the song.
* @return The name of the tracker used to create the song.
*/
String __fastcall TMikMod::GetModType()
{
CheckIfOpen();
return FModule->modtype;
}
/**
* Returns the song comment, if it has one.
* @return The song comment.
*/
String __fastcall TMikMod::GetComment()
{
CheckIfOpen();
return FModule->comment;
}
/**
* Returns the number of sound positions in the module.
* @return The number of sound positions in the module.
*/
unsigned short __fastcall TMikMod::GetNumberPos()
{
CheckIfOpen();
return FModule->numpos;
}
/**
* Returns the song position.
* @return The song position.
*/
unsigned short __fastcall TMikMod::GetPosition()
{
CheckIfOpen();
return FModule->sngpos;
}
/**
* Set the song position.
* @param The song position.
*/
void __fastcall TMikMod::SetPosition(unsigned short APosition)
{
Player_SetPosition(APosition);
}
/**
* Get a voice at a certain position.
* @param Index The number of the voice to get.
* @return A pointer to the voice at Index.
*/
TVoice* __fastcall TMikMod::GetVoice(int Index)
{
return FVoiceList->Items[Index];
}
/**
* This function returns whether sound output is enabled or not.
* @return Returns true if sound output is enabled, false otherwise.
*/
bool __fastcall TMikMod::GetActive()
{
return MikMod_Active();
}
/**
* This function has the same behaviour as feof.
* @return Returns true if the end-of-file has been reached, false otherwise.
*/
static BOOL GST_READER_Eof(MREADER * reader)
{
MOD_READER *pReader = reinterpret_cast<MOD_READER *>(reader);
return (pReader->Stream->Position == pReader->Stream->Size) ? true : false;
}
/**
* This function copies length bytes of data into dest, and return zero if an error occured, and any nonzero value otherwise. Note that an end-of-file condition will not be considered as an error in this case.
*/
static BOOL GST_READER_Read(MREADER * reader, void *ptr, size_t size)
{
MOD_READER *pReader = reinterpret_cast<MOD_READER *>(reader);
pReader->Stream->Read(ptr, size);
return 1;
}
/**
* This function has the same behaviour as fgetc.
*/
static int GST_READER_Get(MREADER * reader)
{
MOD_READER *pReader = reinterpret_cast<MOD_READER *>(reader);
char buf;
pReader->Stream->Read(&buf, 1);
return (int)buf;
}
/**
* This function has the same behaviour as fseek, with offset 0 meaning the start of the object (module, sample) being loaded.
* @param offset Number of bytes to offset from whence.
* @param whence Position used as reference for the offset.
* @return If successful, the function returns zero. Otherwise, it returns non-zero value.
*/
static int GST_READER_Seek(MREADER * reader, long offset, int whence)
{
int Result = 0;
MOD_READER *pReader = reinterpret_cast<MOD_READER *>(reader);
switch(whence)
{
case SEEK_SET:
pReader->Stream->Position = offset;
break;
case SEEK_CUR:
pReader->Stream->Position += offset;
break;
case SEEK_END:
pReader->Stream->Position += pReader->Stream->Size + offset;
break;
default:
Result = 1;
break;
}
return Result;
}
/**
* This function has the same behaviour as ftell, with offset 0 meaning the start of the object being loaded.
*/
static long GST_READER_Tell(MREADER * reader)
{
MOD_READER *pReader = reinterpret_cast<MOD_READER *>(reader);
return pReader->Stream->Position;
}