Entwickler-Ecke

Multimedia / Grafik - Abspielen kurzer Tonsignale in Delphi7


kissgdr - Mo 04.01.21 21:48
Titel: Abspielen kurzer Tonsignale in Delphi7
Ich benötige Hilfe zur Lösung des folgenden Problems:
Ich möchte ein, in Delphi7 geschriebenes Programm mit kurzen Tonsignalen erweitern.
Die Audio-dateien sind 1-3 Sekunden lang (.wav oder .mp3) und sind in einem Memorystream gespeichert.
Bei .wav-Dateien löst MediaPlayer das Problem leider nicht, da nur externe Dateien verarbeitet werden können (FileName='string').
(Darüber hinaus kann ein .exe-Programm, das MediaPlayer verwendet, in einer Win10-Umgebung nicht ausgeführt werden, da ein 'Exception EMCIDeviceError' wird generiert.)
Ich suche nach einer Lösung, die
- kann Memorystream als Input verwenden
- das erstellte EXE-Modul wird auch in Win10-Umgebung laufen


Hotte123 - Mo 04.01.21 23:06

Moin,

schau dir mal die bass.dll an. Nette Beschreibung unter MeMP - Mein einfacher Mp3-Player [http://www.gausi.de/memp-4.html].

Viel Spaß noch.

Moderiert von user profile iconTh69: URL-Titel hinzugefügt.


Th69 - Di 05.01.21 11:14

Es gibt auch die WinAPI-Funktion sndPlaySound [https://docs.microsoft.com/de-de/previous-versions//dd798676(v=vs.85)] (für Waveform-Audio), s.a. Delphi: play sound from memorystream [https://codepad.co/snippet/delphi-play-sound-from-memorystream].


jaenicke - Di 05.01.21 11:21

Die Funktion kann man auch direkt auf Ressourcen verwenden ohne die Ressource vorher in einen MemoryStream zu kopieren (was ich hier einmal vermute): Playing WAVE Resources [https://docs.microsoft.com/de-de/windows/win32/multimedia/playing-wave-resources]

Mit der genannten bass.dll hat man allerdings deutlich mehr Möglichkeiten.

Moderiert von user profile iconTh69: URL-Titel hinzugefügt.


kissgdr - Di 05.01.21 18:54

Danke für die Ideen. Ich habe bisher keine Software außerhalb des offiziellen Delphi verwendet, ich kenne sie nicht. Ihr Studium dauert länger.

Die kurze Audiosignale derzeit sind im WAV-Format, und wegen das spezielle GSM-WAV-Format sind sie kürzer als das MP3-Format, und die Tonqualität ebenfalls akzeptabel ist. Da ich 10-12 solcher kleinen Audiodateien (jeweils 10-25 KB) verarbeiten möchte, finde ich es benutzerfreundlicher, sie in die EXE-Datei zu integrieren, daher möchte ich Memorystream verwenden.


kissgdr - Mi 06.01.21 14:08

Das empfohlene Beispiel Delphi: play Sound from memorystream verwendet die PlayWaveStream-Prozedur, aber ich habe nur eine C ++ - Beschreibung dafür gefunden.


jaenicke - Mi 06.01.21 14:21

user profile iconkissgdr hat folgendes geschrieben Zum zitierten Posting springen:
Das empfohlene Beispiel Delphi: play Sound from memorystream verwendet die PlayWaveStream-Prozedur, aber ich habe nur eine C ++ - Beschreibung dafür gefunden.
Die Prozedur ist doch dort komplett enthalten in dem Link.


kissgdr - Mi 06.01.21 19:13

leider kann ich es nicht finden.
Signin oder Login ist ständig falsch (Timeout).


jaenicke - Mi 06.01.21 21:05

user profile iconkissgdr hat folgendes geschrieben Zum zitierten Posting springen:
leider kann ich es nicht finden.
Signin oder Login ist ständig falsch (Timeout).
Du musst dich dort nicht einloggen. Das komplette Beispiel ist direkt zu sehen, hier der Teil mit der genannten Funktion:

PlayWaveStream


kissgdr - Do 07.01.21 16:38

Ich sehe das auch, aber der referenzierte sndPlaySound kann nicht aufgelöst werden.


jaenicke - Do 07.01.21 23:21

Eine Suche in den bei Delphi normalerweise mitgelieferten Quelltexten oder bei Google zeigt, dass die Funktion in der Unit Winapi.MMSystem zu finden ist.


kissgdr - Fr 08.01.21 15:03

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Eine Suche in den bei Delphi normalerweise mitgelieferten Quelltexten oder bei Google zeigt, dass die Funktion in der Unit Winapi.MMSystem zu finden ist.

Vielen Dank.
Ich habe dies zuvor in der Delphi7-Help (Windows SDK), gefunden.
Ich habe viele Dokumentátionen durchgelest, leider habe ich bisher nirgends ein Beispiel gefunden, das zeigt, wie eine bestimmte win.api aus einer Delphi-Umgebung aufgerufen werden kann. (Was soll ich dazu tun?)
In meiner bisherigen Arbeit war Delphis eigenes Toolkit ausreichend.


kissgdr - Fr 08.01.21 15:42

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Eine Suche in den bei Delphi normalerweise mitgelieferten Quelltexten oder bei Google zeigt, dass die Funktion in der Unit Winapi.MMSystem zu finden ist.

Nochmal vielen Dank!
Ich habe die Lösung gefunden!! uses MMSystem;


jaenicke - Fr 08.01.21 17:46

Ach ja, bei Delphi 7 gab es die Namespaces noch nicht, da habe ich nicht dran gedacht. Damals hieß die Unit noch nur MMSystem wie du ja schon gemerkt hast.

Seit einigen Jahren heißt sie der besseren Übersicht halber Winapi.MMSystem.


kissgdr - Fr 08.01.21 21:47

Basierend auf den Ideen, die ich erhalten habe, habe ich ein Testprogramm erstellt, das so funktioniert, wie ich es möchte:
- Kurze WAV-Signale werden von einem MemoryStream abgespielt
- Das Programm funktioniert auch in XP-, Win7- und Win10-Umgebungen

Ich mag diese Lösung wirklich nicht, weil ich das Ende des Streams beim Abspielen nicht kontrollieren kann.
Daher verwende ich eine sehr grobe Lösung.
Leider konnte ich nirgendwo ein Delphi7-Dokument finden, mit dem ich dieses Problem lösen konnte
(wie z.B. in TMediaPlayer die Notify-Properties)

Ich würde mich sehr freuen, wenn ich eine günstigere Lösung finden könnte

Wie kann ich das Testprogram aufladen? (minimum .pas, .dfm, .dpr)


Gausi - Fr 08.01.21 21:52

Ich hänge mich hier auch mal kurz dran, da ich per Mail dazu eine Anfrage bekommen habe:

Mein Tutorial zur bass.dll geht da ja etwas am Thema vorbei, aber man kann mit der bass.dll wohl auch aus dem Speicher heraus abspielen. Ansätze dazu sollten sich in der Dokumentation zur bass.dll finden. Wie das dort mit Notifications geht, weiß ich leider auch nicht genau.

Ich habe den Thread hier nebenbei verfolgt, und nicht auf die E-Mail geantwortet, da das hier im Topic ja durchaus in eine konstruktive Richtung läuft. :D


Testprogramm hochladen: am besten zippen und dann per "Dateianhang hinzufügen" an den Beitrag dran hängen.


kissgdr - Fr 08.01.21 22:03

Ich habe gefunden - Dateianhang hinzufügen


jaenicke - Fr 08.01.21 22:06

user profile iconkissgdr hat folgendes geschrieben Zum zitierten Posting springen:
Ich mag diese Lösung wirklich nicht, weil ich das Ende des Streams beim Abspielen nicht kontrollieren kann.
Möchtest du nur einen Teil des Sounds abspielen und das Ende angeben?

Oder möchtest du wissen wann das Abspielen beendet ist? Das geht sehr einfach, weil sndPlaySound mit dem Flag SND_SYNC erst nach dem Ende des Abspielens zurückkehrt. Wenn du das also in einen Thread packst, kannst du danach ein Event auslösen ohne etwas anderes als den Thread zu blockieren.

// EDIT:
Ich dachte es mir schon, dass das zweite gemeint ist. Einfaches Beispiel (allerdings reagiert die Exe dann während des Abspielens nicht, daher der Hinweis auf einen Thread):

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:
procedure PlayWaveStream(Stream: TMemoryStream);
begin
  if Stream = nil then
    sndPlaySound(nil, SND_SYNC)
  else
    sndPlaySound(Stream.Memory, (SND_SYNC or SND_MEMORY));
end;

procedure TForm1.ExitClick(Sender: TObject);
begin
  Close;
end;

procedure TForm1.StartClick(Sender: TObject);
begin
  wav_stream:= TMemoryStream.Create;
  try
    wav_stream.LoadFromFile('signal_1.wav');
    PlayWaveStream(wav_stream);
    wav_stream.LoadFromFile('signal_2.wav');
    PlayWaveStream(wav_stream);
  finally
    wav_stream.Free;
  end;
end;

Halt sollte man nie verwenden. Close schließt das Programm sauber.


kissgdr - Fr 08.01.21 22:20

Ohne .exe file probiere ich nochmal


kissgdr - Fr 08.01.21 23:16

Ich habe die Testdatei zweimal gesendet, da sowohl Google Chrome als auch Win10 beim Herunterladen aufgrund der EXE-Datei einen Virus gemeldet haben.

Danke für die schnelle Antwort - mit Delphi Code.
Leider kann ich dies aufgrund externer Dateien nicht verwenden.
ich muss sagen, dass ich in meinem Programm viele kleine externe Dateien verwende.
(Graphik von Schachfiguren, Hilfedokumenten und Audiodateien).
Ich speichere diese in einer Containedatei und platziere die erforderliche Segment - bei Bedarf - im Memorystream, aufgrund eine Seriennummer.
Wenn das Programm veröffentlicht wird, installiert der Benutzer zusätzlich zu .exe nur eine Containerdatei.

In meiner groben Lösung kann das Signal länger oder kürzer sein als waiting_for(x), es funktioniert immer gut.
Wenn das Signal länger ist, wird es abgeschaltet, wenn es kürzer ist, dauert es bis zum Ende des Intervalls.


Th69 - Sa 09.01.21 10:03

kissgdr hat folgendes geschrieben:
Danke für die schnelle Antwort - mit Delphi Code.
Leider kann ich dies aufgrund externer Dateien nicht verwenden.

@kissgdr: Du stellst dich hier wirklich etwas unbeholfen an. Der Code von jaenicke [https://entwickler-ecke.de/user_jaenicke.html] mit LoadFromFile ist doch nur ein Test-Programm. Die eigentliche Funktion PlayWaveStream bzw. sndPlaySound verwendet doch einen TMemoryStream - und du weißt doch jetzt, wie man diesen verwendet.


jaenicke - Sa 09.01.21 11:24

user profile iconkissgdr hat folgendes geschrieben Zum zitierten Posting springen:
Danke für die schnelle Antwort - mit Delphi Code.
Leider kann ich dies aufgrund externer Dateien nicht verwenden.
Ich habe deinen eigenen Code aus dem Testprojekt verwendet und lediglich korrigiert und gekürzt. Du kannst den dort 1:1 einfügen und deinen Code unterhalb von implementation einfach ersetzen.

Der entscheidende Punkt ist ja nur statt SND_ASYNC wie schon erwähnt SND_SYNC zu verwenden, damit die Funktion sndPlaySound erst danach zurückkehrt und dein folgender Code ausgeführt wird.


kissgdr - Sa 09.01.21 16:39

Entschuldigung, aber wir haben uns missverstanden!
Mein Testprogramm verwendet externen Audiodateien nur für Demonstrationszwecken.
In der realen Programmumgebung ist nur eine externe Containerdatei verfügbar, aus der jede Audiodatei in den Memorystream eingelesen wird (das ist meine Privatelösung). Die Wav_stream.LoadFromFile ('signal_1.wav'); Anweisung kann nicht in der endgültigen Programmumgebung verwendet werden.


kissgdr - Sa 09.01.21 17:11

Mit dem vorgeschlagenen SND_SYNC ~ SND_ASYNC-Ersatz habe ich die perfekte und elegante Lösung bekommen!!
Der Verweis auf die externe Datei entfällt natürlich.
Herzlichen Glückwunsch und nochmals Entschuldigung für das Missverständnis. (Google Translate ist schuldig?!)