Autor Beitrag
mimi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3458

Ubuntu, Win XP
Lazarus
BeitragVerfasst: Mi 28.07.04 13:53 
Hallo,
ich habe heute mal eine sound Klasse geschrieben, mit deren hilfe man sehr einfach viele verschiedne formate abspielen kann.
die verwendung ist sehr einfach:

Beispiel:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
Form1.Create:
Sound:=TSound(Handle);
Sound.add(Dateinamen);
Sound.Play(DateiNamen);
Timer1.invervall:=1;

Timer1.OnTimer:
Sound.CheckPlay;


Upgedatete Version, vorschläge von Nightmare_82 habe ich übernommen
Unit:
ausblenden volle Höhe Delphi-Quelltext
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:
{
  Stellt eine Sound Klasse mit Hilfe von FMOd bereit
  Date    : 28.07.2004
  Updatet : 30.07.2004
}


unit usound;

interface

uses fmodtypes,SysUtils,dialogs,ExtCtrls,wihle,Classes;

type

  TSongType = record
    Module: PFMusicModule;
    Stream: PFSoundStream;
    Channel,f: Integer;
    isLoad: Boolean;
    FileName:String;
  end;

  TSound = class
  private
    Timer:TTimer;
    Musik: array of TSongType; // Spielt hintergrund musik ab
    Sound: array of TSongType; // Spielt nur eine Sound datei als hintergrun gereusch
    PlayMusikIndex:Integer; // enhält den aktuellen index, der gerade gepielt wird
    isRandom:Boolean; // Wenn True dann wird CheckPlay nicht mehr ausgeführt
    t:TSoundWihle; // zeit geber für ständige wiederholungen
    function OpenSong(FileName:TFileName):TSongType; // öffnet eine Sound Datei
    function GetListenIndex(Song: array of TSongType; FileName:TFileName): Integer; // sucht in den song angebe liste nach FileName
    procedure RandomMusik; // sucht zuflälig eine Musik Datei aus
    procedure onE;
    procedure DeleteSong(Liste:array of TSongType; index:Integer);
  public
    dFileName:String;
    constructor Create(Handle:Cardinal); // Installisiert die "Sond Engine"
    procedure AddSound(FileName:String); // fügt der Sond Liste eine Datei hinzu
    procedure AddMusik(FileName:String); // fügt der Musik Liste eine Datei hinzu

    procedure ClearSound; // Leert den Sound array
    procedure ClearMusik; // Leert den Musik array
    
    procedure PlayMusik(FileName:String; F:Integer); // Spielt eine Musik Datei ab
    procedure PlaySound(FileName:String; F:Integer); // Spielt eine Sound Datei ab
    procedure StopSong; // Stop einen Song
    procedure CheckPlay; // Schaut nach ob noch eine Datei gespielt wird(sollte in einem Timer aufgerufen werden )
    procedure TInit;

  end;

implementation

uses fmod, fmoderrors;

{
  Installisiert die Klasse TSound
  date    : 28.07.2004
  upDatet : 28.07.2004
}


constructor TSound.Create(Handle:Cardinal);
begin
  if FMOD_VERSION > FSOUND_GetVersion then
  begin
    ShowMessage(PChar(Format('API version %3.2f is newer than DLL version %3.2f', [FMOD_VERSION, FSOUND_GetVersion])));
    Halt;
  end;

  { Initialize FSOUND }
  try
    if not FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND) then
      raise Exception.Create('FSOUND_SetOutput failed');
    if not FSOUND_SetDriver(0then
      raise Exception.Create('FSOUND_SetDriver failed');
    if not FSOUND_SetMixer(FSOUND_MIXER_QUALITY_AUTODETECT) then
      raise Exception.Create('FSOUND_SetMixer failed');
    if not FSOUND_SetHWND(Handle) then
      raise Exception.Create('FSOUND_SetHWND failed');
  except
    ShowMessage(FMOD_ErrorString(FSOUND_GetError));
    raise;
  end;

  if not FSOUND_Init(22050128, FSOUND_Init_GlobalFocus) then
  begin
    Showmessage(FMOD_ErrorString(FSOUND_GetError));
    Halt;
  end;

  PlayMusikIndex:=-1; isRandom:=False;

end;

procedure TSound.ClearSound; // Leert den Sound array
begin
  SetLength(Sound,0);
end;

procedure TSound.ClearMusik; // Leert den Musik array
begin
  SetLength(Musik,0);
end;

procedure TSound.TInit;
begin
  t:=TSoundWihle.Create(True);
  t.onE:=onE;
  t.Priority := tpLower;
  t.FreeOnTerminate:=True;
  t.Resume;
end;

{
  Löscht ein song aus der liste
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.DeleteSong(Liste:array of TSongType;index:Integer);
var
  i:Integer;
begin
  if Length(Liste) -1 >= 1 then begin
    for i:=index to HIGH(Liste) do
      Liste[i]:=Liste[i+1];
  end;
end;

{
  Fürht immer die funktion CheckPlay aus
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.onE;
begin
  CheckPlay;
end;

{
  Stop alle Musik dateien
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.StopSong;
begin
  if Musik[PlayMusikIndex].Module <> NIL then FMUSIC_StopSong(Musik[PlayMusikIndex].Module);
  if Musik[PlayMusikIndex].Channel > -1 then FSOUND_StopSound(Musik[PlayMusikIndex].Channel);  
end;

{
  Sucht eine Musik Datei per zufall aus(random)
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.randomMusik;
var
  r:Integer;
begin
  t.Suspend;
  isRandom:=True;
  try
    Randomize;
    r:=random(length(musik));
    PlayMusik(Musik[r].FileName,Musik[r].f)
  finally
    isRandom:=False;
    t.Resume;
  end;
end;

{
  Schaut nach ob die musik zu ende ist oder nicht
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.CheckPlay;
begin

  if (PlayMusikIndex > -1and (isRandom = False)  then begin

    if (Musik[PlayMusikIndex].Module <> NILand (FMUSIC_IsFinished(Musik[PlayMusikIndex].Module) = True) then
      randomMusik;

    if (Musik[PlayMusikIndex].Stream <> NILand ( FSOUND_IsPlaying(Musik[PlayMusikIndex].Channel) = False) then
      randomMusik;
  end;

end;


{
  Sucht einen Datei namen in einer Liste und gibt den index zurück !
  date    : 28.07.2004
  upDatet : 28.07.2004
}


function TSound.GetListenIndex(Song: array of TSongType; FileName:TFileName): Integer;
var
  i:Integer;
begin
  for i:=0 to High(Song) do begin
    if Song[i].FileName = FileName then begin
      result:=i;
      exit;
    end;
  end;

  result:=-1;
end;

{
  Öffnet eine Sound Datei
  date    : 28.07.2004
  upDatet : 28.07.2004
}




function TSound.OpenSong(FileName:TFileName):TSongType;
var
  Module: PFMusicModule;
  Stream: PFSoundStream;
  SongCount: Integer;
begin
  Stream := nil;
  Module := nil;
  
  Module := FMUSIC_LoadSong(PChar(FileName));


  if Module = nil then
    Stream := FSOUND_Stream_Open(PChar(FileName), FSOUND_NORMAL, 00);

  if (Module = niland (Stream = nilthen begin
    Showmessage(FMOD_ErrorString(FSOUND_GetError));
    exit;
  end;

  if Module <> nil then  FMUSIC_SetMasterVolume(Module, 255);

  result.Module := Module;
  result.Stream := Stream;
  result.isLoad:=True;  
end;

{
  Spielt die Musik / hintergrund sounds ab
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.PlaySound(FileName:String; f:Integer);
var
  Index:Integer;
  Test:TSongType;
begin
  if (f = 1and (PlayMusikIndex > -1then begin
    DeleteSong(Sound,PlayMusikIndex);
    SetLength(Sound,Length(Sound)-1);
  end;

  Index:=GetListenIndex(Sound,FileName);

  if index > -1 then begin
    if Sound[index].isLoad = False then begin
      Test:=OpenSong(Filename);
      Sound[index].Module:=Test.Module;
      Sound[index].Stream:=Test.Stream;
      Sound[index].Channel:=Test.Channel;
    end;

    if Sound[Index].Module <> nil then begin
      FMUSIC_PlaySong(Sound[Index].Module);
      if Sound[Index].Module = nil then
        ShowmEssage(FMOD_ErrorString(FSOUND_GetError));
    end
    else begin
      if Sound[Index].Stream <> nil then begin
        Sound[Index].Channel := FSOUND_Stream_Play(FSOUND_FREE, Sound[Index].Stream);

        if Sound[Index].Channel = -1 then begin
          ShowMessage(FMOD_ErrorString(FSOUND_GetError));
        end
      end;
    end;
  end;


end;

{
  Spielt die Musik / hintergrund sounds ab
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.PlayMusik(FileName:String; f:Integer);
var
  Index:Integer;
  Test:TSongType;
begin
  if (f = 1and (PlayMusikIndex > -1then begin
    DeleteSong(Musik,PlayMusikIndex);
    SetLength(Musik,Length(Musik)-1);
  end;

  Index:=GetListenIndex(Musik,FileName);

  if index > -1 then begin
    if Musik[index].isLoad = False then begin
      Test:=OpenSong(Filename);
      dFileName:=FileName;
      Musik[index].Module:=Test.Module;
      Musik[index].Stream:=Test.Stream;
      Musik[index].Channel:=Test.Channel;
           
    end;


    if Musik[Index].Module <> nil then begin
      FMUSIC_PlaySong(Musik[Index].Module);
      if Musik[Index].Module = nil then
        ShowmEssage(FMOD_ErrorString(FSOUND_GetError));
    end
    else begin

      if Musik[Index].Stream <> nil then begin
        Musik[Index].Channel := FSOUND_Stream_Play(FSOUND_FREE, Musik[Index].Stream);

        if Musik[Index].Channel = -1 then begin
          ShowMessage(FMOD_ErrorString(FSOUND_GetError));
        end
      end;
    end;
  end;
  PlayMusikIndex:=Index; 
end;

{
  Fügt einen Sound der Liste hinzu
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.AddSound(FileName:String);
begin
  if GetListenIndex(Sound,FileName) = -1 then begin
    SetLength(Sound,high(Sound)+2);

    Sound[high(Sound)].FileName:=FileName;
    Sound[high(Sound)].Module:=NIL;
    Sound[high(Sound)].Stream:=NIL;
    Sound[high(Sound)].Channel:=-1;
    Sound[high(Sound)].isLoad:=False;
  end;
end;

{
  Fügt einen Sound der Liste hinzu
  date    : 28.07.2004
  upDatet : 28.07.2004
}


procedure TSound.AddMusik(FileName:String);
begin
  if GetListenIndex(musik,FileName) = -1 then begin
    SetLength(Musik,high(Musik)+2);
    Musik[high(Musik)].FileName:=FileName;

    Musik[high(Musik)].Module:=NIL;
    Musik[high(Musik)].Stream:=NIL;
    Musik[high(Musik)].Channel:=-1;
    Musik[high(Musik)].isLoad:=False;
  end;  
end;

end.

_________________
MFG
Michael Springwald, "kann kein englisch...."


Zuletzt bearbeitet von mimi am Fr 30.07.04 15:06, insgesamt 2-mal bearbeitet
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 28.07.04 15:52 
Das ist schlecht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
  if FMOD_VERSION > FSOUND_GetVersion then  
  begin  
    ShowMessage(PChar(Format('API version %3.2f is newer than DLL version %3.2f', [FMOD_VERSION, FSOUND_GetVersion])));  
    Halt;  
  end;

Nehmen wir an ich habe ein Spiel mit Sound programmiert. Wenn jetzt was bei der Initialisierung deiner Sound Klasse fehlschläg, dann wird gleich das ganze Programm beendet. Ich hab also noch nicht mal die Chance mein "Vier gewinnt" ohne Sound zu spielen. Und der Programmierer hat noch nicht mal die Möglichkeit eine eigenen Fehlermeldung auszugeben.

Du solltest eine Exception werfen, die dann der Programmierer geielt abfangen kann.

Ebendso hier:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  try  
    if not FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND) then  
      raise Exception.Create('FSOUND_SetOutput failed');  
    if not FSOUND_SetDriver(0then  
      raise Exception.Create('FSOUND_SetDriver failed');  
    if not FSOUND_SetMixer(FSOUND_MIXER_QUALITY_AUTODETECT) then  
      raise Exception.Create('FSOUND_SetMixer failed');  
    if not FSOUND_SetHWND(Handle) then  
      raise Exception.Create('FSOUND_SetHWND failed');  
  except

Spezifische Exceptions würde es den Programmierer einfachher machen Fehler abzufangen. Also zum Beispiel so was:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
type
  ESoundSetOutputError = class(Exception);
  ESoundSetDriverError = class(Exception);
  ...;
  ...;

Undd ann im Code:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
constructor TSound.Create....;
resourcestring
  rsSetOutputError = 'Output konnte nicht initialisiert werden';
  ...;
begin
  ...;
  ...;
  // ohne den try-block. Wozu? Wir wollen ja selbst eine Exception werfen
  if not FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND) then  
    raise ESoundSetOutputError.Create(rsSetOutputError);  
  ...;
end;


Aufruf:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
try
  Sound := TSound.Create....;
  try
    Sound. ...;
  finally
    FreeAndNil(Sound);
  end;
except
  on e: ESoundSetOutputError do ShowMessage(e.Message);
  on e: ESoundSetDriverError do ...
end;


Was die Exceptions angeht kannst du dir mal hier mein Demo dazu ankucken: www.luckie-online.de/Downloads/Demos/
mimi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3458

Ubuntu, Win XP
Lazarus
BeitragVerfasst: Mi 28.07.04 18:49 
Danke für dein vorschlag... kennst du tutors über sowas ? also was die laufzeit fehler angehet ??

_________________
MFG
Michael Springwald, "kann kein englisch...."
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 30.07.04 12:23 
hast du schon mal auf den Link geklickt? da findest zu mindest erstmal eine Demo. Ansonsten arbeite / plane ich gerade ein Exception Tutorial - kann aber noch etwas dauern.
mimi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3458

Ubuntu, Win XP
Lazarus
BeitragVerfasst: Fr 30.07.04 12:28 
ja habe ich*G*

und sonst, was könnte ich noch verbessern, einbauen ??

_________________
MFG
Michael Springwald, "kann kein englisch...."
Nightmare_82
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 260



BeitragVerfasst: Fr 30.07.04 14:32 
Ich habe mir die Unit mal angeschaut und habe ein paar Verbesserungsvorschläge:
mimi hat folgendes geschrieben:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TSound.randomMusik;
begin
  isRandom:=True;
  try
    Randomize;
    PlayMusik(Musik[random(high(musik))].FileName)
  finally
    isRandom:=False;
  end;
end;


hier muß es length(musik) oder high(musik)+1 sein, sonst wird das letzte Lied nie abgespielt.
Ansonsten sollte man Randomize nur einmal aufrufen am Anfang.

Was auch noch gut wäre, wäre die Möglichkeit, die Sounds auch wieder freizugeben. Wenn ich damit einen MP3-Player programmieren würde, wären irgendwann alle Lieder im Speicher.Am besten über ein Flag, das regelt, ob die Lieder, nachdem sie gespielt wurden, freigegeben werden(was man beim Laden jedes Lieds/Sounds mitangibt).

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TSound.StopSong;  
begin  
  if Musik[PlayMusikIndex].Module <> NIL then FMUSIC_StopSong(Musik[PlayMusikIndex].Module);  
  if Musik[PlayMusikIndex].Channel > -1 then FSOUND_StopSound(Musik[PlayMusikIndex].Channel);    
end;

Hier solltest du noch prüfen, ob PlayMusikIndex > -1 ist, sonst gibts eine Zugriffsverletzung.

Ansonsten wäre es nicht schlecht, wenn er bei AddSong und AddMusik prüfen würde, ob die Datei schon geladen wurde. Sonst lädt man leicht Lieder doppelt.
Aber die Klasse sieht ansonsten ziemlich brauchbar aus.
mimi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3458

Ubuntu, Win XP
Lazarus
BeitragVerfasst: Fr 30.07.04 14:43 
randomMusik wird ja nur aufgerufen, wenn ein lied zu ände ist, und so macht es wohl nichts *G*

danke für deine vorschläge, werde ich gleich umsetzen :D

_________________
MFG
Michael Springwald, "kann kein englisch...."