Entwickler-Ecke

Multimedia / Grafik - Soundausgabe "flüssiger"


galagher - Sa 23.04.11 20:37
Titel: Soundausgabe "flüssiger"
Hallo!

Ich habe nach der Anleitung hier: http://forum.chip.de/java-delphi-pascal/musik-project-einbinden-567376.html Soundeffekte in mein Programm eingebunden. Klappt auch, was mich aber stört, ist aber die kleine Pause, die vor dem Abspielen des Sounds (= dauert nur 1 Sekunde) entsteht.

Konkret ist es so, dass die Sound-Ausgabe die visuelle Ausgabe kurz blockiert, dann läuft alles wieder. Da hilft auch Application.ProcessMessages nichts.

Wie kann ich die Soundausgabe "flüssiger" machen?


jaenicke - Sa 23.04.11 20:48

Das dauert eben kurz die Ressource zu laden.

Besser geht das mit der Bass-DLL [http://www.un4seen.com/bass.html]. ;-)


galagher - Sa 23.04.11 22:03

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Das dauert eben kurz die Ressource zu laden.
Es sieht so aus, als ob man das in den Griff kriegt, wenn man gleich bei Programmstart eine Ressource lädt, egal, was, Hauptsache, eine Ressource wird geladen, kann auch "Stille" sein. Keine schöne Lösung, scheint aber zu klappen.

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Besser geht das mit der Bass-DLL [http://www.un4seen.com/bass.html]. ;-)
Danke dafür, werde mir das ansehen!


galagher - Mi 27.04.11 06:41

Weiss jemand, wie man mit mmsystem.pas die Lautstärke regelt oder überhaupt den Sound steuert? Die Beispiele, die ich bisher zur Lautstärke gegooglet habe, funktionierten bei mir nicht.


FrEaKY - Mi 27.04.11 06:49

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Weiss jemand, wie man mit mmsystem.pas die Lautstärke regelt oder überhaupt den Sound steuert? Die Beispiele, die ich bisher zur Lautstärke gegooglet habe, funktionierten bei mir nicht.

Ja. Mit der Multimedia API kannst du direkt die Amplitude der Headerpakete anpassen, die du an die Soundkarte schickst. Effekte hinzufügen, verzerren, was immer du willst. Und das ganz ohne eine DLL.
Allerdings ist das Low Level und dann wird nichts mehr mit "PlaySound" und du musst alles selbst schreiben. Das Ganze ist schon etwas umfangreicher. Aber wenn dich das interessiert kann ich es nur empfehlen.

Sonst bleibt wohl nur der Griff zur bass.dll oder fmod und wie sie alle heißen.

mfg


jaenicke - Mi 27.04.11 07:05

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Weiss jemand, wie man mit mmsystem.pas die Lautstärke regelt oder überhaupt den Sound steuert? Die Beispiele, die ich bisher zur Lautstärke gegooglet habe, funktionierten bei mir nicht.
Im Zuge der starken Erweiterungen bei Windows Vista wurde auch die Sound API grundlegend überarbeitet. Ältere Beispiele werden also nicht mehr funktionieren, die z.B. versuchen eine Hardware direkt in der Lautstärke zu regeln.

Ab Vista ist das ganze virtualisiert, so dass man endlich nur noch die eigene Laufstärke regelt und nicht andere Programme dabei beeinträchtigt. Mit der bass.dll funktioniert das aber eigentlich problemlos. :nixweiss:


galagher - Mi 27.04.11 20:25

Danke erstmal für die Tipps, aber alles selbst zu schreiben ist mir dann doch zu viel!


markus5766h - Mi 27.04.11 20:44

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Weiss jemand, wie man mit mmsystem.pas die Lautstärke regelt oder überhaupt den Sound steuert? Die Beispiele, die ich bisher zur Lautstärke gegooglet habe, funktionierten bei mir nicht.



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:
function GetMasterVolume(Mixer: hMixerObj): Word;
var
  MasterVolume    : TMixerControl;
  Details         : TMixerControlDetails;
  UnsignedDetails : TMixerControlDetailsUnsigned;
  Code            : MMResult;
begin
  Result := 0;

  Code := _VolumeControl(Mixer, MasterVolume);
  if Code = MMSYSERR_NOERROR then begin
    with Details do begin
      cbStruct := SizeOf(Details);
      dwControlID := MasterVolume.dwControlID;
      cChannels := 1;  // set all channels
      cMultipleItems := 0;
      cbDetails := SizeOf(UnsignedDetails);
      paDetails := @UnsignedDetails;
    end;
    Code := mixerGetControlDetails(Mixer,
                                   @Details,
                                   MIXER_GETCONTROLDETAILSF_VALUE);

    Result := UnsignedDetails.dwValue;
  end;
  if Code <> MMSYSERR_NOERROR then
    raise Exception.CreateFmt('GetMasterVolume failure, '+
                       'multimedia system error #%d', [Code]);
end;

procedure SetMasterVolume(Mixer: hMixerObj; Value: Word);
var
  MasterVolume    : TMixerControl;
  Details         : TMixerControlDetails;
  UnsignedDetails : TMixerControlDetailsUnsigned;
  Code            : MMResult;
begin
  Code := _VolumeControl(Mixer, MasterVolume);
  if Code = MMSYSERR_NOERROR then begin
    with Details do begin
      cbStruct := SizeOf(Details);
      dwControlID := MasterVolume.dwControlID;
      cChannels := 1;  // set all channels
      cMultipleItems := 0;
      cbDetails := SizeOf(UnsignedDetails);
      paDetails := @UnsignedDetails;
    end;
    UnsignedDetails.dwValue := Value;
    Code := mixerSetControlDetails(Mixer,
                                   @Details,
                                   MIXER_SETCONTROLDETAILSF_VALUE);
  end;
  if Code <> MMSYSERR_NOERROR then
    raise Exception.CreateFmt('SetMasterVolume failure, '+
                         'multimedia system error #%d', [Code]);
end;


. . . weitere Infos in der MMSystem-Unit

läuft auf XP
Vista - keine Ahnung, hab ich nie gehabt
Win7 - definitiv nein


galagher - Mi 27.04.11 21:40

user profile iconmarkus5766h hat folgendes geschrieben Zum zitierten Posting springen:

läuft auf XP
Vista - keine Ahnung, hab ich nie gehabt
Win7 - definitiv nein

Habe XP, und es klappt auch - danke! Natürlich würde ich mein Programm gerne unabhängiger von der Windows-Version machen und daher auch Win7-kompatiblen Code verwenden. Werde mal googlen, auch wenn ich's nicht testen kann.

//Edit: Eine Alternative ist auch, aus dem Programm heraus den Windows-Lautstärkenregler zu öffnen, das wäre sogar besser, da das dann ja jedenfalls funktioniert!

Und da haben wir's ja auch schon:

Delphi-Quelltext
1:
ShellExecute(Handle, nil'SNDVOL32.EXE''/t''', SW_SHOWNA);                    

Frage: klappt dieser Aufruf auch unter Win7?


jaenicke - Mi 27.04.11 22:06

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Habe XP, und es klappt auch - danke!
Ist aber meilenweit schlechter als eine Lautstärkeregelung mit der bass.dll selbst. Denn so regelst du die Lautstärke systemweit für das Gerät. Einer der größten Nervfaktoren unter XP damals...

Mit der bass.dll regelst du dagegen die Lautstärke direkt.

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Natürlich würde ich mein Programm gerne unabhängiger von der Windows-Version machen und daher auch Win7-kompatiblen Code verwenden.
Dort gibt es keine hardwarenahe Regelung für ein Gerät wie unter XP.

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Frage: klappt dieser Aufruf auch unter Win7?
Nein, denn du hast da ein 32 zu viel. Unter Windows 7 64-Bit lautet der Aufruf nur sndvol.exe. ;-)


galagher - Mi 27.04.11 22:20

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Nein, denn du hast da ein 32 zu viel. Unter Windows 7 64-Bit lautet der Aufruf nur sndvol.exe. ;-)
Mit denselben Parametern /s (Lautstärkeregler in einem kleineren Fenster) und /t (Lautstärkeeinstellung ohne die anderen Funktionen)?


