Wie kann man einen Abschnitt aus einem Song richtig schleifen?

8

Ich programmiere eine kleine Music Engine für mein Spiel in C # und XNA, und ein Aspekt davon ist die Möglichkeit, einen Abschnitt aus einem Song zu schleifen. Zum Beispiel hat mein Lied ein Intropart und wenn das Lied das Ende (oder einen anderen bestimmten Punkt) erreicht hat, springt es zurück, wo das Intropart gerade vorbei ist. (A - B - B - B ...)

Jetzt verwende ich IrrKlank, das perfekt und ohne Lücken funktioniert, aber ich habe ein Problem:

Der Punkt, an dem zurückgesprungen werden soll, ist etwas ungenau. Hier ist ein Beispielcode:

public bool Passed(float time)
    {
        if ( PlayPosition >= time )
            return true;
        return false;
    }
//somewhere else
if( song.Passed( 10.0f ) )
   song.JumpTo( 5.0f );

Das Problem ist nun, dass der Song die 10 Sekunden durchläuft, aber einige Millisekunden bis etwa 10.1f spielt und dann auf 5 Sekunden springt. Es ist nicht so dramatisch, aber sehr falsch für meine Bedürfnisse. Ich habe versucht, es so zu beheben:

public bool Passed( float time )
{
      if( PlayPosition + 3 * dt >= time && PlayPosition <= time )
             return true;
      return false;
}

(dt ist die Deltazeit, die seit dem letzten Frame verstrichene Zeit)

Aber ich denke nicht, das ist eine gute Lösung dafür.

Ich hoffe, Sie können mein Problem (und mein Englisch, yay / o /) verstehen und mir helfen :)

Teflo
quelle
Können Sie wählen, ob Sie den Sound streamen oder alles auf einmal laden möchten?
Tesselode
Ich denke es ist möglich. Im Moment lade ich das komplette Lied, weil ich dachte, es wäre schneller, im Lied herumzuspringen. Ich versuche Multithreading, um dieses Problem zu lösen, aber ich habe immer noch Pech: /
Teflo
Versuchen Sie stattdessen, den Sound zu streamen. Schließlich sollte es fast sofort möglich sein, einen kleinen Teil des Songs zu laden.
Tesselode
Wie ist dein Lied codiert?
michael.bartnett
Anstatt "übergeben" zu verwenden, können Sie möglicherweise Fragen (wie Ereignisse) hinzufügen, die eine Rückruffunktion ausführen. Ich weiß nicht, wie Sie Ihre Zeit verwalten. Aber könnten Sie nicht die Anzahl der bisher gelesenen Bytes verwenden und sie mit der Gesamtmenge der Bytes vergleichen und in eine genaue Zeit übersetzen? Sie sollten in der Lage sein zu berechnen, wie viele Daten in einer Sekunde gelesen werden. Ich weiß allerdings nicht, wie schnell das gehen würde. Aber vielleicht könnten Sie dann die Zeit genauer ablesen.
Sidar

Antworten:

3

Meiner Meinung nach besteht eine einfachere und üblichere Lösung darin, zwei verschiedene Tracks zu haben: einen Intro- und einen Loop-Abschnitt. Dann besteht das einzige Problem darin, zu erkennen, wann das Intro endet (recht einfach, wenn auch nicht genau, wenn 30 fps verwendet werden). Dann kann der zweite Titel mit aktiviertem Looping gespielt werden.

Auf diese Weise reduzieren Sie die Fehler bei der Wiedergabe auf eine geringfügige Verzögerung, wenn Sie die Schleife zum ersten Mal starten, anstatt jedes Mal, wenn der Schleifenabschnitt zurückgespult werden muss.

Elideb
quelle
0

Die ideale Lösung wäre, vorher zu wissen, welche Teile des Songs als nächstes in den Puffer geladen werden müssen. Jeder 'Abschnitt' A - B - C hätte eine Start- und Endzeit, und es wäre Ihr untergeordnetes Streaming-System, das die richtigen Teile der Datei in der richtigen Reihenfolge lädt. Sie müssten jedoch eine feste Bitrate haben, damit dies funktioniert, und es könnte ziemlich schwierig sein, herauszufinden, wo in der Datei Sie als nächstes springen müssen.

Ich habe mir die irrKlang-Site kurz angesehen und sie rühmen sich der Möglichkeit, eigene Lesegeräte / Decoder im Dateiformat zu schreiben, um die Engine damit zu erweitern. Vielleicht möchten Sie das ausprobieren.

Es gibt auch ein Tutorial hier den Dateizugriff außer Kraft zu setzen - Sie meine ursprüngliche Idee , einen Schuss geben könnte, oder man könnte es mit mehreren Sounds mischen wie Elideb sagte. Sie können Ihre CMyReadFile-Klasse erweitern, um Zugriff auf mehrere Dateien (wie PartA.wav, PartB.wav, PartC.wav) zu erhalten, und Sie können die Lesemethode überschreiben, anstatt nur:

return (s32)fread(buffer, 1, sizeToRead, File);

Sie könnten so etwas tun wie:

switch(CurrentSectionToPlay)
{
case A:
    return (s32)fread(buffer, 1, sizeToRead, FileA);
case B:
    return (s32)fread(buffer, 1, sizeToRead, FileB);
}

Dies bedeutet, dass der Song nahtlos den Abschnitt wechseln würde, ohne Plays & Stops synchronisieren zu müssen.

Jonathan Connell
quelle