Funktion zum Mischen von WAV-Dateien

Code-Stücke können hier veröffentlicht werden.
Antworten
snakeseven
User
Beiträge: 405
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Samstag 15. April 2006, 09:17

Hallo,
hier eine einfache Funktion zum Zusammenmischen von WAV-Dateien.
Einfach deshalb, weil keine Überprüfung der übergebenen Daten stattfindet, das kann man sich aber bei Bedarf dazubasteln. Es müsste nur der WAV-Header ausgewertet werden (http://www.sonicspot.com/guide/wavefiles.html).
Gegenüber der 'add'-Funktion vom Modul 'audioop' können hier auch Files unterschiedlicher Länge zusammengemischt werden. Es bedarf zudem keiner Extrapakete wie 'Snack' oder 'Pymedia'. Verbesserungsvorschläge willkommen !

Gruss, Seven

Code: Alles auswählen

def mix(wave1,wave2):
    #Mischt 2 Wavefiles zusammen
    #Von beiden Wavefiles wird zunächst die Länge ermittelt.
    #Das kürzere Wavefile wird zum Längeren dazugemischt
    #
    import struct

    length1 = struct.unpack('l',wave1[40:44])[0]  
    length2 = struct.unpack('l',wave2[40:44])[0]
        
    if length1 >= length2:
        mixlen = length2
        header = wave1[:44]
        rest = wave1[length2:length1]
        audioblock1 = struct.unpack(str(length1)+'b',wave1[44:])  #0-44 = Headerbytes. Audioblock beginnt bei #44. 
        audioblock2 = struct.unpack(str(length2)+'b',wave2[44:])  #Die Daten sind im Format 'signed byte' (-128 <= number <= 127)
    else:
        mixlen = length1
        header = wave2[:44] 
        rest = wave2[length1:length2]
        audioblock1 = struct.unpack(str(length2)+'b',wave2[44:])
        audioblock2 = struct.unpack(str(length1)+'b',wave1[44:])
        
    temp = []
    for i in range(0,mixlen):
        value = audioblock1[i] + audioblock2[i]
        
        if value < -128: temp.append(-128)                  #Falls Clippen, begrenzen auf gültigen Wertebereich
        elif value > 127: temp.append(127)
        else: temp.append(value)
            
    result = header + struct.pack(str(mixlen)+'b',*temp) + rest
    return result
Beispiel:

Code: Alles auswählen

import winsound

f =  open('E:/Testwav1.wav', "rb")          
sound1= f.read()
f.close()
f =  open('E:/Testwav2.wav', "rb")          
sound2= f.read()
f.close()

sound1 = mix(sound1,sound2)

f =  open('E:/Testwav3.wav', "wb")          
f.write(sound1)
f.close()

winsound.PlaySound("E:/Testwav3.wav",winsound.SND_FILENAME)
snakeseven
User
Beiträge: 405
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Samstag 15. April 2006, 19:27

Update:
Das folgende Update unpackt tatsächlich nur den Teil der längeren WAV-Datei, der zum Mischen herangezogen wird. Der Rest wird unverändert vom Original geholt. Je kürzer die dazugemische WAV-Datei ist, desto schneller arbeitet die Routine.

Code: Alles auswählen

def mixwave(wave1,wave2):
    #Mischt 2 Wavefiles zusammen
    #Von beiden Wavefiles wird zunächst die Länge ermittelt.
    #Das kürzere Wavefile wird zum Längeren dazugemischt
    
    import struct

    length1 = struct.unpack('l',wave1[40:44])[0]  
    length2 = struct.unpack('l',wave2[40:44])[0]
        
    if length1 > length2:
        length1 = length2               # length1 bekommt die kürzere Länge zugewiesen
        header = wave1[:44]
        rest = wave1[length1:]
    else:
        header = wave2[:44] 
        rest = wave2[length1:]
            
    audioblock1 = struct.unpack(str(length1)+'b',wave1[44:length1+44])  #0-44 = Headerbytes. Audioblock beginnt bei #44. 
    audioblock2 = struct.unpack(str(length1)+'b',wave2[44:length1+44])  #Die Daten sind im Format 'signed byte' (-128 <= number <= 127)
        
    temp = []
    for i in range(0,length1):
        value = audioblock1[i] + audioblock2[i]

        if value < -128: temp.append(-128)                  #Falls Clippen, begrenzen auf gültigen Wertebereich
        elif value > 127: temp.append(127)
        else: temp.append(value)
            
    result = header + struct.pack(str(length1)+'b',*temp) + rest
    return result
BlackJack

Sonntag 16. April 2006, 07:48

Kannst Du zum Einlesen nicht das `wave` Modul benutzen? Da kannst Du eine Menge Abfragen bezüglich des Typs der Daten recht einfach gestalten, und kannst zum Beispiel die Funktion so generisch halten, dass sie die Bits pro Samples aus dem Header benutzt und nicht 8-Bit hart einkodiert hat.

Bei vielen gleichartigen Daten ist das `array` Modul wahrscheinlich auch effizienter als `struct`.

Und ich würde auch testen ob das manuelle erweitern (``+ '\x00' * differenz``) der kürzeren Audiodaten und anschliessendes `audioop.add()` nicht schneller ist, als addieren von einzelnen Samples in einer Python-Schleife.
snakeseven
User
Beiträge: 405
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Sonntag 16. April 2006, 14:32

Werde ich auf jeden Fall ausprobieren. Bin zwar jetzt 8 x so schnell wie zu Beginn meiner ersten versuche mit WAVs, aber schnell genug ist es mir immer noch nicht.
Gruss, Seven
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Sonntag 16. April 2006, 15:37

snakeseven hat geschrieben:aber schnell genug ist es mir immer noch nicht.
Hi Seven!

Schon mal Psyco ausprobiert? Vielleicht wird es dadurch schneller.

Beispiele: http://psyco.sourceforge.net/psycoguide/node8.html

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
snakeseven
User
Beiträge: 405
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Sonntag 16. April 2006, 23:52

gerold hat geschrieben:Schon mal Psyco ausprobiert?
Installiert hab ichs und importieren lässt es sich auch. Werde mich mal morgen näher damit beschäftigen. Die Frage ist nur, ob ich mit Psyco generierte Files auch als CGI laufen lassen kann ?
Gruss, Seven
snakeseven
User
Beiträge: 405
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Montag 17. April 2006, 09:38

Hi,
Habe Psyco mal mit den verschiedenen Optionen ausprobiert und es bringt auch ca 30% mehr Verarbeitungsgeschwindigkeit.
Werde aber dennoch audioop zusätzlich testen. Jetzt begreife ich erst, wie schnell und effektiv Snack ist.
Gruss, Seven
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 17. April 2006, 10:47

Hi Seven!

Da du ja recht viel mit Sounds rumwerkelst ;-), denke ich mal, dass du dieses Tool auf jeden Fall kennen lernen solltest. Es hat zwar nichts mit Python zu tun und mischen kann man damit auch nicht, aber trotzdem...

