GUI für SOX

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Hallöchen,

es interessierte mich, was SOX aus Audiosignalen so macht und was genau die einzelnen Parameter bewirken. Mit tkSnack zur Visualisierung und ein bischen Tkinter drum rum ist eine GUI für SOX entstanden, die gerne erweitert und verbessert werden darf. Freue mich auf Anregungen! Hier der Quelltext.

Gruss, Seven
abgdf

Was ich sehe (die Übertragung auf Linux und meine Soundkarte ist wohl nicht so ganz leicht) gefällt mir sehr.

Ebenso der strukturierte Programmierstil (ich steh eigentlich nicht so auf OOP) :D.

Wäre eigentlich eine sehr gute Ergänzung zu

http://www.python-forum.de/topic-8111.html

Viele Grüße
BlackJack

Wo siehst Du da bitte strukturierten Programmierstil? Es werden ja nicht einmal Funktionen richtig verwendet, die sind einfach nur ein Ersatz für Zeilennummern in alten BASIC-Programmen. Alle Daten werden über globale Variablen ausgetauscht. :shock:

Das viele "copy'n'paste" ist auch sehr unschön.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

BlackJack hat geschrieben:Wo siehst Du da bitte strukturierten Programmierstil? Es werden ja nicht einmal Funktionen richtig verwendet, die sind einfach nur ein Ersatz für Zeilennummern in alten BASIC-Programmen. Alle Daten werden über globale Variablen ausgetauscht. :shock:

Das viele "copy'n'paste" ist auch sehr unschön.
Hi Black-Jack,
ich bin hier nicht angetreten, einen beispielhaften Programmierstil zu präsentieren, sondern eine einfache, visuelle Schnittstelle zu Sox herzustellen, auch für Nichtprogrammierer. Natürlich kann man die globalen Variablen auch über eine Klasse "Globelvars" oder so realisieren, aber macht es das bischen Code übersichtlicher, wenn jeder Variablen noch ein Klassenname vorangestellt wird ? Außerdem sehe ich OOP in dieser Anwendung sowieso nicht. Es gibt keine Funktion, die universell ist und z.B. in einer Library seinen Platz finden sollte.
Wer sich daraus eine optimierte Applikation basteln will, kann das ja machen. Mir geht es darum, die sehr undurchsichtige und sehr schlecht dokumentierte Funktionsweise von Sox zu entschlüsseln. Der Kompressor von Sox z.B. funktioniert gänzlich anders, als alle mir bekannten Kompressoren und ich kenne ziemlich viele.

Copy and Paste ist zugegeben unschön, aber spart Zeit. Zum Ausprobieren von Sox reicht es jedenfalls allemal und nicht jeder Sound-Engineer oder Musiker ist passionierter OOP-Programmierer. Aber wie ich schon sagte, wer es verbessern will, darf sich frei bedienen und ich persönlich freue mich über jede Anregung oder Kritik, so auch über deine.

Für alle Sox-Fans: Sox 1.3 ist draußen, das hat jetzt auch einen EQ. Da spart man sich den umständlichen Weg über den Multibandkompressor.

Gruss, Seven
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

abgdf hat geschrieben:
Wäre eigentlich eine sehr gute Ergänzung zu

http://www.python-forum.de/topic-8111.html

Viele Grüße
Hi abgdf:
Oranje kannte ich noch nicht. Danke für den Link, sieht sehr ausgereift aus. Muß ich mir gleich mal ansehen.

Gruss, Seven
BlackJack

snakeseven hat geschrieben:ich bin hier nicht angetreten, einen beispielhaften Programmierstil zu präsentieren, sondern eine einfache, visuelle Schnittstelle zu Sox herzustellen, auch für Nichtprogrammierer. Natürlich kann man die globalen Variablen auch über eine Klasse "Globelvars" oder so realisieren, aber macht es das bischen Code übersichtlicher, wenn jeder Variablen noch ein Klassenname vorangestellt wird ?
Nein dadurch wird es selbstverständlich nicht übersichtlicher, sie wären ja immer noch global, zumindest in der Verwendung, d.h. man müsste immer noch den gesamten Quelltext durchsehen um festzustellen, wo die Variablen benutzt werden und wofür. Und umgekehrt weiss man dort wo sie benutzt werden, nicht wo sie *noch* benutzt werden. Zum Beispiel ob der Wert nach einem beliebigen Funktionsaufruf immer noch der selbe ist wie vorher.
Außerdem sehe ich OOP in dieser Anwendung sowieso nicht. Es gibt keine Funktion, die universell ist und z.B. in einer Library seinen Platz finden sollte.
Man muss keine Bibliothek schreiben wollen um Vorteile aus OOP zu ziehen. Es geht auch um das sinnvolle Strukturieren eines Programms um es leichter lesbar und leichter erweiter- oder veränderbar zu machen.

