Frage

Bearbeiten 2 + Antwort

Es stellte sich heraus, dass ich dazwischen wickeln musste 0 und Frequency*2*Math.pi.

Jeder, der veröffentlicht wurde, hat dazu beigetragen, dieses Problem herauszufinden. Da der Gast den niedrigsten Ruf hatte, markierte ich nur seinen Beitrag als Antwort. Vielen Dank an alle, das hat mich verrückt gemacht!

Bearbeiten 1

Hier ist meine WrapValue -Methode, die sich vorstellen müssen, dies vorher zu veröffentlichen. Es ist nicht so raffiniert wie Chris Taylors, aber es hat den gleichen Effekt auf mein Ende.

public static double WrapValue(double value, double min, double max)
{
    if (value > max)
        return (value - max) + min;
    if (value < min)
        return max - (min - value);
    return value;
}

Dies mag für Gamedev geeignet sein, aber es ist weniger game-y- und mehr Code-und-Math-y, also habe ich es hier gestellt.

Ich versuche, meine Xbox mit der neuen XNA 4.0 -Dynamicsoundectinstance -Klasse in ein digitales Instrument zu verwandeln, und ich erhalte jede Sekunde. Ich habe festgestellt, dass dies durch jeden Versuch verursacht wird, meinen Domänenwert zwischen 0 und 2*pi zu wickeln.

Ich schrieb eine kleine Klasse namens SineGenerator, die nur eine dynamicsoundeffectinstance beibehält und mit dem mit ihm erzeugten Puffer füttert Math.Sin().

Da ich präzise sein und die Abtastrate von 44.1 oder 48K verwenden möchte, behalte ich a double x (Der Winkel, den ich füttere Math.Sin()) und ein double step wo step ist 2 * Math.PI / SAMPLING_FREQUENCY. Jedes Mal, wenn ich Daten für dynamicsoundeffectinstance generiere. x durch step und hinzufügen sin(frequency * x) zu meinem Beispielpuffer (verkürzt zu a short da XNA nur 16 -Bit -Probentiefe unterstützt).

Ich denke, ich würde den Winkel zwischen 0 und 2*pi besser einwickeln, damit ich keine Präzision für x wie es groß wird. Dies führt jedoch den Klick vor. Ich schrieb meine eigene double WrapValue(double val, double min, double max) Methode falls mathhelper.wrapangle () verrückt war. Weder ein Wickeln zwischen math.pi und -math.pi noch 0 und 2*math.pi werden das Klicken los. Wenn ich mich jedoch nicht darum kümmere, den Wert zu wickeln und ihn einfach wachsen zu lassen, verschwindet das Klicken.

Ich denke, es hat etwas mit der Genauigkeit der .NET -Trig -Funktionen zu tun, wie Sünde (0)! = Sin (2*pi), aber ich weiß nicht genug, um zu beurteilen.

Meine Frage: Warum passiert das und sollte ich mir sogar die Mühe machen, den Winkel zu wickeln?

Der Code:

using System;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;

namespace TestDynAudio
{
    class SineGenerator
    {
  // Sample rate and sample depth variables
  private const int BUFFER_SAMPLE_CAPACITY = 1024;
  private const int BIT_RATE = 16;
  private const int SAMPLING_FREQUENCY = 48000;
  private readonly int BYTES_PER_SAMPLE;
  private readonly int SAMPLE_BUFFER_SIZE;

        private DynamicSoundEffectInstance dynSound = new DynamicSoundEffectInstance(SAMPLING_FREQUENCY, AudioChannels.Mono);
  private double x = 0; // The domain or angle value
  private double step;  // 48k / 2pi, increment x by this for each sample generated
  private byte[] sampleData; // The sample buffer
  private double volume = 1.0f; // Volume scale value

  // Property for volume
  public double Volume
  {
   get { return volume; }
   set { if (value <= 1.0 && value >= 0.0) volume = value; }
  }

  // Property for frequency
  public double Frequency { get; set; }

        public SineGenerator()
        {
   Frequency = 440; // Default pitch set to A above middle C
   step = Math.PI * 2 / SAMPLING_FREQUENCY;
   BYTES_PER_SAMPLE = BIT_RATE / 8;
   SAMPLE_BUFFER_SIZE = BUFFER_SAMPLE_CAPACITY * BYTES_PER_SAMPLE;
   sampleData = new byte[SAMPLE_BUFFER_SIZE];

   // Use the pull-method, DynamicSoundEffectInstance will
   // raise an event when more samples are needed
   dynSound.BufferNeeded += GenerateAudioData;
        }

  private void buildSampleData()
  {
   // Generate a sample with sin(frequency * domain),
   //   Convert the sample from a double to a short,
   //   Then write the bytes to the sample buffer
   for (int i = 0; i < BUFFER_SAMPLE_CAPACITY; i++)
   {
    BitConverter.GetBytes((short)((Math.Sin(Frequency * x) * (double)short.MaxValue) * volume)).CopyTo(sampleData, i * 2);

    // Simple value wrapper method that takes into account the
    // different between the min/max and the passed value
    x = MichaelMath.WrapValue(x + step, 0, 2f * (Single)Math.PI);
   }
  }

  // Delegate for DynamicSoundInstance, generates samples then submits them
  public void GenerateAudioData(Object sender, EventArgs args)
  {
   buildSampleData();
   dynSound.SubmitBuffer(sampleData);
  }

  // Preloads a 3 sample buffers then plays the DynamicSoundInstance
  public void play()
  {
   for (int i = 0; i < 3; i++)
   {
    buildSampleData();
    dynSound.SubmitBuffer(sampleData);
   }
   dynSound.Play();
  }

  public void stop()
  {
   dynSound.Stop();
  }
    }
}
War es hilfreich?

Lösung

Ich schätze . Versuchen Sie, die Frequenz*X zu wickeln, um den Pop loszuwerden.

Andere Tipps

Sie haben Ihre Wrap -Funktion nicht gezeigt, ich habe einen kurzen Test durchgeführt. Die folgenden wurden keine hörbaren Klicks angegeben.

Meine schnelle Wrap -Funktion

public static float Wrap(float value, float lower, float upper)
{
  float distance = upper - lower;
  float times = (float)System.Math.Floor((value - lower) / distance);

  return value - (times * distance);
}

So genannt

x = Wrap((float)(x + step), 0, 2 * (float)Math.PI);

Könnten Präzisionsfehler durch Umwandlung von Doppel in Schwimmkörper für Ihre Wrap -Funktion verursacht werden? Was ist der Zweck sowieso, da Sie ursprünglich Doppel verwenden und einen Doppelrücken erhalten? Sie führen doch 48.000 Conversions pro Sekunde durch und die Fehler würden sich aufbauen. Ihre Wrap -Funktion würde auch nicht funktionieren, wenn Xstep immer mehr als 2PI ist, aber ich sehe nicht, wie das passieren könnte ...

Wenn Sie aufhören, als Schwimmkörper zu kämpfen, behebt dies Ihr Problem nicht, ich empfehle Ihnen, eine X2 -Variable zu erstellen und einen Haltepunkt festzulegen, um zu sehen, warum die Werte unterschiedlich sind:

// declarations
private double x2 = 0;
private byte[] sampleData2 = new byte[BUFFER_SAMPLE_CAPACITY * BYTES_PER_SAMPLE];

// replace your for loop with this and set a breakpoint on
// the line with Console.WriteLine()
for (int i = 0; i < BUFFER_SAMPLE_CAPACITY; i++)
{
    BitConverter.GetBytes((short)((Math.Sin(Frequency * x) * (double)short.MaxValue) * volume)).CopyTo(sampleData, i * 2);
    BitConverter.GetBytes((short)((Math.Sin(Frequency * x2) * (double)short.MaxValue) * volume)).CopyTo(sampleData2, i * 2);

    if (sampleData[i * 2] != sampleData2[i * 2] || sampleData[i * 2 + 1] != sampleData2[i * 2 + 1]) {
        Console.WriteLine("DIFFERENT VALUES!");
    }

    // Simple value wrapper method that takes into account the
    // different between the min/max and the passed value
    x = MichaelMath.WrapValue(x + step, 0, 2f * (Single)Math.PI);
    x2 = x2 + step;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top