Seite 1 von 1

Audioframe manipulieren

Verfasst: Dienstag 29. Dezember 2020, 15:35
von Toaster1337
Guten Tag,

ich beschäftige mich seit einiger Zeit mit der Manipulation von Audioframes um dessen Qualität steigern zu können um in meinem Fall die Erkennung von Sprache-zu-Text (Vosk) zu verbessern.

Geplant ist:
- Rauschen entfernen
- Stille anhängen um das zerhacken des streams zu vermeiden
- Komressor Funktion um die stimme anheben zu können
- Weitere verbesserungen

Mein Scrit sieht derzeit so aus:

Code: Alles auswählen

from array import array
from io import BytesIO
import json
import os
from pyaudio import PyAudio, paInt16
import time

from pydub import *
from audioop import *

LANG = de-de

DEBUG = True

# Mic Settings
THRESHOLD = 5
RATE = 16000
TIMEOUT_LENGTH = 1

CHUNK = 4000
SHORT_NORMALIZE = (1.0 / 32768.0)
NORMALIZE = 1 / 32768


class AudioDetection:
    def __init__(self):
        p = PyAudio()
        self.stream = p.open(rate=RATE,
                             channels=1,
                             format=paInt16,
                             input=True,
                             output=True,
                             input_device_index=None,  # None use default
                             output_device_index=None,  # None use default
                             frames_per_buffer=8000)
        self.stream.start_stream()
        self.stt = Vosk(LANG)

    @staticmethod
    def rms(frame):
        shorts = array('h', frame)
        sum_squares = sum(
            (sample * NORMALIZE) ** 2
            for sample in shorts
        )
        return (sum_squares / len(shorts)) ** 0.5 * 1000

    def detect(self, frame):
        if DEBUG:
            print("Noise detected")
        rec = [frame]
        current = time.time()
        end = time.time() + TIMEOUT_LENGTH

        while current <= end:
            data = self.stream.read(CHUNK)
            if self.rms(data) >= THRESHOLD:
                end = time.time() + TIMEOUT_LENGTH
            current = time.time()
            rec.append(data)

        # rec = AudioSegment.silent(rec)

        if DEBUG:
            print("Play Stream")
            self.stream.write(b''.join(rec))
        text = self.stt.run(b''.join(rec))
        if DEBUG:
            print("Listened: ", text)
        print('Listening...')

    def run(self):
        print("Listening...")
        while True:
            mic_input = self.stream.read(CHUNK)
            rms_val = self.rms(mic_input)
            if rms_val >= THRESHOLD:
                self.detect(mic_input)


class Vosk:
    def __init__(self, language):
        from vosk import Model, KaldiRecognizer
        if not os.path.exists("models/vosk/" + language):
            print("Please download the model from https://alphacephei.com/vosk/models and unpack as "
                  "'" + language + "' in 'models/vosk'.")
            exit(1)

        model = Model("models/vosk/" + language)
        self.rec = KaldiRecognizer(model, RATE)

    def run(self, audiostream):
        audiostream = BytesIO(audiostream)
        while True:
            data = audiostream.read(4000)
            if len(data) == 0:
                break
            if self.rec.AcceptWaveform(data):
                if DEBUG:
                    res = json.loads(self.rec.Result())
                    print("DEBUG: " + res['text'])

        fres = json.loads(self.rec.FinalResult())
        return fres['text']


if __name__ == '__main__':
    recorder = AudioDetection()
    recorder.run()
Jedoch sobald ich versuche das ganze über audioop, Pydub oder eigenen funktionen zu manipulieren bekomme ich gleich fehler wie "int found need byte like"
Mein Grundgedanke ist das ich ersmal die byteorder berücksichten sollte So das man mit Little Endian arbeitet doch wie gehts weiter?
Mir gehen langsam die Ideen aus und hoffe ihr könnt mir dabei weiterhelfen.

MfG
Toaster1337

Re: Audioframe manipulieren

Verfasst: Mittwoch 30. Dezember 2020, 17:33
von Toaster1337
Weis jemand mit welchem Modul(en) ich die raw audiodaten vom stream beabreiten kann um zb. Rauschen zu entfernen?

