Problem z funkcją SNDPlaySound

0

Witam. Posiadam problem z funkcją SNDPlaySound. Problem polega na tym, że czasem się ta funkcja mi nie działa, w szczególności po jakimś tam upływie czasu działania mojego programu. Kiedy powinna reagować to nie reaguje.

Do wykorzystania tej procedury mam kilka wątków (każdy inaczej nazwany).
To jest przykład jednego z nich:

unit Unit12;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Tlhelp32, StdCtrls, PsAPI, Vcl.Samples.Spin, Vcl.ExtCtrls, Math,
  Vcl.Menus, MMSystem;

type
  TPosAlarm = class(TThread)
  private

  protected
    procedure Execute; override;
  end;

 var
  PosAlarm:TPosAlarm;


implementation
uses Unit1;



procedure TPosAlarm.Execute;
begin
  FreeOnTerminate := true;
  while not self.terminated do
begin
SNDPlaySound('PosAlarm.wav', SND_FILENAME OR SND_ASYNC);
sleep(1500);
end;
end;


Initialization
PosAlarm := TPosAlarm.Create(True);

end.

Oczywiście w moim głównym programie mam przygotowany warunek kiedy wywołać funkcję, a więc jest to np. timer z takim czymś:

if form1.label1.caption = 'xoxoxoxo' then begin PosAlarm.Resume.

Nie wiem jak rozwiązać problem, więc piszę tutaj. Myślałem nad zastosowaniem SNDPlaySound w głównym programie, a nie w jego wątku, ale wtedy podczas działania tej funkcji cały program się zacina.

Proszę o pomoc.

0

Po pierwsze, użyj funkcji PlaySound – ta której używasz jest subsetem funkcjonalności PlaySound i istnieje wyłącznie ze względu na wsteczną kompatybilność. Po drugie, upewnij się, że wywołania tych funkcji nie nakładają się na siebie i w razie czego zabezpiecz kod w ten sposób:

SND_ASYNC

The sound is played asynchronously and PlaySound returns immediately after beginning the sound. To terminate an asynchronously played waveform sound, call PlaySound with pszSound set to NULL.

Po trzecie, sprawdź, czy wątki faktycznie działają, bo to wcale nie musi być wina omawianej funkcji.

1

Po mojemu to nie działa gdyż gdzieś w programie zmienia się bieżący folder a nie podajesz pełnej ścieżki do dźwięku, który ma być odtworzony. Poza tym w ogóle nie rozumiem sensu stosowania przy tym wątku.

0

funkcje dźwiękowe to nie moja bajka, ale ....
tworzysz jedną instancję obiektu klasy TPosAlarm ...

Initialization
    PosAlarm := TPosAlarm.Create(True);
end.

i działasz na tej jednej instancji (wielokrotne resume).

może było by lepiej ..
tworzysz nową instancję wątku dla każdego komunikatu który trzeba odgrać

if form1.label1.caption = 'xoxoxoxo' then
     newAlarm:=TPosAlarm.create(false);

a wątek się sam się zamyka i zwalnia po odegraniu komunikatu ponieważ ma freeonterminate=true
Jak już ktoś wcześniej wspomniał... wydaje mi się że masz problem z zarządzaniem wątkami a nie z samym odgrywaniem dźwięków z pliku

@kAzek:
folder jest mało ważny, jeśli będzie zły to pewnie nic się nie odtworzy albo rzuci wyjątkiem. Problemem jest użycie do tego wątku, a dokładniej jednej jego instancji i samego sposobu użycia

1

Jeżeli chodzi od wątki do takiego czegoś (choć przecież jeżeli używa parametru SND_ASYNC nie powinny być potrzebne) to w ogóle nie ma co się bawić w TThread tylko korzystać z dobrodziejstw nowszych wersji Delphi i wywoływać po prostu:

  uses WinApi.MMSystem, System.IOUtils, System.Threading;
  //ciach
  TTask.Run(procedure begin
     SNDPlaySound(PWideChar(TPath.Combine(ExtractFilePath(Application.ExeName), 'PosAlarm.wav'))
       ,SND_FILENAME or SND_ASYNC);
  end)
0

Wiem w czym problem.

