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:

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.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

@ thelittlebug: Also die handelsüblichen Kompressoren arbeiten nach dem Threshold, Ratio, Attack und Release - Prinzip, wie es hier beschrieben wird. Beim SOX-Kompressor reagieren Attack und Decay (= Release) genau entgegengesetzt, wie erwartet. Eine Ratio gibt es nicht. Über "in-dB1,out-dB1" ist nicht nur eine Veringerung, sondern auch eine Verstärkung des Ausgangs-Signals möglich. Der Befehl heißt "Compander". Ein Kompanderist aber was ganz Anderes :roll: Noisegate oder Downwardexpander sind mit dem SOX-Kompressor ebenfalls möglich. Der SOX-Kompressor arbeitet eher wie ein Hüllkurvengenerator. Probiers einfach aus. Es lassen sich damit durchaus kompressorartige Effekte herstellen, man muß nur ein wenig umdenken.

FFT geht auch recht fix mit tkSnack, inkl. grafischer Darstellung.

@BackJack: So isses (fast). Keine gleichmäßige Pegelabsenkung. Der Verstärkungseffekt tritt durch das Output-Gain auf, was aber nichts anderes als ein nachgeschalteter Aufholverstärker und auch nicht immer vorhanden ist.

Ansonsten gibt es hier eine weitere Version, wo jeder Effekt sein eigenes Klassen-Objekt hat.

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

snakeseven hat geschrieben:Ansonsten gibt es hier eine weitere Version, wo jeder Effekt sein eigenes Klassen-Objekt hat.
Jetzt musst du dir noch abgewöhnen Module mit *-Importen reinzuziehen, siehe [wiki]Import[/wiki].
Achja und,

Code: Alles auswählen

# ---------------------------------------------------------------------------- GLOBALS ------------------------------------------------------------------------------------
sollte auch weg - es nervt unfassbar und ist auch noch Falsch. Sowas heißt nämlich nicht "Globals". Auch können Klassennamen mehr als drei Großbuchstaben enthalten - das fördert die Lesbarkeit enorm. PEP8 meint, dass man statt ``buildframe()`` besser ``build_frame()`` und statt ``setvalues()`` besser ``set_values()`` verwenden sollte. Außerdem sollten Kommentare über den Zeilen die man beschreibt stehen und nach daneben - fördert die Übersichtlichkeit und Hilft Zeilen kürzer als 79 Zeichen zu halten. Auch die ``call_volume()`` Funktionen sind allesammt kopiert und somit redundant. Man braucht nur eine generische Funktion, die alle Klassen abdeckt. Zur not kann man dann die anderen ``call_*()``-Funktionen per Lambda-Einzeiler wieder erstellen. Zuletzt ist es unnötig ``__init__()`` jemals explizit aufzurufen - das passiert automatisch wenn ein Objekt instanziiert wird.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Hi Leonidas,
das hört sich nach einer sinnvollen Beschäftigung für ein verregnetes Himmelfahrt an :) Danke für die Verbesserungsvorschläge !

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

Leonidas hat geschrieben: Auch die ``call_volume()`` Funktionen sind allesammt kopiert und somit redundant. Man braucht nur eine generische Funktion, die alle Klassen abdeckt.
Das Problem das ich dabei habe ist, daß man dem 'command=' Befehl der Buttons anscheinend keine Parameter übergeben kann, z.B. command = Play('Volume'). Wie können die einzelnen Aufrufe dann voneinander unterschieden werden ?
Zuletzt ist es unnötig ``__init__()`` jemals explizit aufzurufen - das passiert automatisch wenn ein Objekt instanziiert wird.
Hat bei mir nicht geklappt. Wenn ich den __init__() Aufruf in den call_effect() Funktionen weglasse, werden die Variablen nicht gesetzt ?

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

snakeseven hat geschrieben:Das Problem das ich dabei habe ist, daß man dem 'command=' Befehl der Buttons anscheinend keine Parameter übergeben kann, z.B. command = Play('Volume'). Wie können die einzelnen Aufrufe dann voneinander unterschieden werden ?
Mit ``lambda``:

Code: Alles auswählen

command = lambda: Play('Volume')
Und das andere Problem kann nicht sein. Schreib dir mal in die Konstruktoren ein ``print`` rein - du wist sehen, dass dem Konstruieren eines Klassenobjekts der Konstruktor automatisch aufgerufen wird.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Wobei ich da jetzt schon wieder in `Play()` eine ``if``/``else``-Kaskade sehe. Wenn man sich die ganzen `call_*()`-Funktionen ansieht, dann ändert sich ja nur das Effekt-Objekt. Kann man also so verallgemeinern:

Code: Alles auswählen

def call_effect(effect):
    effect.__init__()  # Dubios!
    effect.build_frame()
    effect.set_values()
Und bei den Lambdafunktionen gibt man dann das Effekt-Objekt an:

Code: Alles auswählen

lambda: call_effect(VOL)
Wobei man bei der Namensgebung vielleicht die Klasse besser `Volume` und das Objekt `VOLUME` nennen sollte. Insgesamt ist die "Globalität" und Verflechtung in dem Programm immer noch extrem hoch.

Zu `__init__()` da gehört alles rein was bei der *Erzeugung* des Objekts passiert. Wenn mehrere Objekte in der `__init__()` dieselbe globale Variable setzen und Du die Objekte alle auf einen Schlag nacheinander erzeugst, dann "gewinnt" natürlich das zuletzt erzeugte Objekt.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

Hi,
Lambda kannte ich noch nicht. Geht gut damit !
__init__ wird nur bei Programmstart ausgeführt, wenn ich die Objekte bilde (Effect = EFFECT()). Wenn ich die Effekte über call_effect(effect) wechsel, werden der Konstruktoren nicht aufgerufen und somit die globalen Variablen nicht gesetzt.

Ganz ohne globale Variablen kann ich es mir schwer vorstellen. Play() muß 'wissen', welcher Effekt gerade aktiv ist und den entsprechenden Kommando-String an SOX schicken.

Gruss, Seven
BlackJack

Alles was `play()` wissen muss, sollte als Argument übergeben werden. Ein Effekt-Objekt kennt alle seine Werte und sollte auch ein paar von den "globalen" Werten kennen, also könnte ein Effektobjekt auch eine `play()`-Methode bekommen. Von der Möglichkeit Argumente zu übergeben und Rückgabwerte zurückzugeben wird fast gar kein Gebrauch gemacht. Deswegen war die erste Variante selbst für ein rein prozedurales Programm stilistisch nicht besonders gut.

Du könntest den Effekten ein Namensattribut verpassen und die Objekte in eine Liste stecken. Dann kann man das Programm so umschreiben, dass der Name nur einmal im Programm stehen muss und die Buttons in einer Schleife erzeugt werden können.

In den `build_frame()`-Methoden ist viel sich wiederholender Quelltext, sowohl auf die Methode bezogen als auch in einzelnen Aufrufen in den Methoden. Da kann man sicher einiges herausziehen.
snakeseven
User
Beiträge: 408
Registriert: Freitag 7. Oktober 2005, 14:37
Wohnort: Berlin
Kontaktdaten:

aktuelle Version.
Mit den Objekten in der Liste war eine gute Idee. Neue Effekte lassen sich so recht schnell dazu programmieren.

Gruss, Seven
Antworten