Seite 1 von 1

Funktion zum Mischen von WAV-Dateien

Verfasst: Samstag 15. April 2006, 09:17
von snakeseven
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)

Verfasst: Samstag 15. April 2006, 19:27
von snakeseven
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

Verfasst: Sonntag 16. April 2006, 07:48
von BlackJack
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.

Verfasst: Sonntag 16. April 2006, 14:32
von snakeseven
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

Verfasst: Sonntag 16. April 2006, 15:37
von gerold
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
:-)

Verfasst: Sonntag 16. April 2006, 23:52
von snakeseven
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

Verfasst: Montag 17. April 2006, 09:38
von snakeseven
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

Verfasst: Montag 17. April 2006, 10:47
von gerold
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
:-)

Verfasst: Montag 17. April 2006, 12:50
von snakeseven
@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.

Verfasst: Montag 17. April 2006, 13:00
von gerold
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
:-)

Verfasst: Montag 17. April 2006, 13:12
von gerold
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
:-)

Verfasst: Montag 17. April 2006, 18:53
von jens
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: