Seite 1 von 2
Verfasst: Donnerstag 10. Mai 2007, 15:48
von snakeseven
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
Verfasst: Donnerstag 10. Mai 2007, 18:01
von 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'
Verfasst: Freitag 11. Mai 2007, 08:01
von snakeseven
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
Verfasst: Freitag 11. Mai 2007, 22:10
von thelittlebug
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

) 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

).
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

Damit wird einem das "if" komplett abgenommen.
Siehe hierzu BlackFin DSP ( gibts ab 3€ im 1000er Pack

)
lgherby
( sry, war auch offtopic dabei )
Verfasst: Freitag 11. Mai 2007, 22:34
von 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.
Verfasst: Sonntag 13. Mai 2007, 23:39
von snakeseven
@ 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

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
Verfasst: Montag 14. Mai 2007, 18:19
von Leonidas
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.
Verfasst: Dienstag 15. Mai 2007, 18:50
von snakeseven
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
Verfasst: Mittwoch 16. Mai 2007, 11:40
von snakeseven
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
Verfasst: Mittwoch 16. Mai 2007, 16:09
von Leonidas
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``:
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.
Verfasst: Mittwoch 16. Mai 2007, 16:26
von 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:
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.
Verfasst: Donnerstag 17. Mai 2007, 14:02
von snakeseven
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
Verfasst: Donnerstag 17. Mai 2007, 15:38
von 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.
Verfasst: Freitag 18. Mai 2007, 11:38
von snakeseven
aktuelle Version.
Mit den Objekten in der Liste war eine gute Idee. Neue Effekte lassen sich so recht schnell dazu programmieren.
Gruss, Seven