SoX: http://sox.sourceforge.net/

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
snakeseven
User
Beiträge: 405
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Montag 17. April 2006, 12:50

@Gerold: Ein sehr vielversprechendes Tool ! Vor allem, weil es sich über subprocess() via command-line gut aus Python heraus nutzen ließe. Leider nur für Linux & Co, aber interessant allemal.

Und hier die Mischfunktion mit 'audioop'. Hat den entscheidenden Geschwindigkeitskick gebracht und ist jetzt so schnell wie 'mix()' aus dem Snackpaket. Nur ohne das ganze nervige Tkinter-Zeugs.

Code: Alles auswählen

def mixwave(wave1,wave2):
    import audioop
    
    #Mischt 2 Wavefiles zusammen
    #Von beiden Wavefiles wird zunächst die Länge ermittelt.
    #Das kürzere Wavefile wird zum Längeren dazugemischt

    length1 = len(wave1)     
    length2 = len(wave2)
        
    if length1 > length2:
        header = wave1[:44]
        rest = wave1[length2:]
        length1 = length2              # length1 bekommt die kürzere Länge zugewiesen
    else:
        header = wave2[:44] 
        rest = wave2[length1:]
            
    audiodata1 = wave1[44:length1]  #0-44 = Headerbytes. Audiodaten beginnen bei #44. 
    audiodata2 = wave2[44:length1]  
        
    result = header + audioop.add(audiodata1,audiodata2,1) + rest
    return result
@BlackJack: Das Modul 'wave' liest zwar den WAV-Header mit, stellt ihn dem Nutzer aber nicht direkt zur Verfügung und 'readframes(n)' liefert nur die reinen Audiodaten (ab Byte #44). Ohne Header geht aber nix, nichtmal abspielen.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 17. April 2006, 13:00

snakeseven hat geschrieben:Leider nur für Linux & Co, aber interessant allemal.
Hi Seven!

Ich habe auch etwas von einem Windows-Binary gelesen. Schau mal bei den Downloads.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 17. April 2006, 13:12

gerold hat geschrieben:Ich habe auch etwas von einem Windows-Binary gelesen.
Hi Seven!

Es wird immer besser. Bei SoX ist auch das Programm "soxmix" mit dabei. Das mischt zwei Sounds zu einem zusammen. :-)

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Montag 17. April 2006, 18:53

Vielleicht kannst du aus Python auch daran andocken:
Audacity® ist ein freier Mehrspur-Audio-Editor für Linux, Mac und Windows.
http://audacity.sourceforge.net

:lol:

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten