2 Samples gleichzeitig via PlaySound() ausgeben

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Benutzeravatar
Delle
Beiträge: 1132
Registriert: 10.05.2005 22:48

2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Delle »

Hallo,

gibt es irgendeine Möglichkeit 2 Samples (wav) gleichzeitig mittels PlaySound() auszugeben?

Wenn es so aufgerufen wird, dann entsteht da immer eine leichte Verzögerung:

PlaySound(0)
PlaySound(1)

Danke für alle Tipps!

Delle
PB 6.21 | Win 11
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von STARGÅTE »

Vllt könnte ein Thread helfen.
Indem du beide PlaySounds in einzelne Threads steckst welche du dann "kurz" nacheinander ausführst.
Deises Kurz sollte kurz genug sein.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Dark
Beiträge: 93
Registriert: 24.08.2007 20:36
Kontaktdaten:

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Dark »

Hallo,

wenn du wirklich willst, das die beiden Audio Dateien gleichzeitig abgespielt werden, musst du sie zu einer neuen zusammen mixen, dies wäre aber um einiges komplizierter, da dies nicht mit den Standardbefehlen geht. Einfacher wäre es wenn du die Dateien sowieso immer zusammen wiedergeben willst und einfach ein Programm wie Audacity nutzt und eine wav Datei erstellt wo beide hereingemixt sind.

mfg,
Dark
Benutzeravatar
Delle
Beiträge: 1132
Registriert: 10.05.2005 22:48

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Delle »

Dark darum geht es ja: Zusammenmixen aber eben ohne viel Aufwand. Audacity fällt da komplett weg.

Ich könnte höchstens die Verzögerung in ms messen und dann einen 2. Thread entsprechend später starten lassen oder sowas...

Je schneller die Kiste, desto synchroner läuft es ja auch ab... allerdings eben nicht 1:1
PB 6.21 | Win 11
Dark
Beiträge: 93
Registriert: 24.08.2007 20:36
Kontaktdaten:

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Dark »

Delle hat geschrieben:Dark darum geht es ja: Zusammenmixen aber eben ohne viel Aufwand. Audacity fällt da komplett weg.

Ich könnte höchstens die Verzögerung in ms messen und dann einen 2. Thread entsprechend später starten lassen oder sowas...

Je schneller die Kiste, desto synchroner läuft es ja auch ab... allerdings eben nicht 1:1
Hallo,

irgendetwas verzögert zu starten, so das es genau gleichzeitig abspielt, ist nachezu unmöglich. Gehen wir davon aus du hast WAV daten mit 48kHz also 48.000 verschiedene Samples pro Sekunde(!), dann wäre der Abstand zwischen einem Sample und dem anderen ~0,021 Millisekunden. Das heißt du müsstest schon im Microsekunden Bereich arbeiten, damit es nicht schon um ein Sample verzögert ist.

Es gibt zwei verschiedene Möglichkeiten Audio Daten zu mixen:
- Die 100% korrekte Methode ist die Samples per Fast Fourrier Transformationen in ihre Frequenzen zu zerlegen, zusammenzurechnen und den Vorgang rückgängig zu machen.
- Die für diesen Fall wesentlich praktikablere Lösung ist es beide Wave Dateien in den Speicher zu laden und dann folgendes zu machen:
Wave Header der beiden Dateien lesen um Informationen über die WaveDateien zu bekommen. Dann Speicher für eine dritte Wave Datei allocaten und deren Header in den Ram schreiben.

Code: Alles auswählen

