Subversion Repositories spacemission

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4 daniel-mar 1
{
2
 SXMedia  Components - Beta 1
3
 --------------------------------
4
 Copyright 1999 Dean Ellis
5
 http://www.sillex.freeserve.co.uk
6
 
7
 This unit is part of the SXMedia Component Set. This code is
8
 supplied as is with no guarantees and must be used at your own
9
 risk.
10
 
11
 No modifications to this code must be made without the express
12
 permission of the author. Please report any problems to
13
 support@sillex.freeserve.co.uk
14
 
15
 You may use these components to create any freeware/shareware
16
 applications that you wish. If the components are to be used in
17
 a commercail product then credit for developement of these components
18
 should be given.
19
 
20
 Credits :
21
 
22
 Developer : Dean Ellis
23
 Testers   : Dominique Louis
24
             Ivan Blecic
25
             Naoki Haga
26
 
27
Version History
28
--------------------------------------------------------------------------------
29
25/01/2000 Modified Destroy and Stop methods to check IDSBuffer before
30
           calling IBuffer.Stop. Stops the "Buffer not Created exception"
31
           being thrown.
32
06/02/2000 Added OnStop and OnStart events
33
           Added Finalize method. This does not have to be called but if you
34
           manually finalize and initialize the DXSound Component you
35
           need to call this method to clear the buffers and the Threads.
36
22/02/2000 Modified SetLoop code to correct functionality.
37
           Modifed Stop method to make sure the OnStop event is only called
38
           if it wsa playing and that the Value of Playing would be False
39
           when the event is fired.
40
15/05/2000 Made use of the MppSdkLibLoaded variable to make sure the component
41
           does nothing if the mppsdk.dll is not found.
42
           Removed the raising of an exception in the constructor as it seemed to
43
           be causing more problems than it was solving.
44
--------------------------------------------------------------------------------
45
}
46
unit SXModPlayer;
47
 
48
{$INCLUDE DelphiXcfg.inc}
49
 
50
interface
51
 
52
uses
53
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
54
  DXSounds, MMSystem, MpSndSys,
55
  {$IFDEF StandardDX}
56
  DirectDraw, DirectSound,
57
    {$IFDEF DX7}
58
      {$IFDEF D3DRM}
59
  Direct3DRM,
60
      {$ENDIF}
61
  Direct3D;
62
    {$ENDIF}
63
    {$IFDEF DX9}
64
  Direct3D9, Direct3D, D3DX9, {Direct3D8,} DX7toDX8;
65
    {$ENDIF}
66
  {$ELSE}
67
  DirectX;
68
  {$ENDIF}
69
 
70
type
71
 
72
   TModOption = (NoResampling, BassExpansion, Surround, Reverb,
73
                  HighQuality, GainControl, NoiseReduction);
74
 
75
   TModOptions = Set of TModOption;
76
 
77
   TSXModPlayer = class(TComponent)
78
     private
79
       { Private declarations }
80
       FFilename: TFilename;
81
       FDXSound : TDXSound;
82
       FSoundBuffer : TDirectSoundBuffer;
83
       FSoundNotify : IDirectSoundNotify;
84
       FBufferDesc : TDSBufferDesc;
85
       FWaveFormat : TWaveFormatEx;
86
       FLoop : Boolean;
87
       FPlaying : Boolean;
88
       FOptions : TModOptions;
89
       FEvents: TList;
90
       FNotify: TList;
91
       FOnStop: TNotifyEvent;
92
       FOnStart: TNotifyEvent;
93
       function GetPosition : Integer;
94
       function GetSilenceData:Integer;
95
     protected
96
       { Protected declarations }
97
       procedure InitSoundEvents;
98
       procedure ThreadCallback;
99
       procedure SetFilename(const Value : TFilename);
100
       procedure SetLoop(const Value : Boolean);
101
       procedure SetOptions(const Value : TModOptions);
102
       procedure Notification(AComponent: TComponent; Operation: TOperation); override;
103
       procedure DoStop;
104
       procedure DoStart;
105
 
106
       function CreateEventList : TList;
107
       function CreateNotifyList : TList;
108
       function CreateSoundBuffer : TDirectSoundBuffer;
109
       function CreateSoundNotify : IDirectSoundNotify;
110
       procedure ClearSoundNotify;
111
       procedure CreateEventThread;
112
       procedure ClearSoundBuffer;
113
 
114
       procedure StartThread;
115
       procedure StopThread;
116
 
117
       property BufferDesc : TDSBufferDesc read FBufferDesc write FBufferDesc;
118
       property WaveFormat : TWaveFormatEx read FWaveFormat write FWaveFormat;
119
       property SoundBuffer : TDirectSoundBuffer read FSoundBuffer write FSoundBuffer;
120
       property SoundNotify : IDirectSoundNotify read FSoundNotify write FSoundNotify;
121
       property Events : TList read FEvents write FEvents;
122
       property Notify : TList read FNotify write FNotify;
123
     public
124
       { Public declarations }
125
       constructor Create( AOwner : TComponent); override;
126
       destructor Destroy; override;
127
       procedure Initialize(Stream:TMemoryStream);
128
       procedure Finalize;
129
       procedure Play(Loop : Boolean);
130
       procedure Stop;
131
       procedure Reset;
132
 
133
       property Position : Integer read GetPosition;
134
       property Playing : Boolean read FPlaying;
135
     published
136
       { Published declarations }
137
       property Filename : TFilename read FFilename write SetFilename;
138
       property DXSound : TDXSound read FDXSound write FDXSound;
139
       property Looping : Boolean read FLoop write SetLoop default False;
140
       property Options : TModOptions read FOptions write SetOptions;
141
       property OnStart : TNotifyEvent read FOnStart write FOnStart;
142
       property OnStop : TNotifyEvent read FOnStop write FOnStop;
143
  end;
144
 
145
implementation
146
 
147
// If you are having problems compiling the package edit this file.
148
// Comment out this include Statement. You should only need to do this
149
// if you are NOT using the latest version of DelphiX (991024)
150
// Comment this Define out if you are using DelphiX992404 or earlier
151
{$DEFINE VERSION991024}
152
 
153
const
154
    EVENTCOUNT:Integer = 2;
155
 
156
type
157
    {Music Event Callback thread}
158
    TSXEventThread = class(TThread)
159
       private
160
          FEventCallback:TThreadMethod;
161
       public
162
          procedure Execute; override;
163
          property EventCallback : TThreadMethod read FEventCallback write FEventCallback;
164
 
165
    end;
166
 
167
 
168
var
169
 
170
   EventThread : TSXEventThread;
171
   CurrentEvent : Integer;
172
 
173
{ TSXEventThread }
174
 
175
procedure TSXEventThread.Execute;
176
begin
177
  if not Assigned(EventCallback) then Terminate;
178
  while not Terminated do
179
  begin
180
     EventCallback;
181
  end;
182
end;
183
 
184
{ TSXModPlayer }
185
 
186
constructor TSXModPlayer.Create(AOwner : TComponent);
187
begin
188
   inherited Create(AOwner);
189
   if MppSdkLibLoaded then
190
     ModMixer.SetMixerOptions(0);
191
   Events := CreateEventList;
192
   Notify := CreateNotifyList;
193
end;
194
destructor TSXModPlayer.Destroy;
195
begin
196
   Finalize;
197
   {}
198
   Notify.Free;
199
   Events.Free;
200
   inherited Destroy;
201
end;
202
procedure TSXModPlayer.Initialize(Stream:TMemoryStream);
203
var Data:Pointer;
204
    Size:Integer;
205
    FreeStream:Boolean;
206
begin
207
  try
208
     if MppSdkLibLoaded then
209
     begin
210
       if SoundBuffer = nil then InitSoundEvents;
211
       FreeStream := False;
212
       if Stream = nil then
213
       begin
214
          Stream := TMemoryStream.Create;
215
          Stream.LoadFromFile(Filename);
216
          FreeStream := True;
217
       end;
218
       Data := Stream.Memory;
219
       Size := Stream.Size;
220
       ModMixer.FreeSong;
221
       ModMixer.LoadSong(Data,Size);
222
       if FreeStream then Stream.Free;
223
     end;
224
  except
225
  end;
226
end;
227
procedure TSXModPlayer.Finalize;
228
begin
229
   Stop;
230
   if Assigned(EventThread) then
231
   begin
232
      EventThread.Terminate;
233
      EventThread.Free;
234
      EventThread := nil;
235
   end;
236
   ClearSoundNotify;
237
   if Assigned(SoundBuffer) then
238
   begin
239
      try
240
         if Playing and (SoundBuffer.IDSBuffer <> nil) then
241
            SoundBuffer.IBuffer.Stop;
242
      finally
243
         SoundBuffer := nil;
244
      end;
245
   end;
246
end;
247
 
248
procedure TSXModPlayer.InitSoundEvents;
249
// DelphiX Version 991024 Edit Version.inc to change declarations
250
{$IFDEF VERSION991024}
251
  var SizeWritten: Cardinal;
252
{$ELSE}
253
// DelphiX Version 992404 and earlier
254
  var  SizeWritten : Integer;
255
{$ENDIF}
256
begin
257
   if MppSdkLibLoaded and Assigned(DXSound) then
258
   begin
259
      DXSound.Primary.IBuffer.GetFormat(@FWaveFormat,Sizeof(WaveFormat),@SizeWritten);
260
      ModMixer.SetWaveFormat(WaveFormat.nSamplesPerSec,WaveFormat.nChannels,WaveFormat.wBitsPerSample);
261
      SoundBuffer := CreateSoundBuffer;
262
      SoundNotify := CreateSoundNotify;
263
      if (SoundBuffer <> nil) and (SoundNotify <> nil) then
264
         CreateEventThread;
265
   end;
266
end;
267
procedure TSXModPlayer.ThreadCallback;
268
var Msg : TMsg;
269
 
270
   procedure ReadData(Event:Integer);
271
   var W1:pointer;
272
       // DelphiX Version 991024 Edit Version.inc to change declarations
273
       {$IFDEF VERSION991024}
274
       S1, S2 : Cardinal;
275
       {$ELSE}
276
       // DelphiX Version 992404 and earlier
277
       S1,S2:Integer;
278
       {$ENDIF}
279
       NumWrite:Longint;
280
       Pos:Integer;
281
       Written1:Integer;
282
   begin
283
     if Event = 0 then
284
        Pos := TDSBPositionNotify(Notify[EVENTCOUNT -1]^).dwOffset
285
     else
286
        Pos := TDSBPositionNotify(Notify[Event-1]^).dwOffset;
287
     NumWrite := TDSBPositionNotify(Notify[Event]^).dwOffset - Pos;
288
     if (NumWrite < 0) then
289
        inc(NumWrite,BufferDesc.dwBufferBytes);
290
     if SoundBuffer.IBuffer.Lock(Pos,NumWrite,w1,{$IFDEF UNICODE}@{$ENDIF}s1,Pointer(nil^),{$IFDEF UNICODE}@{$ENDIF}s2,0) = 0 then
291
     begin
292
        Written1 := ModMixer.Render(W1,S1);
293
        SoundBuffer.IBuffer.Unlock(w1,Written1, nil,0);
294
        if (Written1 = 0) then
295
           Stop;
296
     end;
297
   end;
298
 
299
begin
300
    CurrentEvent := MsgWaitForMultipleObjects(EVENTCOUNT,Events.List{$IFNDEF UNICODE}^{$ENDIF}[0], False, INFINITE, QS_ALLINPUT);
301
    dec(CurrentEvent,WAIT_OBJECT_0);
302
    if CurrentEvent >= EVENTCOUNT then
303
    begin
304
      while (PeekMessage(Msg, 0, 0,0, PM_REMOVE)) do
305
      begin
306
        if Msg.Message = WM_QUIT then
307
           Stop
308
        else
309
        begin
310
          TranslateMessage(Msg);
311
          DispatchMessage(Msg);
312
        end;
313
      end;
314
    end
315
    else
316
    begin
317
       ReadData(CurrentEvent);
318
    end;
319
end;
320
function TSXModPlayer.CreateEventList : TList;
321
begin
322
  Result := TList.Create;
323
  Result.Capacity := EVENTCOUNT;
324
end;
325
function TSXModPlayer.CreateNotifyList : TList;
326
begin
327
  Result := TList.Create;
328
  Result.Capacity := EVENTCOUNT;
329
end;
330
function TSXModPlayer.CreateSoundBuffer : TDirectSoundBuffer;
331
{$IFDEF UNICODE}
332
const DSBCAPS_CTRLDEFAULT = DSBCAPS_CTRLPAN or DSBCAPS_CTRLVOLUME or DSBCAPS_CTRLFREQUENCY;
333
{$ENDIF}
334
begin
335
   Result := TDirectSoundBuffer.Create(DXSound.DSound);
336
   ZeroMemory(@BufferDesc,Sizeof(TDSBufferDesc));
337
   FBufferDesc.dwSize := Sizeof(TDSBufferDesc);
338
   FBufferDesc.dwFlags := DSBCAPS_CTRLDEFAULT or DSBCAPS_STATIC or
339
                         DSBCAPS_GETCURRENTPOSITION2 or DSBCAPS_CTRLPOSITIONNOTIFY;
340
   FBufferDesc.dwBufferBytes := WaveFormat.nAvgBytesPerSec * 2;
341
   FBufferDesc.lpwfxFormat := @WaveFormat;
342
   Result.CreateBuffer(BufferDesc);
343
end;
344
function TSXModPlayer.CreateSoundNotify : IDirectSoundNotify;
345
type TNotifyArray = Array[0..99] of TDSBPositionNotify;
346
var PDSNotify : PDSBPositionNotify;
347
    index : Integer; Offset: Integer;
348
    PNotify: ^TNotifyArray;
349
begin
350
   Result := nil;
351
   if SoundBuffer.IBuffer.QueryInterface(IID_IDirectSoundNotify,Result) = 0 then
352
   begin
353
      {setup notifications here}
354
      Offset := 0;
355
      GetMem(PNotify,EVENTCOUNT * Sizeof(TDSBPositionNotify));
356
      for Index := 1 to EVENTCOUNT do
357
      begin
358
         New(PDSNotify);
359
         PDSNotify^.dwOffset := OffSet;
360
         PDSNotify^.hEventNotify := CreateEvent(nil,False,False,nil);
361
         Notify.Add(PDSNotify);
362
         Events.Add(Pointer(PDSNotify^.hEventNotify));
363
         PNotify[Index-1] := PDSNotify^;
364
         inc(Offset,WaveFormat.nAvgBytesPerSec div EVENTCOUNT);
365
      end;
366
 
367
      if Result.SetNotificationPositions(EVENTCOUNT,{$IFDEF UNICODE}@{$ENDIF}PNotify[0]) <> 0 then
368
         ShowMessage('Notification Falied');
369
      FreeMem(PNotify,EVENTCOUNT * Sizeof(TDSBPositionNotify));
370
   end;
371
end;
372
procedure TSXModPlayer.ClearSoundNotify;
373
var PDSNotify : PDSBPositionNotify;
374
    Index : Integer;
375
begin
376
   for Index := Notify.Count -1 downto 0 do
377
   begin
378
      PDSNotify := Notify[Index];
379
      Notify.Delete(Index);
380
      Events.Delete(Index);
381
      CloseHandle(PDSNotify^.hEventNotify);
382
   end;
383
   SoundNotify := nil;
384
end;
385
procedure TSXModPlayer.CreateEventThread;
386
begin
387
  if not Assigned(EventThread) then
388
  begin
389
     EventThread := TSXEventThread.Create(True);
390
     EventThread.Priority := tpNormal;
391
     EventThread.EventCallback := ThreadCallback;
392
  end;
393
end;
394
 
395
procedure TSXModPlayer.ClearSoundBuffer;
396
var w1,w2:pointer;
397
    // DelphiX Version 991024 Edit Version.inc to change declarations
398
    {$IFDEF VERSION991024}
399
    S1, S2 : Cardinal;
400
    {$ELSE}
401
    // DelphiX Version 992404 and earlier
402
    S1,S2:Integer;
403
    {$ENDIF}
404
    Data:Word;
405
begin
406
  Data := GetSilenceData;
407
  if SoundBuffer.IBuffer.Lock(0,0,w1,{$IFDEF UNICODE}@{$ENDIF}s1,w2,{$IFDEF UNICODE}@{$ENDIF}s2,DSBLOCK_ENTIREBUFFER) = 0 then
408
  begin
409
     FillMemory(W1,S1,Data);
410
     if W2 <> nil then
411
        FillMemory(W2,S2,Data);
412
     SoundBuffer.IBuffer.Unlock(W1,S1,W2,S2);
413
  end;
414
end;
415
procedure TSXModPlayer.StartThread;
416
begin
417
  if Assigned(EventThread) then EventThread.Resume;
418
end;
419
procedure TSXModPlayer.StopThread;
420
begin
421
  if Assigned(EventThread) then EventThread.Suspend;
422
end;
423
{}
424
procedure TSXModPlayer.Play(Loop : Boolean);
425
begin
426
   if MppSdkLibLoaded then
