| |||||||
Du magst keine Werbung? Wir auch nicht!
Einfach registrieren und die Werbung ist weg. Diese Nachricht sehen nur nicht registrierte Nutzer.
![]() |
| | LinkBack | Themen-Optionen | Ansicht |
| | #1 (permalink) |
| Neuer User Registriert seit: Sep 2004 Ort: Hamburg
Beiträge: 35
| Sound Sequencer Problem / Sound Timing
Ich möchte einen kleinen Sound-Sequencer bauen und bekomme keinen sauberen Takt hin. Ich habe mir überlegt, dass es einen Taktgeber gibt, der z.B. jeden Takt ein Event dispatched auf dem man verschiedene Sounds starten kann. Die Idee ist, dass der User eine Auswahl an Sounds hat und diese an- und ausschalten kann. Hat man einen Sound, welcher 4 Takte lang ist, wird dieser dann erst nach 4 Takten neugestartet. Eine Basedrum, die jeden Takt läuft, würde dann logischer weise jeden Takt gestartet werden. Mann kann zwar einfach ausrechnen wie lang ein Takt ist. Z.B. für 120 BPM: 60 / 120 * 1000 = 500ms. Aber leider ist die Timer-Klasse so ungenau, dass man damit keinen sauberen Takt hinbekommt. Ein ENTER_FRAME Event kommt natürlich auch nicht in Frage, da die Events sich ja immer in dem Frame Raster bewegen, welches sich je nach CPU Auslastung auch noch verändert. Auf der Suche nach Lösungen habe ich verschiedene Ansätze gefunden: Eine Lösung für Flash MX: http://www.actionscript.org/resource...ing/Page1.html Leider funktioniert die nicht mehr in AS3, da das SOUND_COMPLETE Event unsauber kommt. Eine Lösung für AS3: http://glossar.hs-augsburg.de/Konsta...ActionScript_3) Habe ich nicht zum laufen bekommen. Der Artikel wird auch zur Zeit überarbeitet, also vielleicht stimmt da auch was nicht. André's erste Synthis: http://blog.andre-michelle.com/2006/...iocyclebuffer/ In den Kommentaren bedanken sich die Leute für die Sources, ich kann sie nicht finden..? Ich will ja eigentlich auch keinen Sound erzeugen, sondern geladene mp3's zum richtigen Zeitpunkt starten. Also mittlerweile sind wir auch soweit das Projekt für den FP10 zu kompilieren, wenn das eine Lösung bringen würde. Aber André Michelle's und Joa Ebert's Sequencer lief doch auch im FP9. Daher muss es doch eine Lösung geben. Hat irgendjemand eine Idee, oder einen verständlichen Ansatz für mein Problem? Oder einen Tipp? Link? Vielen Dank schon Mal! Raphael |
| | |
| | #2 (permalink) |
| \x3a\x6f\x29 Registriert seit: Apr 2004 Ort: paris
Beiträge: 806
|
Wenn du Samples zählst, statt einen Timer zu benutzen, bist du immer Sample-Exakt und hast keine Timing Probleme. Wenn du bei 120 BPM also 500ms pro Takt hast kannst du das ausrechnen. Bei 44.1khz hast du 44100 Samples pro 1000ms. D.h. du hast alle 44100/2 Samples einen neuen Takt. |
| | |
| | #5 (permalink) |
| Neuer User Registriert seit: Sep 2004 Ort: Hamburg
Beiträge: 35
|
Also erstmal Danke für die Antworten. Allerdings bringen sie mich nicht so richtig weiter.. Wenn ich das richtig verstehe sollte man das also ungefähr so machen (mal eben schnell mit nested functions): Code: var sound : Sound = new Sound( );
var channel : SoundChannel;
var samplesAmount : int = 60 / 120 * 44100;
var samples : int;
var i : int;
var date1 : Date;
var date2 : Date;
function countSamples (event : SampleDataEvent) : void
{
samples = Math.min( 8192, samplesAmount );
samplesAmount -= samples;
for ( i = 0; i < samples ; i++ )
{
event.data.writeFloat( 0 );
event.data.writeFloat( 0 );
}
}
function onSoundComplete (event : Event) : void
{
date1 = new Date( );
if(date2) trace( "sauberer takt. ms: " + (date1.getTime( ) - date2.getTime( )) );
date2 = new Date( );
samplesAmount = 60 / 120 * 44100;
channel = sound.play( );
channel.addEventListener( Event.SOUND_COMPLETE, onSoundComplete, false, 0, true );
// starte andere sound-objekte, die auf diesem takt liegen
}
sound.addEventListener( SampleDataEvent.SAMPLE_DATA, countSamples, false, 0, true );
channel = sound.play( );
channel.addEventListener( Event.SOUND_COMPLETE, onSoundComplete, false, 0, true ); Wenn ich es richtig verstehe, dient mein Soundobjekt ja nur als Taktgeber. Die anderen Sound-Objekte sind ja normal geladene MP3's die einfach auf dem Takt gestartet werden. Oder ist das vom Ansatz schon falsch? Gruß, Raphael |
| | |
| | #6 (permalink) | |
| [+] Registriert seit: Dec 2002 Ort: cologne
Beiträge: 2.271
| Zitat:
Du hast Zugriff auf jedes Sample. D.h. wenn du deine Samples ordentlich mitzählst, kannst du auch eine BassDrum an sampleexakter Stelle starten. Ohne weitere Timer. Schau Dir bitte auch die Beispiele an. Dort findest du einiges, was du verwerten kannst. | |
| | |
| | #7 (permalink) |
| Neuer User Registriert seit: Sep 2004 Ort: Hamburg
Beiträge: 35
|
Ok, also zähle ich die Samples nicht ordentlich? Welchen weiteren Timer meinst du? Ich hab mir die Sourcen schon mal angeschaut, aber hab da nicht wirklich was rausziehen können. Komme da leider nicht weiter. Hat jemand sonst das geblickt und hat Lust das mal an einem simplen Beispiel zu zeigen? FP9 und FP10? Sowas kann ja eigentlich keine Raketentechnik sein.. Sounds zum richtigen Zeitpunkt abspielen.. tsts |
| | |
| | #8 (permalink) |
| vermisst ein e Registriert seit: Oct 2007
Beiträge: 774
|
naja so trivial ists nicht. wie du schon rausgefunden hast, gibts beim naiven ansatz, einfach das Sound-objekt zu verwursten, keine zuverlaessige moeglichkeit - das allein ist ja schon schade genug. daher ist man gezwungen, sich die sounds neu zusammen zu basteln, was nen relativ hohen rechenaufwand fuer diesen task bedeutet. voraussetzung fuer die sample-methode ist, dass deine sounds exakt gleich lang sind. also alle ein vielfaches einer gewissen anzahl samples haben, sonst musst du sie irgendwo abschnibbeln. die sounds muessen soweit geladen sein, dass du zugriff auf die abzuspielenden samples hast, duerfte klar sein. nun erzeugst du dir ein neues sound-objekt, dem du einen listener fuer das SampleDataEvent hinzufuegst. den ganzen vorgang startest du dann mit play() auf diesem mixer-sound-objekt. innerhalb des listeners liest du mit extract() aus jedem deiner quell-sounds mindestens 2028 samples aus (das sind pro sample jeweils zwei floats bei stereo-klang). hier ist zu beachten, dass dir extract() n bytearray fuellt und jeder float 4 byte gross ist (daher sind 2028 samples 2028*2*4 bytes gross, wenn du stereo verwendest). diese samples aus den quellsounds summierst du auf und schreibst sie wie in deinem code oben ins data-property des events. pro durchgelaufenem listener addierst du die anzahl geschriebener samples auf einen counter mit druff und hast damit immer die aktuelle position. aber als erster schritt waere es vielleicht gut, alle quell-sounds in einen zu schreiben, die synchronisation kommt dann als naechster.. weil so trivial ist es dann wie gesagt doch nicht (;
__________________ krisrok.de Geändert von kRizzl (10-12-2008 um 13:08 Uhr) |
| | |
| | #9 (permalink) |
| Neuer User Registriert seit: Sep 2004 Ort: Hamburg
Beiträge: 35
|
Ah, das ist interessant! Das bedeutet, dass ich gar nicht auf das SOUND_COMPLETE warte und dann die Soundobjekte mit play() starte, sondern ich lasse einen Sound als Taktgeber unendlich laufen und in dem SampleDataEvent.SAMPLE_DATA Listener hole ich mir von allen Sounds per extract() die Bytes und schreibe sie in meinen Taktgeber Sound! Das ist die Antwort die ich brauchte. So verstehe ich jedenfalls schon mal das Grundprinzip besser. Werde das heute Abend mal austesten. Das geht allerdings nur im FP10, oder? Weißt du wie man das im FP9 gemacht hat? André's Sequencer lief ja auch schon vor FP10 Zeiten.. Ach, und meinst du 2048 samples? Weil 2028 ja keine Zweierpotenz ist. Und wie kommst du eigentlich auf diese Mindestzahl (2028 oder 2048)? Man kann ja pro Event 512 bis 8192 Samples schreiben.. Dank und Gruß, Raphael |
| | |
| | #10 (permalink) |
| vermisst ein e Registriert seit: Oct 2007
Beiträge: 774
|
natuerlich 2048.. gnarf wie ich auf die anzahl komm, weiss ich auch nicht. ich hatte im kopp, dass es zwischen 2048-8192 sein muessen.das geht nur ab fp10, yep. die burschen da oben haben sich das schon im fp9 ueber nen cleveren hack ermoeglicht. ist allerdings noch rechenlastiger, hat aber inzwischen vielleicht sogar weniger latenz? (;
__________________ krisrok.de Geändert von kRizzl (10-12-2008 um 13:32 Uhr) |
| | |
| | #11 (permalink) |
| Neuer User Registriert seit: Sep 2004 Ort: Hamburg
Beiträge: 35
|
Ah ok, alles klar ![]() Verdammt, ich war doch auf der FITC in Amsterdam dieses Jahr und habe mir angeschaut wie André das gehackt hat. Aber richtig dran erinnern kann ich mich nicht mehr.. Evtl. ist die FP10 Variante aber eh die performantere und saubere. Werde das später mal austesten. Danke auf jeden Fall! |
| | |
| | #12 (permalink) |
| [+] Registriert seit: Dec 2002 Ort: cologne
Beiträge: 2.271
|
Ein praktisches einfaches Beispiel anhand eines Metronomes. Code: package
{
import flash.display.Sprite;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.utils.ByteArray;
import flash.utils.setTimeout;
/**
* @author aM
*/
public class Metronome extends Sprite
{
static public const BLOCK_SIZE: int = 2048;
private var _sound: Sound;
private var _mPosition: int;
private var _mDuration: Number;
private var _mIndex: int;
private var _mPhase: Number;
private var _mDecay: Number;
public function Metronome()
{
setTimeout( _init, 100 );
}
private function _init(): void
{
var tempo: Number = 130.0;
_mDuration = ( .25 * 10584000.0 ) / tempo; // QUARTER NOTE LENGTH
_mPosition = _mDuration;
_mIndex = 0;
_mDecay = 1.0;
_mPhase = 0.0;
_sound = new Sound();
_sound.addEventListener( SampleDataEvent.SAMPLE_DATA, sampleData );
_sound.play();
}
private function sampleData( event: SampleDataEvent ): void
{
var bytes: ByteArray = event.data;
var amplitude: Number;
for( var i: int = 0 ; i < BLOCK_SIZE ; ++i )
{
amplitude = Math.sin( _mPhase * Math.PI * 2 ) * _mDecay * .3;
if( _mIndex == 0 )
_mPhase += 1360 / 44100;
else
_mPhase += 880 / 44100;
bytes.writeFloat( amplitude );
bytes.writeFloat( amplitude );
if( --_mPosition <= 0 )
{
if( ++_mIndex == 4 )
_mIndex = 0;
_mPhase = 0.0;
_mPosition = _mDuration;
_mDecay = 1.0;
}
else
{
_mDecay *= .9992;
}
}
}
}
} |
| | |
![]() |
| Lesezeichen |
| Stichworte |
| beat, sequencer, sound, takt, timing |
| Themen-Optionen | |
| Ansicht | |
| |