jaenicke - Mi 27.04.11 22:25

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Nein, denn du hast da ein 32 zu viel. Unter Windows 7 64-Bit lautet der Aufruf nur sndvol.exe. ;-)
Mit denselben Parametern /s (Lautstärkeregler in einem kleineren Fenster) und /t (Lautstärkeeinstellung ohne die anderen Funktionen)?
Es gibt unter Windows 7 nur noch eine applikationsbezogene Regelung. Da gibt es nur ein Fenster dafür. Die Parameter werden ignoriert.

SndVolWin7


galagher - Do 28.04.11 18:06

Ok, danke euch!

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Mit der bass.dll regelst du dagegen die Lautstärke direkt.
Habe die bass.dll-Dateien in einen Delphi-Unterordner kopiert, den Pfad zur bass.dll hinzugefügt, trotzdem wurde die bass.dll nicht gefunden. Habe aber auch kein Delphi-Package oder Setup gefunden.

Ich habe das jetzt so gelöst, dass unter Win7 sndvol.exe aufgerufen wird, unter XP kann man die Lautstärke mit meinem Programm regeln. Zwar systemweit, aber für meine Zwecke hier reicht's!


jaenicke - Do 28.04.11 19:02

Du kannst die bass.dll einfach in das Verzeichnis deiner Exe legen. Du musst dann die Soundausgabe aber auch damit machen, aber das macht vieles ohnehin viel einfacher.


markus5766h - Do 28.04.11 19:08

zur Vervollständigung : Lautstärke regeln unter Win7 - TEndpointVolume


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:
unit 'was auch immer . . .';

interface

uses
  . . . ActiveX, MMDeviceApi;

const 
  CLSID_TaskbarList: TGUID = '{56fdf344-fd6d-11d0-958a-006097c9a090}'
  IID_ITaskbarList:  TGUID = '{56FDF342-FD6D-11d0-958A-006097C9A090}'
  IID_ITaskbarList2: TGUID = '{602D4995-B13A-429b-A66E-1935E44F4317}'
  IID_ITaskbarList3: TGUID = '{ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf}';

type 
  TBPF =   (TBPF_NOPROGRESS = 0,
           TBPF_INDETERMINATE = 1,
           TBPF_NORMAL = 2,
           TBPF_ERROR = 4,
           TBPF_PAUSED = 8);
  TBATF =  (TBATF_USEMDITHUMBNAIL = 1,
           TBATF_USEMDILIVEPREVIEW = 2);

   ITaskbarList = interface(IUnknown)
      ['{56FDF342-FD6D-11d0-958A-006097C9A090}']
      function HrInit : HResult; stdcall;
      function AddTab(hWndOwner : HWND) : HResult; stdcall;
      function DeleteTab(hWndOwner : HWND) : HResult; stdcall;
      function ActivateTab(hWndOwner : HWND) : HResult; stdcall;
      function SetActiveAlt(hWndOwner : HWND) : HResult; stdcall;
   end{ ITaskbarList }

  ITaskbarList2 = interface(ITaskbarList)
    ['{602D4995-B13A-429b-A66E-1935E44F4317}']
    function MarkFullscreenWindow(wnd : HWND; fFullscreen : bool) : HResult; stdcall;
  end;

  ITaskbarList3 = interface (ITaskbarList2)
  ['{ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf}']
    function SetProgressValue (hWnd: HWND; ullCompleted: int64; ullTotal: int64): HResult; stdcall;
    function SetProgressState (hWnd: HWND; tbpFlags: TBPF): HResult; stdcall;
    function RegisterTab (hwndTab: HWND; hwndMDI: HWND): HResult; stdcall;
    function UnregisterTab (hwndTab: HWND): HResult; stdcall;
    function SetTabOrder (hwndTab: HWND; hwndInsertBefore: HWND): HResult; stdcall;
    function SetTabActive (hwndTab: HWND; hwndMDI: HWND; tbatFlags: TBATF): HResult; stdcall;
    function ThumbBarAddButtons (hWnd: HWND; cButtons: integer; pButtons: pointer): HResult; stdcall;
    function ThumbBarUpdateButtons (hWnd: HWND; cButtons: cardinal; pButtons: pointer): HResult; stdcall;
    function ThumbBarSetImageList (hWnd: HWND; himl: pointer): HResult; stdcall;
    function SetOverlayIcon (hWnd: HWND; hIcon: HICON; pszDescription: PWideChar): HResult; stdcall;
    function SetThumbnailTooltip (hWnd: HWND; pszTip: PWideChar): HResult; stdcall;
    function SetThumbnailClip (hWnd: HWND; prcClip: PRect): HResult; stdcall;
  end;

type
    TForm1 = class(TForm)


    procedure cbMasterMuteClick(Sender: TObject);
    procedure FormShow(Sender: TObject);

    procedure SetMasterVolume;
    procedure SetChannelsVolume;
    procedure SetChannelsFlagVolume;

   private
    { Private-Deklarationen }
                FTaskBarList : ITaskbarList3;

  public
    { Public-Deklarationen }
  end;

type
 TepVolEvents = Procedure(f:byte) of object;


var
  Form1: TForm1;
  endpointVolume    : IAudioEndpointVolume = nil;
  deviceEnumerator  : IMMDeviceEnumerator;
  defaultDevice     : IMMDevice;
  pProps            : IPropertyStore;
  epVolEvents       : TepVolEvents;
  VolumeLevel       : Single;
  OldVolume         : Single;


implementation

{$R *.dfm}

var
  ChannelCnt               : Cardinal;
  ChannelLeft,ChannelRight : Single;
  VolMaster                : Single;
  VolMute                  : Bool;
  Percent                  : Integer;
  ChannelFlag              : Boolean;


procedure TForm1.FormCreate(Sender: TObject);
var
  i : Integer;
begin
  FTaskBarList := CreateComObject(CLSID_TaskbarList)  as  ITaskbarList3;
  ChannelFlag := False;

  CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, deviceEnumerator);
end;