427
   begin
428
     if Assigned(SoundBuffer) and Assigned(EventThread) then
429
     begin
430
        SetLoop(Loop);
431
        ClearSoundBuffer;
432
        StartThread;
433
        FPlaying := True;
434
        DoStart;
435
        SoundBuffer.IBuffer.Play(0,0,DSBPLAY_LOOPING);
436
     end;
437
   end;
438
end;
439
procedure TSXModPlayer.Stop;
440
begin
441
   if MppSdkLibLoaded then
442
   begin
443
     if Assigned(SoundBuffer) and Assigned(EventThread) then
444
     begin
445
        try
446
          if Playing and (SoundBuffer.IDSBuffer <> nil) then
447
          begin
448
            FPlaying := False;
449
            SoundBuffer.IBuffer.Stop;
450
            DoStop;
451
          end;
452
        finally
453
          StopThread;
454
        end;
455
     end;
456
   end;
457
end;
458
procedure TSXModPlayer.Reset;
459
begin
460
   if MppSdkLibLoaded then
461
     ModMixer.SetCurrentOrder(0);
462
end;
463
{Property Accessors}
464
function TSXModPlayer.GetPosition : Integer;
465
begin
466
  Result := 0;
467
end;
468
procedure TSXModPlayer.SetFilename(const Value : TFilename);
469
begin
470
  if FFilename <> Value then
471
  begin
472
     FFilename := Value;
473
  end;
474
end;
475
procedure TSXModPlayer.SetLoop( const Value : Boolean);
476
var Flags : DWord;
477
begin
478
   if FLoop <> Value then
479
   begin
480
      FLoop := Value;
481
      if MppSdkLibLoaded then
482
      begin
483
        Flags := ModMixer.GetMixerOptions;
484
        case Value of
485
           True:Flags := Flags or MPPMIX_LOOP;
486
           False:Flags := Flags and (not MPPMIX_LOOP);
487
        end;
488
        ModMixer.SetMixerOptions(Flags);
489
      end;
490
   end;
491
end;
492
procedure TSXModPlayer.SetOptions( const Value : TModOptions );
493
const OptionArray: array[Boolean,TModOption] of Integer = (
494
    (0,0,0,0,0,0,0),
495
   (MPPMIX_NORESAMPLING, MPPMIX_BASSEXPANSION,  MPPMIX_SURROUND,
496
   MPPMIX_REVERB, MPPMIX_HIGHQUALITY, MPPMIX_GAINCONTROL,
497
   MPPMIX_NOISEREDUCTION)
498
   );
499
var Flags : DWord;
500
begin
501
   if FOptions <> Value then
502
   begin
503
      FOptions := Value;
504
      if MppSdkLibLoaded then
505
      begin
506
        Flags := 0;
507
        Flags := Flags or OptionArray[NoResampling in Value,NoResampling];
508
        Flags := Flags or OptionArray[BassExpansion in Value,BassExpansion];
509
        Flags := Flags or OptionArray[Surround in Value,Surround];
510
        Flags := Flags or OptionArray[Reverb in Value,Reverb];
511
        Flags := Flags or OptionArray[HighQuality in Value,HighQuality];
512
        Flags := Flags or OptionArray[GainControl in Value,GainControl];
513
        Flags := Flags or OptionArray[NoiseReduction in Value,NoiseReduction];
514
        ModMixer.SetMixerOptions(Flags);
515
        SetLoop(Looping);
516
      end;
517
   end;
518
end;
519
procedure TSXModPlayer.Notification(AComponent: TComponent; Operation: TOperation);
520
begin
521
   inherited Notification( AComponent, Operation);
522
 
523
   if (Operation = opRemove) and (AComponent = DXSound) then
524
      DXSound := nil;
525
end;
526
procedure TSXModPlayer.DoStop;
527
begin
528
  if Assigned(FOnStop) then
529
    FOnStop(Self);
530
end;
531
procedure TSXModPlayer.DoStart;
532
begin
533
  if Assigned(FOnStart) then
534
    FOnStart(Self);
535
end;
536
function TSXModPlayer.GetSilenceData:integer;
537
const SilenceData:array[1..2] of integer = ($80,$0);
538
begin
539
  Result := SilenceData[WaveFormat.wBitsPerSample div 8];
540
end;
541
 
542
end.