Re: Audioframe manipulieren

Verfasst: Mittwoch 30. Dezember 2020, 17:37
von __deets__
Du musst den eingehenden Frame eben so wandeln wie du ihn später brauchst. 16bit pcm zu float64 zb.

Re: Audioframe manipulieren

Verfasst: Mittwoch 30. Dezember 2020, 17:49
von Toaster1337
Was ich brauche ist:
Channels: 1 (mono)
Rate: 16000
Format: 16bit

Dies ist doch bereits in pyaudio festgelegt?

Re: Audioframe manipulieren

Verfasst: Mittwoch 30. Dezember 2020, 18:14
von __deets__
Du hast ja oben eine entsprechende Fehlermeldung angegeben. Die Bibliothek liefert die daten in einem definierten Format, sollte sich in der Dokumentation finden, wie Genau das aussieht. Und du hat’s Algorithmen, die ein anderes brauchen. Du musst also wandeln.

Re: Audioframe manipulieren

Verfasst: Mittwoch 30. Dezember 2020, 19:56
von Toaster1337
__deets__ hat geschrieben: Mittwoch 30. Dezember 2020, 18:14 Du hast ja oben eine entsprechende Fehlermeldung angegeben. Die Bibliothek liefert die daten in einem definierten Format, sollte sich in der Dokumentation finden, wie Genau das aussieht. Und du hat’s Algorithmen, die ein anderes brauchen. Du musst also wandeln.
Klingt sinnvoll da jeder versuch scheitert...
Könnte mir jemand ein Beispiel geben wie ich das umsetzten könnte?
Bin ziemlich neu was Audiomanipulation angeht und versuche mich da zurecht zu finden.

Erstmal will ich damit beginnen das Grundrauschen/Hintergrund geräusdche zu entfernen.
Da ist natürlich die Frage womit sich dies gut lösen lässt.
Mir schwebt PyDub oder Audioop vor doch vielleicht weis jemand besseren Rat.

Re: Audioframe manipulieren

Verfasst: Mittwoch 30. Dezember 2020, 20:02
von __deets__
Mir fehlt der konkrete Code mit der konkreten (nicht paraphrasierten) Fehlermeldung. Denn dann kann man sehen, welcher Aufruf da gemacht wird. Mir jetzt selbst ein Beispiel aus den Fingern zu saugen fehlt die Zeit.

Re: Audioframe manipulieren

Verfasst: Donnerstag 31. Dezember 2020, 14:21
von Toaster1337
Würde sich meines Erachtens nach gut mit Audioop umsetzten lassen:

Code Beispiel:

Code: Alles auswählen

def echocancel(outputdata, inputdata):
    pos = audioop.findmax(outputdata, 800)    # one tenth second
    out_test = outputdata[pos*2:]
    in_test = inputdata[pos*2:]
    ipos, factor = audioop.findfit(in_test, out_test)
    # Optional (for better cancellation):
    # factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)],
    #              out_test)
    prefill = '\0'*(pos+ipos)*2
    postfill = '\0'*(len(inputdata)-len(prefill)-len(outputdata))
    outputdata = prefill + audioop.mul(outputdata, 2, -factor) + postfill
    return audioop.add(inputdata, outputdata, 2)
Dazu müsste man ja eigentlich nur eine Stilles audio mit dem Grunrauschen aufnehmen und in dieser funktion rausschneiden lassen.

Re: Audioframe manipulieren

Verfasst: Donnerstag 31. Dezember 2020, 14:26
von __blackjack__
Bei `prefill`/`postfill` müsste es wohl ``b"\0" * length`` heissen wobei das IMHO weniger leicht zu lesen/verstehen ist als ``bytes(length)``.

Re: Audioframe manipulieren

Verfasst: Donnerstag 31. Dezember 2020, 14:26
von __deets__
Das ist doch kein Code, der einen Audio Frame vom System bekommt. Sondern irgendeine Verarbeitung. Womit der Wunsch nach einem nachvollziehbaren Beispiel nicht erfüllt ist.

Was du da zum grundrauschen mutmaßt, kann ich so nicht beurteilen. DSP ist nicht trivial. Das war ja aber eigentlich auch nicht die Frage.