procedure TForm1.cbMasterMuteClick(Sender: TObject);
begin

  if cbMasterMute.Checked then
    begin

      if endpointVolume = nil then Exit;
      VolumeLevel := 0;
      endpointVolume.SetMasterVolumeLevelScalar(VolumeLevel, nil);
      FTaskBarList.SetProgressState(Application.handle, TBPF_Indeterminate);
    end
      else
        begin
          if endpointVolume = nil then Exit;
          if not ChannelFlag then
            begin // not ChannelFlag
              VolumeLevel := OldVolume;
              endpointVolume.SetMasterVolumeLevelScalar(VolumeLevel, nil);
            end
              else  // ChannelFlag
                begin
                  SetChannelsFlagVolume;
                end;

        end;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  if deviceEnumerator = nil then begin ShowMessage('NIL'); Exit; end;
  deviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, defaultDevice);
  if defaultDevice <> nil then defaultDevice.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, endpointVolume);
  endpointVolume.GetChannelCount(ChannelCnt);
  endpointVolume.GetMasterVolumeLevelScalar(VolMaster);
  VolumeLevel := VolMaster;
  endpointVolume.GetChannelVolumeLevelScalar(0,ChannelLeft);
  endpointVolume.GetChannelVolumeLevelScalar(1,ChannelRight);
  vLeft  := ChannelLeft;
  vRight := ChannelRight;
  endpointVolume.GetMute(VolMute);
  if VolMute=True then cbMasterMute.Checked := True else cbMasterMute.Checked := False;
  lbMaster.Caption := Format('%1.3f', [VolMaster]);

  Percent := Round(VolumeLevel * 100);
  // FTaskBarList.SetProgressState(Application.handle, TBPF_Normal);
  case Round(VolumeLevel * 1000of
   0 .. 749    : FTaskBarList.SetProgressState(Application.handle, TBPF_Normal);
   750 .. 1001 : FTaskBarList.SetProgressState(Application.handle, TBPF_Error);
  end// end case of
  FTaskBarList.SetProgressValue(Application.handle, Percent, 100);
end;


procedure TForm1.FormMouseWheelDown(Sender: TObject; Shift: TShiftState;
  MousePos: TPoint; var Handled: Boolean);
begin
  if reg1 then
    begin
      if imgRegler1.Top > 20 then imgRegler1.Top := imgRegler1.Top - wheel;
      VolumeLevel := ( (200 - (imgRegler1.Top - 20)) / 200);
      SetMasterVolume;
    end;
  if reg2 or reg3 then
    begin
      if reg2 then
        if imgLeft.Top > 20 then imgLeft.Top := imgLeft.Top - wheel;
      if reg3 then
        if imgRight.Top > 20 then imgRight.Top := imgRight.Top - wheel;

      vLeft    := ( (200 - (imgLeft.Top - 20)) / 200);
      vRight   := ( (200 - (imgRight.Top - 20)) / 200);
      SetChannelsVolume;
    end;
end;

procedure TForm1.FormMouseWheelUp(Sender: TObject; Shift: TShiftState;
  MousePos: TPoint; var Handled: Boolean);
begin
  if reg1 then
    begin
      if imgRegler1.Top < 220 then imgRegler1.Top := imgRegler1.Top + wheel;
      VolumeLevel := ( (200 - (imgRegler1.Top - 20)) / 200);
      SetMasterVolume;
    end;
  if reg2 or reg3 then
    begin
      if reg2 then
        if imgLeft.Top < 220 then imgLeft.Top := imgLeft.Top + wheel;
      if reg3 then
        if imgRight.Top < 220 then imgRight.Top := imgRight.Top + wheel;

      vLeft  := ( (200 - (imgLeft.Top - 20)) / 200);
      vRight := ( (200 - (imgRight.Top - 20)) / 200);
      SetChannelsVolume;
    end;
end;

procedure TForm1.SetMasterVolume;
begin
  if endpointVolume = nil then Exit;
  cbMasterMute.Checked := False;
  OldVolume   := VolumeLevel;
  OldLeft  := vLeft;
  OldRight := vRight;
  endpointVolume.SetMasterVolumeLevelScalar(VolumeLevel, nil);
  lbMaster.Caption := Format('%1.3f', [VolumeLevel]);

end;

procedure TForm1.SetChannelsVolume;
begin
  if endpointVolume = nil
    then Exit;

  endpointVolume.SetChannelVolumeLevelScalar(0, vLeft, nil);
  endpointVolume.SetChannelVolumeLevelScalar(1, vRight, nil);
  lbLeft.Caption  := Format('%1.3f', [vLeft * 1]);
  lbRight.Caption := Format('%1.3f', [vRight * 1]);
  SetMasterVolume;
end;

procedure TForm1.SetChannelsFlagVolume;
begin
  if endpointVolume = nil
    then Exit;

  endpointVolume.SetChannelVolumeLevelScalar(0, OldLeft, nil);
  endpointVolume.SetChannelVolumeLevelScalar(1, OldRight, nil);
  lbLeft.Caption  := Format('%1.3f', [OldLeft * 1]);
  lbRight.Caption := Format('%1.3f', [OldRight * 1]);
  //SetMasterVolume;
end;


ich habe seinerzeit die Lautstärkeregelung per Mouse-Wheel vorgenommen, lässt sich aber
für die eigene Verwendung ändern.
Die Deklarationen und Funktionen zu TTaskbar ... werden nicht unbedingt benötigt.


markus5766h - Do 28.04.11 19:20

[quote="user profile icongalagher"(639296)]
Habe XP, und es klappt auch - danke! Natürlich würde ich mein Programm gerne unabhängiger von der Windows-Version machen und daher auch Win7-kompatiblen Code verwenden. Werde mal googlen, auch wenn ich's nicht testen kann.
[quote]

eine Funktion die auf XP und Win7 funktioniert wirst Du nicht finden -
2 Möglichkeiten
1) Version abfragen und entsprechende Funktionen nutzen
2) bass.dll benutzen und nur die eigene Quelle regeln


galagher - Do 28.04.11 21:44

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Besser geht das mit der Bass-DLL [http://www.un4seen.com/bass.html]. ;-)
Habe mir das heruntergeladen und ins Projekt eingebunden. Und es funktioniert - natürlich - nicht. :autsch:

In der Hilfe steht:
Zitat:
BASS_ChannelSetAttribute(
DWORD handle,
BASS_ATTRIB_VOL,
float volume
);


Parameters
handle The channel handle.
volume The volume level... 0 (silent) to 1 (full).

Ich habe also die Hilfe weiter befragt und auch etwas gefunden, aber das klappt auch nicht:

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  ch: HCHANNEL; ch := BASS_SampleGetChannel(0, False);
begin
  BASS_ChannelSetAttribute(ch, BASS_ATTRIB_PAN, 0);  //welcher Wert, 0, 1, oder egal?
  BASS_ChannelSetAttribute(ch, BASS_ATTRIB_VOL, 100);  //welcher Wert?

Ich habe keine Ahnung, welche Werte da rein sollen. Ich will doch nur lauter oder leiser machen!


jaenicke - Do 28.04.11 21:53

Du hast es doch selbst zitiert: :gruebel:
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
volume The volume level... 0 (silent) to 1 (full).


jaenicke - Do 28.04.11 22:29

Ok, wenn es denn wirklich so schwierig für dich ist... im Anhang liegt ein komplettes ganz simples Beispiel. Einfach mit dem Button eine MP3 auswählen.

Simpler Player mit Lautstärkeregelung (zip, 655.04 KB)


galagher - Fr 29.04.11 16:48

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Ok, wenn es denn wirklich so schwierig für dich ist...
Danke, das funktioniert, aber ich glaube, wir reden da aneinander vorbei: Ich möchte nicht die Lautstärke für eine bestimmte Sounddatei ändern, sondern für beliebige viele Klangeffekte, die in meine exe-Datei als Ressource eingebunden sind und die ich bei Bedarf mit SndPlaySound lade.

Der Aufruf BASS_SetVolume bewirkt in meinem Programm gar nichts. Und wenn ich keine externe Sounddatei lade, was ist dann das Handle in BASS_ChannelPlay?

Ich bin nicht unwillig, selbst etwas zu tun, habe aber im Moment keine Idee! Welche Funktionen von bass.pas muss ich verwenden, wenn ich die Lautstärke nur für mein Programm ändern will?


jaenicke - Fr 29.04.11 17:05

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
sondern für beliebige viele Klangeffekte, die in meine exe-Datei als Ressource eingebunden sind und die ich bei Bedarf mit SndPlaySound lade.
Das Laden musst du dann schon über die Bass.dll machen, dafür ist die da. ;-)

Du kannst zum Beispiel über TResourceStreams die Ressourcen in den Speicher laden, dort bereit halten und deren Daten dann mit BASS_StreamCreate zum Abspielen nutzen.

Eine andere Möglichkeit gibt es nicht. Entweder du lässt die bass.dll alles machen, abspielen, Klangeffekte (Nachhall, ...) und Lautstärkeregelung oder du kümmerst dich je nach Betriebssystem um die Lautstärkeregelung, was aber erst ab Vista anwendungsbezogen funktioniert.


galagher - Fr 29.04.11 17:14

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Entweder du lässt die bass.dll alles machen, abspielen, Klangeffekte (Nachhall, ...) und Lautstärkeregelung oder du kümmerst dich je nach Betriebssystem um die Lautstärkeregelung, was aber erst ab Vista anwendungsbezogen funktioniert.
Ok, dann ist das klar, das es so halb-halb nicht geht.

Habe aber soeben hier -> http://entwickler-forum.de/showthread.php?t=27605 waveOutSetVolume entdeckt:

Delphi-Quelltext
1:
waveOutSetVolume(0, MakeLong(iVolume, iVolume));                    

Das ist anwendungsbezogen, aber funktioniert's auch unter Win7?
//Edit: doch nicht anwendungsbezogen :?


ALF - Fr 29.04.11 17:19

so geht das auch nicht.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
 chan1:  HSTREAM;//<----- diese Variable ist wichtig, sonnst geht es auch nicht mit bass.dll
 song: String;
....
....

song:= 'Deine SoundDatei';
chan1 := BASS_StreamCreateFile(False, pchar(song),00,
            BASS_MUSIC_STOPBACK {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});

Bass_ChannelPLay(chan1, false):
BASS_ChannelSetAttribute(chan1, BASS_ATTRIB_VOL, 1);//<-- je nach auflösung 0, 0.01, 0.02 bis 1