unit Unit14;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Tlhelp32, StdCtrls, PsAPI, Vcl.Samples.Spin, Vcl.ExtCtrls, Math,
  Vcl.Menus, MMSystem;

type
  TPosAlarm = class(TThread)
  private
  protected
    procedure Execute; override;
  end;

type
  TDing= class(TThread)
  private
  protected
    procedure Execute; override;
  end;

type
  TAlarm= class(TThread)
  private
  protected
    procedure Execute; override;
  end;

type
  TMsgAlarm = class(TThread)
  private

  protected
    procedure Execute; override;
  end;

 var
  PosAlarm:TPosAlarm;
  Ding:TDing;
  Alarm:TAlarm;
  MsgAlarm:TMsgAlarm;



implementation
uses Unit1, Unit13;



procedure TPosAlarm.Execute;
begin
  FreeOnTerminate := false;
  while not self.terminated do
begin
SNDPlaySound('PosAlarm.wav', SND_FILENAME OR SND_SYNC);
sleep(1500);
end;
end;

procedure TDing.Execute;
begin
  FreeOnTerminate := false;
  while not self.terminated do
begin
SNDPlaySound('Ding.wav', SND_FILENAME OR SND_SYNC);
sleep(1500);
end;
end;

procedure TAlarm.Execute;
begin
  FreeOnTerminate := false;
  while not self.terminated do
begin
if (strtoint(Form1.Label7.Caption) = 0) then
begin
SNDPlaySound('Danger.wav', SND_FILENAME OR SND_SYNC);
sleep(1500);
end;
end;
end;

procedure TMsgAlarm.Execute;
var
lp, i : integer;
begin
  FreeOnTerminate := false;
  while not self.terminated do
begin
 if Form13.Label1.Caption = 'on' then
begin
SNDPlaySound('Danger.wav', SND_FILENAME OR SND_SYNC);
sleep(1500);
end;
end;
end;




Initialization
PosAlarm := TPosAlarm.Create(True);
Alarm := TAlarm.Create(True);
MsgAlarm := TMsgAlarm.Create(True);
Ding := TDing.Create(True);

end.

W przypadku gdy wyje mi alarm np. PosAlarm i zawyje mi nagle Ding i ja ten ding wyłączę to wtedy gasi się również PosAlarm.
Da się to jakoś oddzielić, żeby jedne nie miało bezpośrednio wpływu na drugie?

0

Funkcje SNDPlaySound ani PlaySound nie potrafią odtwarzać wielu dźwięków na raz dlatego włączenie jednego powoduje wyłączenie drugiego to normalne zachowanie. Użyj wielu dynamicznie tworzonych TMediaPlayer.

0

Więc do tego będę potrzebować chyba komponent tak? Nigdy nie instalowałem żadnego komponentu xd

0

Można skorzystać z funkcji PlaySound w celu odtwarzania kilku różnych dźwięków (ale nie jednocześnie) – wystarczy zastosować się do tego co napisałem, czyli przed odtworzeniem nowego dźwięku, wywołać tę funkcję z nilem dla pierwszego parametru. Aktualnie odtwarzany dźwięk zostanie zatrzymany, a nowy będzie mógł być odegrany. Wątki nie są tutaj w ogóle potrzebne.

@DroniC: potrzebujesz odtworzyć kilka dźwięków naraz, czy nie?

0
furious programming napisał(a):

Można skorzystać z funkcji PlaySound w celu odtwarzania kilku różnych dźwięków (ale nie jednocześnie) – wystarczy zastosować się do tego co napisałem, czyli przed odtworzeniem nowego dźwięku, wywołać tę funkcję z nilem dla pierwszego parametru. Aktualnie odtwarzany dźwięk zostanie zatrzymany, a nowy będzie mógł być odegrany. Wątki nie są tutaj w ogóle potrzebne.

@DroniC: potrzebujesz odtworzyć kilka dźwięków naraz, czy nie?

Tak fajnie by było odtwarzać kilka dzwięków na raz i móc ingerować w to który ma być wyłączony i kiedy. Niestety korzystając z tego co wysłałem wyżej próbując wyłączyć jeden dzwięk gaszę wszystkie.

0