Aber selbst ohne OOP ist das ein schlechtes Programm. Keine der Funktionen nimmt Argumente entgegen oder gibt einen Wert zurück. Damit sind das einfach nur verkleidete Zeilennummern und die Aufrufe GOTO's. Das ist tiefstes Mittelalter.
Copy and Paste ist zugegeben unschön, aber spart Zeit.
Copy'n'Paste spart im Endeffekt keine Zeit, ausser man weiss, dass man das Programm wirklich nur einmal kurz braucht und nie anpassen oder erweitern muss. Dann wird es nämlich unangenehm und kostet richtig Zeit.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Hi,
Ich verstehe voll und ganz, was du meinst, aber du hast da auch andere, professionellere Beurteilungskriterien. Ich schätze mal 60% der Sox-Anwender sind Programmierleien oder Gelegenheitsprogrammierer wie ich, die einfach nur schnell und unkomplliziert ihr Lieblingstool um eine optische Schnittstelle erweitern wollen. Dafür ist dieser klassische "Mittelalterstil" meiner Meinung nach geeigneter, als komplizierte OOP-Programme mit unzähligen "selfs" und kryptischen Parameterübergaben. Will Peter Meier das Programm z.B. um das Feature Delay erweitern, kopiert er sich die entsprechenden Zeilen einer der anderen Effekte und paßt die Sox-Parameter an. So soll es jedenfalls gedacht sein. Ein Hilfswerkzeug fürs Studio und keine Grundlage für einen professionellen Audioeditor.
Wobei es mich schon interessieren würde, wie es als OOP-Programm aussehen würde. Sicherlich etliche Zeilen weniger, aber leserlicher ?

Gruss, Seven
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snakeseven hat geschrieben:Ich verstehe voll und ganz, was du meinst, aber du hast da auch andere, professionellere Beurteilungskriterien.
Können Programme von Hobbyprogrammierern nicht strukturiert sein?
snakeseven hat geschrieben:Ich schätze mal 60% der Sox-Anwender sind Programmierleien oder Gelegenheitsprogrammierer wie ich, die einfach nur schnell und unkomplliziert ihr Lieblingstool um eine optische Schnittstelle erweitern wollen. Dafür ist dieser klassische "Mittelalterstil" meiner Meinung nach geeigneter, als komplizierte OOP-Programme mit unzähligen "selfs" und kryptischen Parameterübergaben.
Komisch, ich finde dieses unfasssbar umständliche und unnütze, unstrukturierte OOP aber irgendwie lesbarer und einleuchtender. Ist meine Wahrnehmung gestört?
Was mich als jemand der das programm erweitern will viel mehr stört, ist der eklatante Mangel brauchbarer Kommentare. ``peWIN``, anyone?
Außerdem setzt du die Variable ``args`` selbst zusammen, was erstens unschön gelöst ist und zweitens vollständig unnötig, weil ``subprocess.Popen()`` das selbst kann.
snakeseven hat geschrieben:Will Peter Meier das Programm z.B. um das Feature Delay erweitern, kopiert er sich die entsprechenden Zeilen einer der anderen Effekte und paßt die Sox-Parameter an.
Und mit OOP ist das nicht mehr Möglich? Ich sehe da keinerlei Zusammenhang.
snakeseven hat geschrieben:Wobei es mich schon interessieren würde, wie es als OOP-Programm aussehen würde. Sicherlich etliche Zeilen weniger, aber leserlicher ?
Womöglich einige Zeilen länger und sicherlich verständlicher.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@snakeseven: Ob es strukturierter programmiert kürzer wird weiss ich nicht. Aber länger heisst nicht unbedingt unverständlicher weil Funktionen und Klassen Funktionalität kapseln sollen. Viele kleine Funktionen, die eine Sache gut und übersichlich lösen, brauchen oft mehr Zeilen als eine "Monsterfunktion" die alles auf einmal macht, durch die man aber nur mit Mühe durchsteigt.