Willst du mehherer Songs zur gleichen Zeit abspielen, dann halt

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
chan1 := BASS_StreamCreateFile(False, pchar(song1),00,
            BASS_MUSIC_STOPBACK {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
chan2 := BASS_StreamCreateFile(False, pchar(song2),00,
            BASS_MUSIC_STOPBACK {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
chan3 := BASS_StreamCreateFile(False, pchar(song3),00,
            BASS_MUSIC_STOPBACK {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});

oder ein HSTREAM array einsetzen und über das Array weiter arbeiten.
und für jeden 'chan' kannst Du nun auch die lautstärke seperat regeln zusammen mischen oder mit effekten drauflegen!

aber steht ja auch in der Bass Hilfe mit drin. :wink:
Gruss Alf


galagher - Fr 29.04.11 17:47

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
so geht das auch nicht.
Aber wie in deinem Beispiel auch nicht!

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
aber steht ja auch in der Bass Hilfe mit drin. :wink:
Genau, und deshalb werde ich da mal (aber nicht jetzt :mrgreen: ) durchsteigen, das wird sonst nichts.

Danke an euch!


ALF - Fr 29.04.11 18:09

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
so geht das auch nicht.
Aber wie in deinem Beispiel auch nicht!

Dann dürfte mein ganzer mxplayer nicht funktionieren :wink:

Ich gehe davon aus das du

Delphi-Quelltext
1:
2:
 if not BASS_Init(1441000, Handle, nilthen
      Error('Error initializing audio!');
im Create der Form schon drin hast!
sonnst funct natürlich gar nix.

Gruss ALf


galagher - Fr 29.04.11 18:24

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Ich gehe davon aus das du

Delphi-Quelltext
1:
2:
 if not BASS_Init(1441000, Handle, nilthen
      Error('Error initializing audio!');
im Create der Form schon drin hast!
Ja, jetzt schon! :oops:
Ok, und wenn ich dich noch fragen darf: Wie lade ich die Sounds nicht als Datei, sondern als Ressource meiner exe?

Ich habe also aus einer Datei sound.rc eine sound.res gemacht und diese mit {$R sound.res} eingebunden. Nun möchte ich mit bass eine dieser Ressourcen abspielen. Wie mache ich das?


jaenicke - Fr 29.04.11 18:48

Wie ich schrieb:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Du kannst zum Beispiel über TResourceStreams die Ressourcen in den Speicher laden, dort bereit halten und deren Daten dann mit BASS_StreamCreate zum Abspielen nutzen.
Das habe ich noch nie genutzt, sieht aber rein von den Parametern der Funktion richtig aus (keine Zeit in die Hilfe zu schauen grad). ;-)


ALF - Fr 29.04.11 19:49

Hier hast Du mich natürlich auch auf den linken Fuss erwischt :oops:
Verwendet selber hab ich es nie (brauche es nicht).
Wie das nun ist wenn eine fertige wav/mp3 im speicher ist(incl header usw)?

Wenn ich mich aber richtig erinnerre musst Du die calback funktion Filereadproc oder streamproc benutzen, da Du die Daten ja schon im Speicher hast.
Also das was @jaenicke schon gesagt hat, mit TResourceStreams, und den erhaltenen Buffer übergibst Du an die funktion.

Getestet hab ich leider selber noch nicht.

Dazu müsstest Du aber BASS_StreamCreate oder BASS_StreamCreateFileUser benutzen.

Denn BASS_StreamCreateFile geht nur mit Dateien.


Gruss ALf


galagher - Fr 29.04.11 20:03

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Dazu müsstest Du aber BASS_StreamCreate oder BASS_StreamCreateFileUser benutzen.
Das ist, wenn ich mir die Beispiel-pas ansehe, mehr verwirrend als hilfreich. Ich meine, dass es langsamer ist, wenn die Datei erst von der Platte gelesen werden muss.
Da kann auf im Speicher gehaltene Daten natürlich schneller zugegriffen werden, ich möchte verhindern, dass eine sichtbare Pause im Programm entsteht.

Aber ich blicke da nicht durch.


jaenicke - Fr 29.04.11 20:39

Vom Handy aus, dehalb nur kurz:
Die von mir genannte Funktion bekommt eine Callback Funktion als Parameter. Darin musst du die Daten bereitstellen.

Am einfachsten lade die Daten mit Hilfe eines TResourceStreams in einen TMemoryStream und lasse den offen.

In der Callback Funktion kannst du die Daten einfach aus dem Stream holen und liefern.


galagher - Fr 29.04.11 20:47

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Vom Handy aus, dehalb nur kurz:
Die von mir genannte Funktion bekommt eine Callback Funktion als Parameter. Darin musst du die Daten bereitstellen.

Am einfachsten lade die Daten mit Hilfe eines TResourceStreams in einen TMemoryStream und lasse den offen.

In der Callback Funktion kannst du die Daten einfach aus dem Stream holen und liefern.

Ist wirklich toll von euch, wie ihr mir helfen wollt, aber ich verstehe nur Bahnhof. Muss mich zuerst mit den Begriffen "TResourceStream", "TMemoryStream", "Callback" auseinandersetzen (was ist das? Wie wendet man es an?), und dann versuchen, daraus was zu machen.


jaenicke - Fr 29.04.11 21:00

TResourceStream: Laden von Ressourcen aus der Exe, TMemoryStream: ein Stream mit Daten, die im Speicher liegen, die beiden sollten kein Problem sein.

Ein Callback dient dazu, der Funktion eine eigene Funktion mitzugeben, die diese aufrufen kann. Zum Beispiel wie hier um weitere Daten abzufragen.

Such einmal nach Beispielen zu EnumWindows. Da gibt es auch einen Callback.


galagher - Sa 30.04.11 11:16

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
TResourceStream: Laden von Ressourcen aus der Exe, TMemoryStream: ein Stream mit Daten, die im Speicher liegen, die beiden sollten kein Problem sein.
Bis TResourceStream bin ich ja schon gekommen, nur was ich dann damit machen soll und wie es weiter geht, weiss ich nicht.

Das hat so keinen Sinn, der ganze Aufwand, nur um anwendungsbezogen laut/leise zu drehen, steht doch nicht dafür! Ich habe ja bereits ein Konzept, das Sounds abspielt (nur halt so laut, wie sie eben sind bzw. wie die Lautstärke systemweit eingestellt ist). Wenn die Lautstärkeregelung nicht einfach zu bewerkstelligen ist, ist sie Nebensache.

Ich komme mit bass.dll ohne deutsche Hilfe und Beispiele einfach nicht weiter.


ALF - Sa 30.04.11 12:27

Vor Montag kann ich leider nicht weiter helfen (bin nicht home).
Aber Du wirst sehen das dies dann relativ überschbar bleibt.

Gruss Alf


galagher - Sa 30.04.11 14:29

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Vor Montag kann ich leider nicht weiter helfen
Mein Projekt ist ja noch nicht beendet, also von daher... :mrgreen:


ALF - So 01.05.11 11:46

So nun hab ich noch ein bisschen Zeit.

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:
{je nach dem wieviel sounds Du einbindest, solltest Du diese evtl gleich mit ein TypArray umsetzten} 
  {TMySong = Array[1..bisx] of TResourceStream}
  {TMyMem = Array[1..bisx] of Pointer}
  {TMyPlay = Array[1..bisx] of HSTREAM}
  {TResourceId = Array[1..bisx] of Cardinal}
  {TMyVolume = Array[1..bisx] of float}
{vorteil ist klar, Du brauchst dann nur noch mit den index der Arrays arbeiten}
{vielleicht gibt es noch ne effizientere methode????}

{ab hier gilt es für einen song}
private
  mySong: TResourceStream;
  myMem: Pointer;
  myPlay: HSTREAM;
  myVolume: float;
  resourceID: Cardinal;

....
....
....

procedure TForm1.FormCreate(Sender: TObject);
begin
  resourceID:= 1;
  {leider kann man mit hinstance, bzw mySong nicht direct mit Bass arbeiten,}
  {wie es das mmsystem kann}
  {darum der Umweg über Speicherreservierung und das laden der Daten in den Speicher}
  mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND');
  GetMem(myMem, mySong.Size);
  mysong.Read(myMem^, mySong.Size);

end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
   {nicht vergessen alles zum schluss frei zugeben}
   Bass_Stop;
   Bass_Free;
   FreeMem(myMem);
   mySong.Free;

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    {dies währe mit dem mmsystem, aber da kannst Du nur volume Global regeln}
    //PlaySound(PChar(1),HInstance, snd_ASync or snd_Memory or snd_Resource);
    
    myVolume:= 0.2;//<-- von 0 bis 1
    myPlay:= BASS_StreamCreateFile(True, myMem , 0, mySong.Size, BASS_STREAM_AUTOFREE);
    BASS_ChannelSetAttribute(myPlay, BASS_ATTRIB_VOL, myVolume);
    BASS_ChannelPlay(myPlay, False)
end;
....
....
....

dies ist wie gesagt für ein song(wave Datei)
Hoffe nix übersehen zu haben!
Doch hab ich. :oops:
Es gibt keine Fehlerbehandlung!
- hier TResourceStream.Create (Try block)
- und alles was mit Bass gemacht wird(steht in der Help!


Mit der bass kannst Du nun noch links, rechts, souround(7.1) ansteuern um für dein Spiel schöne effekte zu machen 8)

Gruss Alf

Ps: Bin leider kein Profi um das evtl effektiver oder besser zu machen!
Da beneide ich alle die, die das können.


turboPASCAL - So 01.05.11 18:44

Ich habe mir eine kleine Classe gebastelt die den Umgang mit der Bass.dll ein wenig erleichtert.

Im Demo schaut es dann so aus:


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:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Dynamic_Bass24, SoundFX, XPMan, StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    TrackBar1: TTrackBar;
    TrackBar2: TTrackBar;
    TrackBar3: TTrackBar;
    Label1: TLabel;
    Label2: TLabel;
    Button4: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ButtonsClick(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure TrackBar2Change(Sender: TObject);
    procedure TrackBar3Change(Sender: TObject);
  private
    { Private-Deklarationen }
    SndFX: TSndFX;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // yes, sounds...
  SndFX := TSndFX.Create;
  try
    SndFX.SetChannels(4);
    SndFX.LoadSndFromResource(hInstance, MAKEINTRESOURCE(1), RT_RCWAVE, 0);
    SndFX.LoadSndFromResource(hInstance, MAKEINTRESOURCE(2), RT_RCWAVE, 1);
    SndFX.LoadSndFromResource(hInstance, MAKEINTRESOURCE(3), RT_RCWAVE, 2);
    SndFX.LoadSndFromResource(hInstance, MAKEINTRESOURCE(1), RT_RCDATA, 3);

    SndFX.LoadSndFromFile(PCHAR(ExtractFilePath(ParamStr(0))+'\music.ogg'), 4, BASS_SAMPLE_LOOP);
    SndFX.SetChannelVol(425);
    SndFX.PlayChannel(4);

    TrackBar1.Position := 25;
  except
    on Exception do Exception.CreateFmt('Zonk! %s', ['Fehler bei SndFX.Create']);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if ASSIGNED(SndFX) then SndFX.Free;
end;

procedure TForm1.ButtonsClick(Sender: TObject);
begin
  if ASSIGNED(SndFX) then
    case (Sender as TButton).Tag of
      10: SndFX.PlayChannel(0);
      20: SndFX.PlayChannel(1);
      30: SndFX.PlayChannel(2);
      40: SndFX.PlayChannel(3);
    end;
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  SndFX.SetChannelVol(4, TrackBar1.Position);
end;

procedure TForm1.TrackBar2Change(Sender: TObject);
begin
  if ASSIGNED(SndFX) then
    SndFX.SetChannelVol(2, TrackBar2.Position);
end;

procedure TForm1.TrackBar3Change(Sender: TObject);
begin
  if ASSIGNED(SndFX) then
    SndFX.SetGlobalVol(TrackBar3.Max - TrackBar3.Position);
end;

end.


galagher - Mo 02.05.11 10:39

@user profile iconALF:
mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND'); verursacht eine Zugriffsverletzung.
Aber vielleicht kriege ich es ja doch noch hin.

@user profile iconturboPASCAL:
SndFX.LoadSndFromFile(PCHAR(ExtractFilePath(ParamStr(0))+'\music.ogg'), 4, BASS_SAMPLE_LOOP); Damit wird ja eine Datei geladen, genau das möchte ich nicht, sondern Sounds, die Resourcen sind, nutzen!


ALF - Mo 02.05.11 11:21

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
@user profile iconALF:
mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND'); verursacht eine Zugriffsverletzung.
Aber vielleicht kriege ich es ja doch noch hin.

Das hängt wahrscheinlich davon ab was Du einbindest (Wave oder mp3).
Bzw was Du hierfür stehen hast 'SOUND' oder 'WAVE' oder 'Musik' etc. (wenn Du wave format verwendest)

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
@user profile iconturboPASCAL:
SndFX.LoadSndFromFile(PCHAR(ExtractFilePath(ParamStr(0))+'\music.ogg'), 4, BASS_SAMPLE_LOOP); Damit wird ja eine Datei geladen, genau das möchte ich nicht, sondern Sounds, die Resourcen sind, nutzen!
Diese stelle must Du ja auch nicht benutzten :wink:
Sondern die anderen 4 Zeilen, wo er die aus resource laden tut (SndFX.LoadSndFromResource).
Er zeigt Dir aber damit, das beides genauso schnell ist!

PS. Nur so am Rande bemerkt.
Ob es nun Sinn macht die AudioDateien in eine res zu packen :gruebel: , wegen der Bass.DLL.
Die musst Du auch mit geben, wenn Du dein Spiel jemanden geben willst!
Man muss also nicht die Audiodateien unbedingt in die exe packen, sondern ganz normal im create deiner Form laden und abspielen dann, wenn du sie benötigst.
Geht also genauso schnell :!:
Vorteil ist der, willst Du sonnds hinzufügen/ändern, musst Du nicht jedesmal ein Res erzeugen.

Gruss Alf


galagher - Mo 02.05.11 12:35

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Das hängt wahrscheinlich davon ab was Du einbindest (Wave oder mp3).
Bzw was Du hierfür stehen hast 'SOUND' oder 'WAVE' oder 'Musik' etc. (wenn Du wave format verwendest)
Ja, ich verwende WAVE, es funktioniert aber leider auch mit 'WAVE' nicht.

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Diese stelle must Du ja auch nicht benutzten :wink:
Sondern die anderen 4 Zeilen, wo er die aus resource laden tut (SndFX.LoadSndFromResource).
Er zeigt Dir aber damit, das beides genauso schnell ist!

Ich habe beide Codes einfach nur ausprobiert (also, ja, ich geb's zu, copy & paste :oops: ). Sehe mir das aber nochmal an.

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Ob es nun Sinn macht die AudioDateien in eine res zu packen :gruebel: , wegen der Bass.DLL.
Die musst Du auch mit geben, wenn Du dein Spiel jemanden geben willst!
Muss ich die in jedem Fall mitgeben, also auch dann, wenn ich die Sounds als Dateien lade?

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Man muss also nicht die Audiodateien unbedingt in die exe packen, sondern ganz normal im create deiner Form laden und abspielen dann, wenn du sie benötigst.
Stimmt, ja, alle zuerst laden, dann sind sie im Speicher und können jederzeit abgespielt werden.

Wie gesagt, ich schaue mir das alles noch genau an, habe aber jetzt im Moment keine Zeit. Melde mich dann wieder! Mal sehen, ob ich das hinbekomme!
Vielen Dank!


jaenicke - Mo 02.05.11 12:37

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Muss ich die in jedem Fall mitgeben, also auch dann, wenn ich die Sounds als Dateien lade?
Ja, natürlich.


ALF - Mo 02.05.11 13:09

Es gibt ebend Vor- und Nachteile :wink:
Benutzt Du nur das mmsystem und die Audiodateien im Res,brauchst Du zwar nix mitliefern, kannst aber nur global die Lautstärke ändern!

Mit Bass.DLL kannst Du viele tolle sachen machen, must sie aber mit ausliefern sonst kommt kein sound.
Naja gut, kann man auch in eine res packen, dann extraieren und auf Platte speichern. Aber das muss jeder für sich selbst entscheiden, wie transportabel er sein Prog schreibt.

Gruss Alf


galagher - Di 03.05.11 17:29

mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND'); verursacht immer eine Zugriffsverletzung, und user profile iconturboPASCAL's Beispiel kann ich nicht anwenden, weil ich die SoundFX.pas nicht habe.

Aber egal, wie gesagt, der ganze Aufwand, nur um die Lautstärke anwendungsbezogen ändern zu können, zahlt sich nicht aus, daher bleibe ich bei den Sounds als Resourcen.

Überlegenswert ist das Laden beim Programmstart aber allemal!


ALF - Di 03.05.11 17:56

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND'); verursacht immer eine Zugriffsverletzung,..
Dann stimmt Deine RC Datei nicht bzw der ResEditor macht was falsch?

Versuche es mal mit dem mmsystem und resource.
Beispiel meiner rc:
1 SOUND "E:\Programme\Borland\Delphi7\Projects\Bimel 2x.wav"

Delphi-Quelltext
1:
2:
//pchar(der name den du verwendest oder wenn du mit zahlen aggierst die zahl,wie ich mit der 1
PlaySound(PChar(1),hInstance, snd_ASync or snd_Memory or snd_Resource);

Wenn es dann nicht funct machst Du was falsch :P

Gruss ALf


galagher - Mi 04.05.11 17:15

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND'); verursacht immer eine Zugriffsverletzung,..
Dann stimmt Deine RC Datei nicht bzw der ResEditor macht was falsch?

Ich weiss schon, woran es liegt: Es müssen natürlich Zahlen sein, die in ResourceID gespeichert werden. Wenn ich Namen nützen will, kann ich diese als Konstanten definieren. Jedenfalls so funktioniert es!


ALF - Mi 04.05.11 17:30

Upps, ResourceID = integer hätte ich vielleicht erwähnen sollen. Da ich nur die ID benutzte, zwecks Arrays, macht sich leichter im gesamten Programm :wink:

Gruss ALf


galagher - Mi 04.05.11 17:43

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
   {nicht vergessen alles zum schluss frei zugeben}
   Bass_Stop;
   Bass_Free;
   FreeMem(myMem);
   mySong.Free;

end;

Sollte man nicht zumindest mySong und mymem jedesmal freigeben, nachdem der Sound abgespielt wurde?


jaenicke - Mi 04.05.11 18:07

Nein, denn hier geht es ja gerade darum den selben Song mehrfach abzuspielen. Da macht es natürlich wenig Sinn die Daten bei jedem Abspielen neu in den Speicher zu laden. Denn genau das macht das Abspielen dann ja wieder langsamer.


ALF - Mi 04.05.11 18:09

Jaein, wenn Du sie die ganze Zeit benötigst, ist es unnutz die ganze laderrei der sounds von vorn durchzuführen. So hast Du sie und kannst nun an allen Stellen wo du sie benötigst sofort starten ohne das Du jedesmal den ladevorgang ausführst.

Edit: Hier noch mal eine Entscheidungshilfe für Dich, wenn Du nun doch mit der Bass.DDL arbeitetst!
Wenn Du das Spiel weitegeben willst must Du die Bass.DLL mitgeben!
Wenn Du sie nicht als Res mit einpackst in Deiner Exe und dann wieder ertsellst wenn das Spiel gestartet wird,
brauchst Du auch Deine Sounds nicht in Res packen! Sparst dir somit den ganzen Aufwand mit MEM MySong usw.

Mit der herkömlichen Methode von der Platte, geht das
abspielen genauso schnell als wenn Du sie im Speicher hast!

Gruss ALf

mist der chef war schneller :wink:


galagher - Do 05.05.11 19:00

Ich verstehe nicht ganz! Da ich resourceID verschiedene Werte zuweisen muss, um verschiedene Soundeffekte abzuspielen, kann ich das so nicht brauchen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure TForm1.FormCreate(Sender: TObject);
begin
  resourceID:= 1;
  {leider kann man mit hinstance, bzw mySong nicht direct mit Bass arbeiten,}
  {wie es das mmsystem kann}
  {darum der Umweg über Speicherreservierung und das laden der Daten in den Speicher}
  mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND');
  GetMem(myMem, mySong.Size);
  mysong.Read(myMem^, mySong.Size);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    {dies währe mit dem mmsystem, aber da kannst Du nur volume Global regeln}
    //PlaySound(PChar(1),HInstance, snd_ASync or snd_Memory or snd_Resource);
    
    myVolume:= 0.2;//<-- von 0 bis 1
    myPlay:= BASS_StreamCreateFile(True, myMem , 0, mySong.Size, BASS_STREAM_AUTOFREE);
    BASS_ChannelSetAttribute(myPlay, BASS_ATTRIB_VOL, myVolume);
    BASS_ChannelPlay(myPlay, False)
end;


Ich brauche eine eigene Prozedur zum Abspielen der Sounds, es muss daher alles in einer Prozedur sein:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TForm1.PlaySound(iResID: Integer);
begin
  resourceID := iResID;  //Ich kann resourceID auch ganz weglassen und nur mit iResID arbeiten!

  mySong:= TResourceStream.CreateFromID(hInstance, ResourceID, 'SOUND');
  GetMem(myMem, mySong.Size);
  mysong.Read(myMem^, mySong.Size);

  myVolume:= 0.2;//<-- von 0 bis 1
  myPlay:= BASS_StreamCreateFile(True, myMem , 0, mySong.Size, BASS_STREAM_AUTOFREE);
  BASS_ChannelSetAttribute(myPlay, BASS_ATTRIB_VOL, myVolume);
  BASS_ChannelPlay(myPlay, False)
end;

Wenn ich alles in einer Prozedur habe, belegt GetMem doch immer mehr Speicher, oder? Ich meine, bei 20 - 30 Soundeffekten, die laufend abgespielt werden sollen, rufe ich doch sehr oft die Prozedur PlaySound auf, die jedesmal Speicher reserviert, den aber nicht mehr freigibt, oder sehe ich das falsch?

Ich meine, die Sounds sind ja schon im Speicher (wenn ich eine .res einbinde). Ich muss sie ja nur noch "holen" und abspielen!


glotzer - Do 05.05.11 19:04

Lade die Datei genau EINMAL am Anfang in den Speicher und ruf sie dann nurnoch auf.
Vieleicht mit einer "array of pointer" oder nimm gleich "TList"


ALF - Do 05.05.11 20:38

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
...doch immer mehr Speicher, oder? Ich meine, bei 20 - 30 Soundeffekten, die laufend abgespielt werden sollen, rufe ich doch sehr oft die Prozedur PlaySound auf, die jedesmal Speicher reserviert, den aber nicht mehr freigibt, oder sehe ich das falsch?
!
ne Du darfst die nur im Create laden ! der rest ist nur das Handle was darauf zeigt!!!

Kleiner Tip, hatt ich doch schon mal geschrieben :wink:
Du bist der, der weis wieviele Sounds Du verwendest

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:
//nimm Arrays, bisx ist die Anzahl Sounds die Du drin hast!
Private
     mySong: Array[1..bisx] of TResourceStream;
     pChan: Array[1..bisx] of HStream;
     myVolume: Array[1..bix] of float;
     myMem: Array[1..bisx] of pointer //<----??????
//usw dann

//im Createteil<--------- nur hier laden der sounds nicht in deiner playroutine
For i:= 1 to bisx do
begin
    mySong[i]:= TResourceStream.CreateFromID(hInstance, i, 'SOUND');
    GetMem(myMem[i], mySong[i].Size);
    mySong[i].Read(myMem[i]^, mySong[i].Size);
end;

myVolume[1]:= 0.2;//<-- einzelen sounds die Lautstärke fix zuordnen
myVolume[2]:= 0.7;
....
...
//usw

//eine PlayProcedure mit Parameter, hier könntest Du auch die Lautstärke individuell noch bestimmen
//wenn du es willst
procedure Mplay(playid: integer); // oder   Mplay(playid: integer; volume: float)
var
  setvolume: float;
begin
    if volume <>  myVolume[playid] then              //<-- nur wenn Du willst
       setvolume:= volume                            //ansonnsten  myVolume[playid] unten rein
    else
       setvolume:= myVolume[playid];
    
    pChan[playid]:= BASS_StreamCreateFile(True, myMem[playid] , 0, mySong[playid].Size, BASS_STREAM_AUTOFREE);
    BASS_ChannelSetAttribute(pChan[playid], BASS_ATTRIB_VOL, setvolume);//<<--hier
    BASS_ChannelPlay(pChan[playid], False);

end;


nun kannst Du aus jeder xbeliebigen Stelle deines Progs dies Proc aufrufen

Mplay(1, 0.2) währe ein Schuss
Mplay(2, 0.5) währe eine Klingel

oder auch
myplay(1, 0.02); Schuss sehr leise also weit entfernt
myplay(1, 0.5); Schuss relativ laut also sehr nah
wenn der Schuss lauter werden soll, weil der Gegener näher kommt 8)

Warum ich die Arrays mit 1 anfangenlasse, ist begründet durch die Resource.
Die fängt nun mal mit 1 an und nicht mit 0! Somit merk ich es mir besser für alle Arrays :wink:

Edit:
Hier noch ne kleine spielerei wenn der Gegner von links kommt:

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:
private
    myPan Array[1..bisx] of float;//<-- nur wenn Du willst, standard ist 0


// im create alle werte auf 0 setzen oder so wie Du es willst


//eine PlayProcedure mit Parameter
Mplay(playid: integer; volume, pan: float);//<-- pan ist -1 gegner schießt von links ;)
var
  ......
  setpan: float;
begin
    .....
    .....
    .....
    if pan <> myPan[playid] then
       setpan:= pan
    else
       setpan:= myPan[playid];
     
   .....
   .....
   BASS_ChannelSetAttribute(pChan[playid], BASS_ATTRIB_VOL, setvolume);
   BASS_ChannelSetAttribute(pChan[playid], BASS_ATTRIB_PAN, setpan);//setpan von -1(links) 0(mitte) bis +1(rechts)
   BASS_ChannelPlay(pChan[playid], False);
end;

Wenn Du das ganze noch schöner machen willst Suround(7.1), dann sollte das ganze in eine Klasse oder Objekt sein, der Du nur die xyz Werte vom Spieler und Gegner übergibst und die Soundid. Der Rest wird dann dort errechnet von wo nun der sound kommt. So als Zugabe deines vorhabens :zwinker:
Wenn es Dir aber so ausreicht, ist das oben ne gute Lösung.

Nachtrag:
Allerdings sind 20-30 Soundeffekte im Speicher nicht Notwendig!
Da solltest Du Fallunterschiede machen.

Szenische-Sounds und Event-Sounds.

Event-Sound brauchst Du nicht im Speicher vorhalten! Die werden geladen wenn sie gebraucht werden und danach wieder freigegeben!
Überdenke dies nochmal! 20-30 wavedateien können mehr als 1,5 GB speicher fressen!!!!(je nach grösse)
Gruss ALf


TAKTER - Fr 28.12.12 00:06

Hey,
man kann das Ganze auch noch kürzer gestalten...

Der Stream beinhaltet schon einen Zeiger und eine Size:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var 
sh:HStream;
sd:TResourceStream;

  sd:=TResourceStream.Create(HInstance,'sound','mp3');
  sr:=BASS_StreamCreateFile(True,sd.Memory,0, sd.Size,BASS_STREAM_AUTOFREE);


Moderiert von user profile iconNarses: Delphi-Tags hinzugefügt


FrEaKY - Fr 28.12.12 19:24

Ich finde es eine Frechheit, wie Micro$hit alle 2-3 Jährchen sein neustes fehlerhaftes Betriebssystem rausbringt. Wir sollen das als Endnutzer dann finanzieren, und als Entwickler uns auch noch jedes Mal neu anpassen. Abwärtskompatibilität ist nur eine Farce. Deswegen es dieses Unternehmen auch irgendwann nicht mehr geben. Die Monopolmacht kommt ja heute schon langsam ins Bröckeln.
Ich unterstütze das schon lange nicht mehr, benutze auch noch XP. Ihr könnt ruhig lachen, ist mir egal.


jaenicke - Sa 29.12.12 02:51

user profile iconFrEaKY hat folgendes geschrieben Zum zitierten Posting springen:
Ich finde es eine Frechheit, wie Micro$hit alle 2-3 Jährchen sein neustes fehlerhaftes Betriebssystem rausbringt. Wir sollen das als Endnutzer dann finanzieren, und als Entwickler uns auch noch jedes Mal neu anpassen. Abwärtskompatibilität ist nur eine Farce. Deswegen es dieses Unternehmen auch irgendwann nicht mehr geben. Die Monopolmacht kommt ja heute schon langsam ins Bröckeln.
Ich unterstütze das schon lange nicht mehr, benutze auch noch XP.
Und das hat was mit dem letzten Verlauf des Threads zu tun? Die BASS-DLL usw. funktioniert doch überall problemlos. :gruebel:
Davon abgesehen hatten die Änderungen an der Sound-API ja nun mehr als gute Gründe, die war unter XP ja schrecklich im Vergleich...

user profile iconFrEaKY hat folgendes geschrieben Zum zitierten Posting springen:
Ihr könnt ruhig lachen, ist mir egal.
Och, na dann gerne :lol: ;-)


Gausi - Sa 29.12.12 10:07

user profile iconFrEaKY hat folgendes geschrieben Zum zitierten Posting springen:
Abwärtskompatibilität ist nur eine Farce. Deswegen es dieses Unternehmen auch irgendwann nicht mehr geben. Die Monopolmacht kommt ja heute schon langsam ins Bröckeln.


Sorry fürs OT, können wir ggf. auch in einem extra Topic weiterführen. Aber was Abwärtskompatibilität angeht, ist Windows spitze. Windows XP kam vor über 10 Jahren raus, und noch immer laufen fast alle Programme auch auf diesem System. Es mag an der einen oder anderen Stelle Einschränkungen oder kleinere Probleme geben (wenn z.B. ein Programm ins eigene Verzeichnis schreiben will und/oder Admin-Rechte voraussetzt), aber im Großen und Ganzen ist MS in der Hinsicht vorbildlich. Was auch ein guter Grund für die enorme Verbreitung sein dürfte.

Wenn du über Abwärtskompatibilität mecken willst, geh zu Apple. Da ist das wirklich ein Krampf. Aber Apple hat ja dafür so ein super benutzerfreundliches Interface. Da kann man sogar an allen Seitenrändern die Größe der Fenster verändern, nicht nur unten rechts. Oh, warte, verwechselt. Das war andersrum. :mrgreen:


galagher - Do 16.05.13 17:20

user profile iconmarkus5766h hat folgendes geschrieben Zum zitierten Posting springen:


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:
function GetMasterVolume(Mixer: hMixerObj): Word;
var
  MasterVolume    : TMixerControl;
  Details         : TMixerControlDetails;
  UnsignedDetails : TMixerControlDetailsUnsigned;
  Code            : MMResult;
begin
  Result := 0;

  Code := _VolumeControl(Mixer, MasterVolume);
  if Code = MMSYSERR_NOERROR then begin
    with Details do begin
      cbStruct := SizeOf(Details);
      dwControlID := MasterVolume.dwControlID;
      cChannels := 1;  // set all channels
      cMultipleItems := 0;
      cbDetails := SizeOf(UnsignedDetails);
      paDetails := @UnsignedDetails;
    end;
    Code := mixerGetControlDetails(Mixer,
                                   @Details,
                                   MIXER_GETCONTROLDETAILSF_VALUE);

    Result := UnsignedDetails.dwValue;
  end;
  if Code <> MMSYSERR_NOERROR then
    raise Exception.CreateFmt('GetMasterVolume failure, '+
                       'multimedia system error #%d', [Code]);
end;

procedure SetMasterVolume(Mixer: hMixerObj; Value: Word);
var
  MasterVolume    : TMixerControl;
  Details         : TMixerControlDetails;
  UnsignedDetails : TMixerControlDetailsUnsigned;
  Code            : MMResult;
begin
  Code := _VolumeControl(Mixer, MasterVolume);
  if Code = MMSYSERR_NOERROR then begin
    with Details do begin
      cbStruct := SizeOf(Details);
      dwControlID := MasterVolume.dwControlID;
      cChannels := 1;  // set all channels
      cMultipleItems := 0;
      cbDetails := SizeOf(UnsignedDetails);
      paDetails := @UnsignedDetails;
    end;
    UnsignedDetails.dwValue := Value;
    Code := mixerSetControlDetails(Mixer,
                                   @Details,
                                   MIXER_SETCONTROLDETAILSF_VALUE);
  end;
  if Code <> MMSYSERR_NOERROR then
    raise Exception.CreateFmt('SetMasterVolume failure, '+
                         'multimedia system error #%d', [Code]);
end;


läuft auf XP
Vista - keine Ahnung, hab ich nie gehabt
Win7 - definitiv nein

Hallo!

Ist jetzt zwar schon ein Weilchen her, aber jetzt habe ich deinen Code erfolgreich mit Windows 7 getestet. Sound lauter und leiser funktioniert wie gewünscht!