For i = 0 to SampleCount - 1
  Sample1.i = PeekW(*Wave1+i*2) ;bei 16 bit Wave Oder PeekL(*Wave1+i*4) bei 32 Bit Wave
  Sample2.i = PeekW(*Wave2+i*2) ;bei 16 bit Wave Oder PeekL(*Wave1+i*4) bei 32 Bit Wave
  PokeW(*Wave3+i*2, (Sample1+Sample2)/2 )  ;Beide Samples addieren und durch zwei Teilen (Bei 32 bit erst beides teilen und dann addieren, da es sonst zu einem Integer Overflow kommt und dann auch wieder PokeL(...)
next
Achtung, dies funktioniert auch nur, wenn beide Wave Dateien die gleiche Samplingrate und Bitrate haben.

Nur musst du aufpassen, das Wave Dateien riesig sein können und dann wäre es sinvoller, lieber direkt aus den beiden Wave Dateien zu lesen und nur den Sound zu mixen, der auch gleich Wiedergegeben wird, was wiederum nur funktioniert wenn du eine Librarieverwendest bei dem du einen Sound stream verwendest.
Es gibt also keine einfache gute Möglichkeit. Wenn dir das speicher Problem egal ist, geht diese Methode zumindestens.

mfg,
DarkPlayer
Benutzeravatar
Delle
Beiträge: 1132
Registriert: 10.05.2005 22:48

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Delle »

Naja geh mal davon aus, dass es kurze "Samples" sind und alle die gleiche Frequenz/Bitrate haben und mono sind.

Ich hab schon mit den RIFF-Headern rumhantiert und wollte 2 wavs zusammenmischen lassen, allerdings ist das auch wieder nicht so einfach wie es aussieht :(

Deswegen kam mir die Idee es einfach gleichzeitig abzuspielen und aufzunehmen.

Die Verzögerung ist leider bei mir nicht im "Microbereich", sondern eher im ms-Bereich... man hört also auch deutlich das ein Sample von beiden etwas zeitversetzt ist.

Wenn man jetzt mit 2 präparierten "Testsamples" eine Aufnahme macht und dann mit Audacity die Verzögerung in ms rausmarkiert, dann könnte es klappen...

Also den einen Sample in Thread 1 packen und X ms später abspielen lassen und den anderen in Thread 2 einfach "danach".

Also in etwa so bei gemessenen 250 ms Zeitverzögerung:

Code: Alles auswählen

Procedure Play1(wert)
 Delay(wert)
 PlaySound(1)
EndProcedure

Procedure Play2(wert)
 Delay(wert)
 PlaySound(2)
EndProcedure

CreateThread(@Play1,250)
CreateThread(@Play2,0)
Wobei größtenteils das Aufnahmeprogramm auch noch für Verzögerungen sorgt :(
PB 6.21 | Win 11
Dark
Beiträge: 93
Registriert: 24.08.2007 20:36
Kontaktdaten:

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Dark »

Hallo,

habe mal schnell nen Code geschrieben um zwei Audio Dateien zu mixen und als Ergebnis ein Sound Handle zu bekommen:

Code: Alles auswählen

Structure WaveFile
  chunkID .s{4}
  chunkSize.l
  riffType.s{4}
  FormatHeader.s{4}
  FormatLength.l
  wFormatTag.w
  wChannels.w
  dwSamplesPerSec.l
  dwAvgBytesPerSec.l
  wBlockAlign.w
  wBitsPerSample.w
  DataHeader.s{4}
  DataLen.l
EndStructure


Procedure MixSound(FileName1.s, FileName2.s, Volume1.f, Volume2.f)
  
  Protected Result.i = #False
  Protected Size1.i = FileSize(FileName1)
  Protected Size2.i = FileSize(FileName2)
  
  If Size1 And Size2

    Protected File1.i = OpenFile(#PB_Any, FileName1)
    If File1
      
      Protected File2.i = OpenFile(#PB_Any, FileName2)  
      If File2
        
        *Wave1.WaveFile = AllocateMemory(Size1)
        
        If *Wave1
          *Wave2.WaveFile = AllocateMemory(Size2)
          
          If *Wave2
          
            If Size1 > Size2
              *Wave3.WaveFile = AllocateMemory(Size1)
            Else
              *Wave3.WaveFile = AllocateMemory(Size2)
            EndIf
            
            If *Wave3
            
              ReadData(File1,*Wave1,Size1)
              ReadData(File2,*Wave2,Size2)
              
              If *Wave1\dwSamplesPerSec = *Wave2\dwSamplesPerSec And *Wave1\wChannels = *Wave2\wChannels

                If *Wave1\wBitsPerSample <> 8 And *Wave1\dwSamplesPerSec = *Wave2\dwSamplesPerSec
                  
                  Protected BitPerSample.i = *Wave1\wBitsPerSample 
                  Protected MaxSamples.i = *Wave1\DataLen / (BitPerSample / 8)
                  
                  If *Wave2\DataLen / (BitPerSample / 8) > MaxSamples
                    MaxSamples = *Wave2\DataLen / (BitPerSample / 8)
                  EndIf
                  
                  Protected i.i
                  Protected Sample1.i
                  Protected Sample2.i
                  
                  If BitPerSample = 16
                  
                    For i = 0 To MaxSamples - 1  
                      If i < (*Wave1\DataLen / (BitPerSample / 8))
                        Sample1 = PeekW(*Wave1 + SizeOf(WaveFile) + i * 2)  * Volume1
                      Else
                        Sample1 = 0
                      EndIf
                      If i < (*Wave2\DataLen / (BitPerSample / 8))
                        Sample2 = PeekW(*Wave2 + SizeOf(WaveFile) + i * 2) * Volume2
                      Else
                        Sample2 = 0
                      EndIf
                      PokeW(*Wave3  + SizeOf(WaveFile) + i * 2, (Sample1 + Sample2) /2) 
                    Next
                    
                  ElseIf BitPerSample = 32
                  
                    For i = 0 To MaxSamples - 1  
                      If i < (*Wave1\DataLen / (BitPerSample / 8))
                        Sample1 = PeekL(*Wave1 + SizeOf(WaveFile) + i * 4) * Volume1
                      Else
                        Sample1 = 0
                      EndIf
                      If i < (*Wave2\DataLen / (BitPerSample / 8))
                        Sample2 = PeekL(*Wave2 + SizeOf(WaveFile) + i * 4) * Volume2
                      Else
                        Sample2 = 0
                      EndIf
                      PokeL(*Wave3  + SizeOf(WaveFile) + i * 2, Sample1 / 2 + Sample2 / 2) 
                    Next
                    
                  EndIf
                  
                  If *Wave1\DataLen > *Wave2\DataLen
                    CopyMemory(*Wave1, *Wave3,  SizeOf(WaveFile)) 
                  Else
                    CopyMemory(*Wave2, *Wave3,  SizeOf(WaveFile))
                  EndIf 
                  
                  Result = CatchSound(#PB_Any, *Wave3)
                  
                EndIf
              
              EndIf

              FreeMemory(*Wave3)
            EndIf
            FreeMemory(*Wave2)
          EndIf
          FreeMemory(*Wave1)
        EndIf
        
        CloseFile(File2)
      EndIf
      
      CloseFile(File1)
    EndIf
  EndIf
  
  ProcedureReturn Result
EndProcedure
Volume1 und Volume2 zwei müssen hierbei zwischen 0 und 1 liegen (1 = Volle Lautstärke)

Beispiel:

Code: Alles auswählen

InitSound()
Sound =  MixSound("C:\datei1.wav", "C:\datei2.wav",1,1)
If Sound
  PlaySound(Sound,0,100)
endif
mfg,
DarkPlayer
Benutzeravatar
Delle
Beiträge: 1132
Registriert: 10.05.2005 22:48

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Delle »

Hm... sieht auf den ersten Blick alles soweit logisch aus ;)

Spielt bei mir aber komischerweise immer nur einen Sound davon ab...

Beispiel:

Code: Alles auswählen

InitSound()
Sound =  MixSound("1.wav", "2.wav",1,1)
If Sound
PlaySound(Sound,0,100)
Delay(5000)  
EndIf
Hab mal eine .rar-Datei gemacht mit der .pb und den 2 .wavs: http://ul.to/ci0bpi

Die 2 Sounds haben zwar nicht die gleiche Länge (wird ja aber in Deinem Code berücksichtigt), die Daten (Bitrate/KHz/Mono) sind aber gleich.

Manchmal gibt's auch einen "Ungültigen Speicherzugriff" (Lesefehler an Adresse ...)
PB 6.21 | Win 11
Dark
Beiträge: 93
Registriert: 24.08.2007 20:36
Kontaktdaten:

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Dark »

Delle hat geschrieben:Hm... sieht auf den ersten Blick alles soweit logisch aus ;)

Spielt bei mir aber komischerweise immer nur einen Sound davon ab...

Beispiel:

Code: Alles auswählen

InitSound()
Sound =  MixSound("1.wav", "2.wav",1,1)
If Sound
PlaySound(Sound,0,100)
Delay(5000)  
EndIf
Hab mal eine .rar-Datei gemacht mit der .pb und den 2 .wavs: http://ul.to/ci0bpi

Die 2 Sounds haben zwar nicht die gleiche Länge (wird ja aber in Deinem Code berücksichtigt), die Daten (Bitrate/KHz/Mono) sind aber gleich.

Manchmal gibt's auch einen "Ungültigen Speicherzugriff" (Lesefehler an Adresse ...)
Hi,

Das liegt daran, dass deine eine 1.wav scheinbar noch einen zusätzlichen Header beinhaltet, den ich hier nicht berücksichtige. Du müsstest wenn du wirklich alle Wave Dateien haben willst, ne bessere Routine schreiben um den Header einzulesen. Nach dem ich die datei erneut mit Audacity abgespeichert habe, war der extra header weg und der code hat funktioniert.

mfg,
DarkPlayer
Benutzeravatar
Delle
Beiträge: 1132
Registriert: 10.05.2005 22:48

Re: 2 Samples gleichzeitig via PlaySound() ausgeben

Beitrag von Delle »

Gute Idee! Ich lass das "Headerentfernen" einfach per Batch in Audacity machen...
PB 6.21 | Win 11
Antworten