Aber wie schon gesagt: Selbst ohne OOP ist der Quelltext nicht schön. Temporäre Dateien kann man zum Beispiel mit dem `tempfile`-Modul anlegen. Das "denkt" sich einen passenden eindeutigen Dateinamen aus, der im systemweiten temporären Ordner liegt. Um diese simple Änderung durchzuführen muss man wegen dem vielen copy'n'paste ganz viele Stellen ändern. Mit der Gefahr, dass man eine vergisst oder einen Fehler an einer Stelle einbaut.

Viele der Effektfunktionen haben einen identischen Aufbau, der sich ganz einfach in eine eigene Funktion herausziehen lässt. Exemplarisch (und ungetestet) für die ersten drei Funktionen:

Code: Alles auswählen

def call_sox(command, *args):
    source = path_wav + ' '
    dest = last_visit + 'temp.wav '
    sox_command = '%s -q %s %stemp.wav %s ' % (sox_path,
                                               path_wav,
                                               last_visit,
                                               command)
    sox_command += ' '.join(map(str, args))
    proz = subprocess.Popen(sox_command, shell=True)
    proz.wait()
    pos = string.find(args, command)
    lb1["text"] = sox_command[pos:]


def deesser():
    call_sox('mcompand',
             '"0.001,0.1 0,0"',
             dfreq,
             '"0.001,0.1%s,-30 0 0 0"' % dthresh)


def compressor():
    call_sox('compand',
             '%s,%s' % (cattack, cdecay),
             '%s,%s' % (cthresh, cgain),
             '0 0 0')


def highpass():
    call_sox('highpass', hpf, 'vol', hpv, 'dB')
Das Erweitern um einen zusätzlichen Effekt sollte jetzt ein klein wenig einfacher und übersichtlicher sein.

Wobei es natürlich immer noch zu komplex und fehleranfällig ist. Man muss erst einmal neue globale Namen für die Argumente des Effekts finden, die sich nicht mit bereits verwendeten Namen überschneiden dürfen. Dann muss man eine vorhandene Funktion kopieren und anpassen. Und den GUI-Teil dazu schreiben. Je mehr Effekte hinzu kommen um so unübersichtlicher wird das Ganze.

Wenn es wirklich einfach für Endanwender sein soll, dann müsste man so einen Effekt deklarativ beschreiben können. Der Anwender müsste den Namen des Effekts, die Namen der Argumente, deren Typ, Voreinstellung und Wertebereich angeben. Und vielleicht noch ein "Template" für die Befehlszeile. Der Rest, inklusive der GUI-Aufbau, sollte automatisch passieren.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Hi,
@Leonidas: Kommentare sollten in der Tat nicht fehlen ! Werde ich ergänzen. Subprocess.Popen baut sich den Pfad selbst zusammen ? Woher weiss es die Parameter, kannst du mir ein Beispiel geben, wie das geht ?

@Black Jack: danke für das Beispiel !

Ich kann OOP halt nicht so gut lesen, wie viele andere hier. Beschäftige mich damit allerdings auch nur rudimentär. Aber zurück zum eigentlichen Thema, um hier nicht völlig offtopic zu werden:

Ich würde mich freuen, wenn es im Forum Soxuser gibt, die Lust haben, den Code um neue Effekte zu erweitern. Ich denke, die hervorragende Audioqualität der Soxalgorythmen ist es wert. Und wenn sich einer dann noch die Mühe macht, Struktur in den Programm-Code zu bringen, dann sind alle happy (inklusive mir!).

Gruss, Seven
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snakeseven hat geschrieben:@Leonidas: Kommentare sollten in der Tat nicht fehlen ! Werde ich ergänzen. Subprocess.Popen baut sich den Pfad selbst zusammen ? Woher weiss es die Parameter, kannst du mir ein Beispiel geben, wie das geht ?
Lies dir am besten den [wiki=Neue Features#Subprocess]Abschnitt aus dem Wiki[/wiki] durch.
Zuletzt geändert von Leonidas am Mittwoch 9. Mai 2007, 20:40, insgesamt 1-mal geändert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Es gibt anscheinend schon ein Tcl/Tk-Frontend: http://studio.sourceforge.net/

Ansonsten wäre es vielleicht nicht schlecht ein Modul zu erstellen, das Sox für Python zur Verfügung stellt. Also eine Sammlung von Funktionen, oder Objekten die den Aufruf von Sox kapseln. Darauf kann man dann verschiedene GUIs aufsetzen.

Da man Sox prima in einer Pipe verwenden kann, könnte man die API so gestalten, dass man verschiedene Aufrufe verketten kann ohne das immer temporäre Dateien bei jedem Schritt erzeugt werden müssen.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

BlackJack hat geschrieben:Es gibt anscheinend schon ein Tcl/Tk-Frontend: http://studio.sourceforge.net/

Da man Sox prima in einer Pipe verwenden kann, könnte man die API so gestalten, dass man verschiedene Aufrufe verketten kann ohne das immer temporäre Dateien bei jedem Schritt erzeugt werden müssen.
Ja, das Front-End kenne ich. Beschränkt sich leider nur auf wenige Standard-Effekte und mit Tcl/Tk kenne ich mich nicht aus.
Das Pipen von Sox zu Sox klappt gut, aber leider von TkSnack nach Sox nicht, oder ich habs noch nicht rausgefunden !?

Habe derweil eine OOP-Version von der GUI erstellt, die noch kommentiert werden muss. 384 statt 514 Zeilen ist schon Einiges weniger. Poste ich, sobald die Comments fertig sind.

Nächtle, Seven
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Moin,
Neue Version.
Gestandene OOPler können das sicherlich besser, aber immerhin gibt es keine globalen Variablen mehr. 442 Zeilen sinds dann doch geworden. Ich finds auch nicht wirklich leserlicher, aber das kann auch mit meinem Programmierstil in Sachen OOP zu tun haben. Immerhin sind jetzt auch Komentare drin. Freue mich wie immer über Verbesserungsvorschläge !

Gruss, Seven
BlackJack

Es ist noch nicht so viel leserlicher weil nicht wirklich alles ordentlich "entkoppelt" ist. Man muss, wenn man einen neuen Effekt hinzufügen will, immer noch Änderungen über das gesamte Programm verteilt vornehmen und die Namen für die Argumente der einzelnen Effekte liegen auch immer noch alle in einem einzigen Namensraum.

Und die vielen ``if``/``elif``-Kaskaden über den Effektnamen sind ein gutes Zeichen dafür, dass man Code, der eigentlich zusammengehört, nämlich zu *einem* Effekt, über das gesamte Programm verstreut hat. Das ist ein Ansatzpunkt für einen sinnvollen Einsatz von Objekten. Statt eine ``if``-Abfrage pro Effekt könnte da *ein* Methodenaufruf auf einem Effekt-Objekt stehen. Vereinfacht sieht die Situation ungefähr so aus:

Code: Alles auswählen

creature = None

def speak():
    if creature == 'bird':
        print 'Piep'
    elif creature == 'dog':
        print 'Wau'
    elif creature == 'snake':
        print 'Ssschhhhh'


def move():
    if creature == 'bird':
        print 'flatter'
    elif creature == 'dog':
        print 'renn'
    elif creature == 'snake':
        print 'kriech'


def main():
    global creature
    for creature in ('bird', 'dog', 'snake'):
        speak()
        move()
Das würde in OOP so aussehen:

Code: Alles auswählen

class Bird(object):
    def speak(self):
        print 'Piep'
    
    def move(self):
        print 'flatter'


class Dog(object):
    def speak(self):
        print 'Wau'
    
    def move(self):
        print 'renn'


class Snake(object):
    def speak(self):
        print 'Ssschhhhh'
    
    def move(self):
        print 'kriech'


def main():
    for creature in (Bird(), Dog(), Snake()):
        creature.speak()
        creature.move()
Die OOP-Variante ist etwas länger, aber man kann jetzt jede Kreatur für sich allein betrachten und wenn man eine neue hinzufügen möchte, braucht man nicht mehr das ganze Programm abzusuchen und an den entsprechenden Stellen die ``if``-Abfragen erweitern, sondern braucht nur eine neue Klasse zu schreiben.

Die meisten Funktionen im `string`-Modul ist übrigens "deprecated" und sollten nicht mehr verwendet werden. `join()` gibt's als Methode auf Objekten. Und manuell Leerzeichen in die Liste einzufügen um dann mit der leeren Zeichenkette zu "joinen" stellt den Zweck der Methode ein wenig in Frage.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

BlackJack hat geschrieben:Es ist noch nicht so viel leserlicher weil nicht wirklich alles ordentlich "entkoppelt" ist.
Ja, mein Problem ist, daß die Tkinter-Widgets wiederum Funktionen aufrufen. Also muss ich für jeden Effekt schonmal zwei separate Funktionen zur Verfügung stellen, damit 'Button' und 'Scale' bedient werden.
Und die vielen ``if``/``elif``-Kaskaden über den Effektnamen sind ein gutes Zeichen dafür, dass man Code, der eigentlich zusammengehört, nämlich zu *einem* Effekt, über das gesamte Programm verstreut hat.
Wie soll man alles in ein Objekt packen, welches universell für alle Effekte gilt, wenn die Effekte völlig unterschiedliche Anforderungen haben? An Design (Anzahl Fader), Parameter (verschiedene Anzahl Parameter) und SOX-Handling. Normalize z.B. muß vorab eine statistische Auswertung vornehmen. Hier bräuchte man schonmal eine if-Abfrage, da die anderen das nicht brauchen. Der Equalizer benötig Input-Dämpfung und Output-Gain, die anderen nicht. Ich bin da überfragt !?
Und manuell Leerzeichen in die Liste einzufügen um dann mit der leeren Zeichenkette zu "joinen" stellt den Zweck der Methode ein wenig in Frage.
Stimmt, hab ich korrigiert. Was sollte man anstelle von string.join() nehmen ?

Gruss, Seven
BlackJack

Ich meinte natürlich nicht ein Objekt für alle Effekte, sondern ein Objekt pro Effekt. Wie bei dem Beispiel mit den Tieren. Ein Objekt für alle Effekte hast Du ja im Moment schon.

Fast alle Funktionen im `string`-Modul sind als Methoden auf Zeichenketten verfügbar.

Code: Alles auswählen

In [37]: ', '.join(('a', 'b', 'c'))
Out[37]: 'a, b, c'
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

BlackJack hat geschrieben:Ich meinte natürlich nicht ein Objekt für alle Effekte, sondern ein Objekt pro Effekt.


Ja, natürlich, das ist machbar und das werde ich jetzt auch angehen.
Fast alle Funktionen im `string`-Modul sind als Methoden auf Zeichenketten verfügbar.

Code: Alles auswählen

In [37]: ', '.join(('a', 'b', 'c'))
Out[37]: 'a, b, c'
okydoky

Seven
thelittlebug
User
Beiträge: 188
Registriert: Donnerstag 20. Juli 2006, 20:46
Wohnort: Wien
Kontaktdaten:

SOX kannte ich bisher noch nicht, sieht allerdings recht praktisch aus wenn man mit Sounddateien zu tun hat.

Mittels numpy ( ich hoffe das stimmt jetzt auch vielleicht wars auch numeric oder gar beide :D ) könntest du eine FFT Analyse in dein Programm einbauen.

Vielleicht hilft dir das die SOX Ausgaben zu analysieren.

Aber zum Thema Compressor, was macht SOX dort anders als andere? Sag mir wenn ich falsch liege aber ein Compressor erhöht den Signalpegel (hebt leise Bereiche) unter Verlust von Dynamik (Anzahl der möglichen Lautstärkeunterschiede wird geringer, na no na ned :D).

Im großen und ganzen sollte ein Compressor demnach nur den Pegel erhöhen.
ausgabepegel = eingabepegel * faktor

Dabei wird natürlich noch geclippt. Also quasi aufgepasst das keine zu hohen Werte entstehen können.

if ausgabepegel > maximalpegel: ausgabepegel = maximalpegel

btw. DSP Chips können das Clipping bereits in Ihren Prozessorregistern erledigen, Sachen gibts, die gibts nicht :D Damit wird einem das "if" komplett abgenommen.
Siehe hierzu BlackFin DSP ( gibts ab 3€ im 1000er Pack :D )

lgherby
( sry, war auch offtopic dabei )
BlackJack

Ganz so einfach ist es nicht, der Faktor hängt auch vom Eingangssignal ab, es wird nicht gleichmässig verstärkt. Je stärker das Eingangssignal ist, desto schwächer ist die Verstärkung. Es werden also vorwiegend leise Passagen verstärkt. Clipping müsste vermeidbar sein.

Hoffe ich habe jetzt keinen totalen Quatsch geschrieben.
Antworten