No to dlaczego nie podajesz takich informacji od razu? Marnujemy czas na szukanie dziury w PlaySound, a Ty chcesz ją wykorzystać w sposób, jakiego ona nie obsługuje…

Skorzystaj z podpowiedzi od @kAzek lub użyj jakiejś biblioteki, na przykład BASS.

1

Z tego co widzę trzeba Ci czegoś takiego (oparte na TMediaPlayer)

unit uPlayer;


interface

uses Winapi.Windows, Vcl.Forms, Vcl.MPlayer, System.Threading;

type
  TPlayer = class(TObject)
  private
    fMP: TMediaPlayer;
    fPlayRepeat: Boolean;
    fStoped: Boolean;

    procedure MediaPlayerNotify(Sender: TObject);

    procedure SetFileName(AFileName: string);
    function GetFileName: string;
  public
    property FileName: string read GetFileName write SetFileName;
    property PlayRepeat: Boolean read fPlayRepeat write fPlayRepeat;
    procedure Play;
    procedure Stop;
    constructor Create(AFileName: string);
    destructor Destroy; override;
  end;

implementation

procedure TPlayer.MediaPlayerNotify(Sender: TObject);
begin
  TTask.Run(procedure begin
    if fPlayRepeat and (not fStoped) and (TMediaPlayer(Sender).NotifyValue = nvSuccessful) then //aby powtarzał
    begin
      Sleep(1500);
      Play;
    end;
  end)
end;

procedure TPlayer.SetFileName(AFileName: string);
begin
  Stop;
  fMP.FileName:= AFileName;
end;

function TPlayer.GetFileName: string;
begin
  result:= fMP.FileName;
end;

procedure TPlayer.Play;
begin
  Stop;
  if not (fMP.Mode = TMPModes.MPOpen) then
    fMP.Open;
  if (fMP.Mode = TMPModes.MPStopped) or (fMP.Mode = TMPModes.MPPaused) then
    fMP.Play;
  fMP.Notify:= True;
  fStoped:= False;
end;

procedure TPlayer.Stop;
begin
  fStoped:= True;
  if (fMP.Mode = TMPModes.MPPlaying) or (fMP.Mode = TMPModes.MPPaused)  then
    fMP.Stop;
end;

constructor TPlayer.Create(AFileName: string);
begin
  inherited Create;
  fPlayRepeat:= True;
  fStoped:= False;
  fMP:= TMediaPlayer.Create(Application);
  fMP.Visible:= False;
  fMP.Parent:= Application.MainForm;
  fMP.OnNotify:= MediaPlayerNotify;
  fMP.AutoRewind:= True;
  fMP.FileName:= AFileName;
end;

destructor TPlayer.Destroy;
begin
  Stop;
  if (fMP.Mode = TMPModes.MPOpen) then
    fMP.Close;
  //fMP.Free;  Zwolni go TApplication
  inherited Destroy;
end;


end.

To jest cały kod modułu dodaj go do uses i zadeklaruj sobie zmienne typu TPlayer np: PosAlarm: TPlayer i tworzysz PosAlarm:= TPosAlarm.Create('sciezka i nazwa pliku alarm'); tylko tworzysz w takim miejscu gdy już główna forma apki istnieje, bo TMediaPlayer wymaga rodzica (w podobny sposób resztę). Masz metody Play i Stop i chyba to jest tyle co Ci trzeba. Żadnych wątków (w TPlayer jest zaimplementowany wątek ze względu na ten Sleep przed powtórzeniem odtwarzania) tyle odpalasz playery i powinno śmigać a jak nie to pisz i koniecznie wklej kod co zrobiłeś.

0

Hm. Skopiowałem cały kod wkleiłem do nowego unitu, a następnie dodałem go do sekcji uses w "głównym" programie. Następnie żeby zobaczyć czy to wgl. działa w buttona dodałem to:

uPlayer:= TPlayer.Create('ding.wav');

no i nie działa.

[dcc32 Error] Unit1.pas(1216): E2029 '.' expected but ':=' found
0

Wiesz co przeczytaj jakiś mega podstawowy kurs Delphi bo masakra. uPlayer:= TPlayer.Create('ding.wav'); jak to ma działać jak uPlayer to nazwa modułu...

1 użytkowników online, w tym zalogowanych: 0, gości: 1