Dieser Artikel wurde maschinell übersetzt.

Der DirectX-Faktor

Simulieren eines analogen Synthesizers

Charles Petzold

Charles PetzoldVor 50 Jahren, erstellt ein Physiker und Ingenieur namens Robert Moog einen Elektronische Musik-Synthesizer mit einer eher ungewöhnlichen Funktion: eine Orgel-Tastatur.Manche Komponisten elektronischer Musik verunglimpft solches prosaisch und altmodische Kontrolle Gerät, während andere Komponisten — und vor allem Künstler — begrüßt diese Entwicklung.Bis Ende der 1960er Jahre Wendy Carlos Switched-On Bach war eines der meistverkauften klassischen Alben aller Zeiten geworden, und der Moog-Synthesizer den Mainstream getreten.

Die frühen Moog-Synthesizer waren modulare und programmierten mit Patch-Kabel.1970 Wurde jedoch der Minimoog veröffentlicht — klein, einfach zu bedienen und spielen und bei nur $1.495 Preis.(Eine gute Geschichte über diese frühen Synthesizer ist das Buch "analoge Tage: Die Erfindung und die Auswirkungen des Moog-Synthesizers"[Harvard University Press, 2004], durch Trevor Pinch und Frank Trocco.)

Wir klassifizieren die Moog und ähnliche Synthesizer als "analoge" Geräte, da sie erstellen Sounds mit unterschiedliche Spannungen erzeugt aus Schaltung aus Transistoren, Widerstände und Kondensatoren gebaut.Im Gegensatz dazu erstellen moderner "digital" Synthesizer sound durch algorithmische Berechnungen oder digitalisierten Proben.Ältere Geräte sind weiter als "subtraktive" Synthesizer klassifiziert: Statt Haus Gesamtklang durch die Kombination von Sinustönen (eine Technik namens Additiven Synthese), subtraktiven Synthesizer mit einer Wellenform, die reich an Oberwellen beginnen — wie ein Sägezahn oder quadratischen Welle — und führen Sie ihn dann durch Filter beseitigen einige Obertöne und verändern das Timbre des Klangs.

Eine entscheidende Konzept, die Pionierarbeit von Robert Moog war "Voltage Control." Sollten Sie einen Oszillator, der die Komponente eines Synthesizer ist, die eine grundlegende audio-Wellenform einer Art generiert.In früheren Synthesizer die Häufigkeit dieser Wellenform kann durch den Wert eines Widerstandes irgendwo in der Schaltung gesteuert werden, und diese Variablen Widerstand könnte ein Dial gesteuert werden.Aber ein spannungsgesteuerter Oszillator (VCO) ist die Frequenz des Oszillators einer Eingangsspannung geregelt.Beispielsweise kann jede Zunahme von einem Volt auf der Oszillator verdoppeln die Oszillatorfrequenz.Auf diese Weise kann die Frequenz des VCO durch eine Tastatur gesteuert werden, die eine Spannung erzeugt, die von einem Volt pro Oktave erhöht.

Bei analog-Synthesizer zeigt die Ausgabe von einem oder mehreren VCOs Voltage controlled Filter (VCF) zum Ändern des harmonischen Inhalts der Wellenform.Eingangsspannungen bis die VCF-Steuerelements den Filter-cutoff-Frequenz oder die Schärfe der Filter-Antwort (des Filters Qualität oder Q).Die Ausgabe der VCF, geht dann in ein spannungsgesteuerter Verstärker (VCA), der Gewinn davon durch eine andere Spannung gesteuert wird.

Hüllkurvengeneratoren

Aber sobald Sie anfangen, darüber reden VCFs und VCA, Dinge kompliziert, und ein wenig Hintergrundwissen ist notwendig.

Zurück im 19. Jahrhundert begannen einige Wissenschaftler (vor allem Hermann von Helmholtz) erhebliche Fortschritte in der Erforschung der physikalischen und Klangwahrnehmung.Merkmale der Klang wie Frequenz und Lautstärke erwies sich als relativ einfach im Vergleich mit dem verzwickten Problem der Klangfarbe — die Qualität des Tones, die uns erlaubt, eine Violine oder Posaune Klavier unterscheiden.Es wurde die Hypothese (und etwas gezeigt) Zusammenhang zwischen Klang und der Klang harmonisch Inhalt, ist den unterschiedlichen Grad der Intensität der Sinus-Kurven, die den Klang zu bilden.

Aber als Jahrhunderts Forscher weiter untersucht begann, entdeckt sie, dass es so einfach war.Harmonische Inhalt ändert sich im Laufe eines musikalischen Ton und dies trägt zu dem Instrument Klangfarbe.Insbesondere ist der Anfang einer Notiz von einem Musikinstrument entscheidend für auditive Wahrnehmung.Wenn ein Klavier Hammer oder Violine Bogen zuerst eine Zeichenfolge berührt oder vibrierende Luft in Holz od. Metall angetrieben wird, tritt sehr komplexe harmonische Aktivität.Diese Komplexität nimmt sehr schnell ab, aber ohne diese Töne klingen dumpf und weit weniger interessant und unverwechselbar.

Um die Komplexität der realen Töne zu imitieren, kann kein Synthesizer einfach eine Notiz ein- und ausschalten wie einen Schalter aktivieren.(Zu hören, was so ein einfacher Synthesizer, überprüfen Sie das ChromaticButtonKeyboard-Programm im Februar 2013-Rate dieser Spalte auf klingt wie msdn.microsoft.com/magazine/jj891059.) Zu Beginn jeder Note muss der Klang eine kurze "Blip" hohes Volumen und unterschiedlichem Klang vor zu stabilisieren.Wenn die Note endet, muss nicht einfach, aber sterben, mit einem Rückgang von Umfang und Komplexität angehalten werden.

Für Lautstärke gibt es ein allgemeine Muster für diesen Prozess: Für einen Hinweis auf eine Zeichenfolge, Messing oder Holzblasinstrument Instrument gespielt der Sound steigt auf eine maximale Lautstärke schnell, dann stirbt aus ein bisschen und hält verlässliche.Wenn die Note endet, nimmt es schnell in Band ab.Diese beiden Phasen sind bekannt als die "Attack" und "release".

Für weitere perkussive Instrumente — einschließlich Klavier — die Notiz erreicht maximalen Lautstärke schnell während des Angriffs aber dann stirbt ab langsam, wenn das Instrument beispielsweise ungedämpften, bleibt, während die Klaviertaste gedrückt.Sobald die Taste losgelassen wird, stirbt die Notiz schnell ab.

Um diese Effekte zu erzielen, implementieren Synthesizer so genannte eine "Hüllkurvengenerator." Abbildung 1 zeigt ein ziemlich standard Beispiel bezeichnet einen Angriff-Verfall-stützen-Mitteilung (ADSR)-Umschlag.Die horizontale Achse ist an der Zeit, und die vertikale Achse ist Lautheit.

An Attack-Decay-Sustain-Release Envelope
Abbildung 1 einen Angriff-Decay-stützen-Release-Umschlag

Wenn eine Taste auf der Tastatur gedrückt wird und der Hinweis beginnt zu klingen, Sie hören die Attack und Decay Abschnitten, die ein Platzen der Ton am Anfang zu geben und dann stabilisiert die Notiz auf der Sustain-Ebene.Wenn die Taste losgelassen wird und der Hinweis endet, tritt Abschnitt Freigabe.Für ein Klavier-Art-Sound könnte die Zerfallszeit ein paar Sekunden, und das Sustain-Niveau ist auf NULL gesetzt, so dass der Ton weiterhin zerfallen, solange die Taste gedrückt gehalten wird.

Selbst die einfachsten analog-Synthesizer haben zwei ADSR Umschläge: Man steuert die Lautstärke und die andere steuert den Filter.Dies ist normalerweise ein Tiefpass-Filter.Wie eine Note angeschlagen wird, die cutoff-Frequenz steigt schnell um weitere hochfrequente Oberwellen bis zu ermöglichen, und dann die cutoff-Frequenz verringert sich etwas.Viel betonte, entsteht den unverwechselbaren analogen-Synthesizer Zirpen Sound.

Das AnalogSynth-Projekt

Etwa neun Monaten als ich eine digitale Simulation eines kleinen 70er Jahre-Ära analog Synthesizer Programm mit XAudio2 zöge, bemerkte ich, dass die Hüllkurvengeneratoren einer der schwierigeren Aspekte des Auftrags wäre.Es war nicht einmal klar für mich, ob diese Hüllkurvengeneratoren außerhalb der Audio-Verarbeitung-Stream (und daher greifen auf die SetVolume und SetFilterParameters Methoden eine XAudio2-Stimme), oder irgendwie in den audio Stream errichtet werden.

Schließlich ließ ich mich auf die Umsetzung der Umschläge als XAudio2 Audioeffekte — mehr formal bekannt als Audio verarbeiten von Objekten (APOs).Dies bedeutet, dass die Umschlag-Logik direkt auf der audio-Stream funktioniert.Ich wurde zuversichtlicher mit diesem Ansatz nach Codierung Filter-Logik, die die digitale Biquad Filter dupliziert XAudio2 integriert.Mit meinen eigenen Filtercode, dachte ich, ich wäre in der Lage, den Filter-Algorithmus in Zukunft ohne größere Störungen in die Programmstruktur zu ändern.

Abbildung 2 zeigt den Bildschirm die resultierende analoge­Synth-Programm, dessen Quellcode Sie können download unter archive.msdn.microsoft.com/mag201307DXF.Obwohl ich durch die Anordnung der Steuerelemente auf der Minimoog beeinflusst wurde, ich die tatsächliche UI eher einfach gehalten, mit z.B. Reglern anstatt wählt.Mein Fokus wurde auf die Interna.

The AnalogSynth Screen
Abbildung 2 AnalogSynth-Bildschirm

Die Tastatur besteht aus einer Reihe von benutzerdefinierten Schlüssel-Steuerelementen, die Zeiger Ereignissen und gruppiert in Octave-Steuerelemente.Die Tastatur ist tatsächlich sechs Oktaven in der Breite und horizontal mit die Dicke graue Streifen unter den Tasten gescrollt werden kann.Ein roter Punkt identifiziert Middle c.

Das Programm kann 10 gleichzeitige Noten spielen, aber das ist mit einem einfachen #define in MainPage.xaml.cs änderbar.(Frühe analoge Synthesizern wie dem Minimoog waren monophon.) Jedes dieser 10 Stimmen ist eine Instanz einer Klasse, die ich rief SynthVoice.SynthVoice verfügt über Methoden zum Festlegen die verschiedenen Parameter der Stimme (einschließlich Frequenz, Lautstärke und Umschläge), sowie Methoden mit dem Namen Trigger und Release anzugeben, wenn eine Taste gedrückt oder freigegeben hat.

Der Minimoog erreicht seinen charakteristischen "druckvollen" Klang teilweise mit zwei Oszillatoren parallel laufen und oft leicht mistuned, entweder vorsätzlich oder die Frequenz driften häufig in analogen Schaltungen.

Aus diesem Grund erstellt jede SynthVoice zwei Instanzen einer Oszillator-Klasse, die von den oben links in der Systemsteuerung angezeigt, die kontrolliert werden Abbildung 2.Das Control Panel können Sie die Wellenform und die relative Lautstärke für diese zwei Oszillatoren festlegen, und Sie können die Häufigkeit transponieren, um ein oder zwei Oktaven nach oben oder unten.Darüber hinaus können Sie die Frequenz des zweiten Oszillators durch bis zu einer halben Oktave kompensiert.

Jeder Oszillator-Instanz erstellt ein IXAudio2SourceVoice-Objekt und macht Methoden mit dem Namen SetFrequency, SetAmplitude und SetWaveform.SynthVoice leitet die zwei IXAudio2SourceVoice-Ausgänge an ein IXAudio2SubmixVoice und dann instanziiert zwei benutzerdefinierten audio-Effekte, FilterEnvelopeEffect und Amplitude genannt­EnvelopeEffect, für die es, diese Submix-Stimme gilt.Diese beiden Effekte teilen eine Klasse namens EnvelopeGenerator, die ich kurz beschreiben werde.

Abbildung 3 zeigt die Organisation der Komponenten in jeder SynthVoice.Für die 10 SynthVoice-Objekte gibt es insgesamt 20 IXAudio2Source­Voice Instanzen gehen in 10 IXAudio2SubmixVoice-Instanzen, die dann an einem einzigen IXAudio2MasteringVoice weitergeleitet werden.Ich benutze eine Abtastrate von 48.000 Hz und 32-Bit Gleitkomma-Beispiele im gesamten.

The Structure of the SynthVoice Class
Abbildung 3 die Struktur der SynthVoice-Klasse

Der Benutzer steuert den Filter aus dem Mittelteil des Control Panels.Ein ToggleButton ermöglicht den Filter umgangen werden; Andernfalls ist die Cutoff-Frequenz relativ die Notiz, die gespielt wird.(Mit anderen Worten, verfolgt die Grenzfrequenz des Filters die Tastatur.) Die Emph­Asis-Regler steuert die Filtereinstellung Q.Der Umschlag-Schieberegler steuert den Grad, zu dem der Umschlag die Filter-cutoff-Frequenz beeinflusst.

Die vier Regler verbunden mit den Filter-Umschlag und der Lautstärke-Umschlag funktionieren ähnlich.Die Attack, Decay und Release-Regler sind alle Laufzeiten von 10 Millisekunden bis 10 Sekunden in einer logarithmischen Skala.Die Regler haben Tool-Tip-Wertkonverter die Dauer verbunden mit den Einstellungen angezeigt.

AnalogSynth macht keine Volumen-Anpassungen für die 20 möglichen gleichzeitigen IXAudio2SourceVoice Instanzen oder um die Tendenz der digitalen Biquad Filter Audio in der Nähe der cutoff-Frequenz verstärken entgegenzuwirken.Daher erleichtert AnalogSynth, die Audiodaten zu überladen.Damit den Benutzer dies zu vermeiden, verwendet das Programm die XAudio2­CreateVolumeMeter-Funktion, um einen Audioeffekt zu erstellen, die das ausgehende Geräusch überwacht.Ändert sich der grüne Punkt in der oberen rechten Ecke auf rot, Ausgang Audio wird abgeschnitten wird, und verwenden Sie den Schieberegler ganz nach rechts, um die Lautstärke zu verringern.

Frühe Synthesizer verwendet Patchkabel, um Komponenten zu verbinden.Infolge dieses Erbe bezeichnet man eine bestimmtes Synthesizer-Setup noch als ein "Patch". Wenn du einen Patch, der ein Geräusch macht, die Sie behalten möchten gefunden, drücken Sie die Schaltfläche "Speichern", und weisen Sie einen Namen.Drücken Sie die Load-Taste, um eine Liste der zuvor gespeicherten Patches und wählen Sie eine.Diese Patches (wie auch das aktuelle Setup) werden im Bereich lokale Einstellungen gespeichert.

Der Umschlag-Generator-Algorithmus

Code, der einen Hüllkurvengenerator implementiert ist im Grunde eine State-Machine mit fünf sequenzielle heißt es, dass ich Stille, Attack, Decay, Sustain und Release genannt.Aus Sicht der UI scheint es am natürlichsten, Attack, Decay, angeben und erhalten in Bezug auf Zeit Dura­gen, aber beim Ausführen der Berechnungen, die Sie, die zu einer Rate konvertieren müssen — eine Zunahme oder Abnahme der Lautheit (oder Filter-cutoff-Frequenz) pro Zeiteinheit.Die zwei audio-Effekte in AnalogSynth verwenden diesen wechselnden Ebenen um den Effekt zu implementieren.

Diese State-Machine ist nicht immer so wie das Diagramm in sequenziellen Abbildung 1 scheint zu implizieren.Zum Beispiel was passiert, wenn eine Taste gedrückt und so schnell, dass der Umschlag nicht noch Abschnitt Sustain erreicht hat, wenn die Taste losgelassen wird veröffentlicht?Am Anfang dachte ich der Umschlag darf zu füllen seinen Angriff und Verfall Abschnitte und gehen Sie dann rechts in die Release-Abschnitt, aber das funktionierte nicht gut für ein Klavier-Typ-Umschlag.In einem Klavier Umschlag Sustain ist 0 (null) und die Zerfallszeit ist relativ lang.Ein Schlüssel schnell gedrückt und wieder losgelassen hatte noch eine lange Verfall — als ob es überhaupt nicht veröffentlicht wurden!

Ich entschied, dass für eine schnelle drücken und loslassen, ich Abschnitt Angriff lasse abzuschließen, aber dann sofort direkt zum Abschnitt Freigabe.Dies bedeutete, dass die letzte Rate der Rückgang müssten berechnet basierend auf der aktuellen Ebene.Dies erklärt, warum es ein Unterschied gibt in der Behandlung der Veröffentlichung in der Struktur für die Umschlag-Parameter, hier gezeigt:

struct EnvelopeGeneratorParameters
{
  float baseLevel;
  float attackRate;   // in level/msec
  float peakLevel;
  float decayRate;    // in level/msec
  float sustainLevel;
  float releaseTime;  // in msec
};

Für die Amplitudenhüllkurve BaseLevel auf 0 festgelegt ist, PeakLevel auf 1 festgelegt ist und SustainLevel liegt irgendwo zwischen diesen Werten. Der Filter-Umschlag finden Sie die drei Ebenen ein Multiplikator auf die Filter-cutoff-Frequenz angewendet: BaseLevel ist 1, und PeakLevel richtet sich nach den Regler mit der Bezeichnung "Envelope" und kann zwischen 1 und 16. Diese Frequenz-Multiplikator von 16 entspricht vier Oktaven.

AmplitudeEnvelopeEffect und FilterEnvelopeEffect teilen die EnvelopeGenerator-Klasse. Abbildung 4 zeigt die EnvelopeGenerator-Headerdatei. Beachten Sie die öffentliche Methode die Umschlag-Parameter eingestellt und zwei öffentliche Methoden mit dem Namen Attack und Release, die auslösen den Umschlag zum beginnen und beenden. Diese drei Methoden sollten in dieser Reihenfolge aufgerufen werden. Der Code wird nicht in einen Umschlag beschäftigen, deren Parameter den Fortschritt zur Halbzeit ändern, geschrieben.

Abbildung 4 die EnvelopeGenerator-Headerdatei

class EnvelopeGenerator
{
private:
  enum class State
  {
    Dormant, Attack, Decay, Sustain, Release
  };
  EnvelopeGeneratorParameters params;
  float level;
  State state;
  bool isReleased;
  float releaseRate;
public:
  EnvelopeGenerator();
  void SetParameters(const EnvelopeGeneratorParameters params);
  void Attack();
  void Release();
  bool GetNextValue(float interval, float& value);
};

Der aktuelle berechnete Wert aus der Hüllkurvengenerator wird durch wiederholte Aufrufe von GetNextValue gewonnen. Die Intervall-akzeptables­Ment wird in Millisekunden angegeben, und die Methode berechnet basieren auf diesem Intervall möglicherweise wechseln Staaten dabei einen neuen Wert. Wenn der Umschlag mit Abschnitt freigegeben, GetNextValue, gibt true abgeschlossen wurde, zeigen Sie, dass der Umschlag ist abgeschlossen, aber ich nicht wirklich diesen Rückgabewert an anderer Stelle im Programm verwenden.

Abbildung 5 zeigt die Implementierung der EnvelopeGenerator-Klasse. Im oberen Bereich der GetNextValue ist die Methode der Code direkt in den Release-Status überspringen, wenn eine Taste losgelassen wird, und die Berechnung von einem Release Rate auf der aktuellen Ebene und die Schließzeit basierend.

Abbildung 5 die Umsetzung der EnvelopeGenerator

EnvelopeGenerator::EnvelopeGenerator() : state(State::Dormant)
{
  params.baseLevel = 0;
}
void EnvelopeGenerator::SetParameters(const EnvelopeGeneratorParameters params)
{
  this->params = params;
}
void EnvelopeGenerator::Attack()
{
  state = State::Attack;
  level = params.baseLevel;
  isReleased = false;
}
void EnvelopeGenerator::Release()
{
  isReleased = true;
}
bool EnvelopeGenerator::GetNextValue(float interval, float& value)
{
  bool completed = false;
  // If note is released, go directly to Release state,
  // except if still attacking
  if (isReleased &&
    (state == State::Decay || state == State::Sustain))
  {
    state = State::Release;
    releaseRate = (params.baseLevel - level) / params.releaseTime;
  }
  switch (state)
  {
  case State::Dormant:
    level = params.baseLevel;
    completed = true;
    break;
  case State::Attack:
    level += interval * params.attackRate;
    if ((params.attackRate > 0 && level >= params.peakLevel) ||
      (params.attackRate < 0 && level <= params.peakLevel))
    {
      level = params.peakLevel;
      state = State::Decay;
    }
    break;
  case State::Decay:
    level += interval * params.decayRate;
    if ((params.decayRate > 0 && level >= params.sustainLevel) ||
      (params.decayRate < 0 && level <= params.sustainLevel))
    {
      level = params.sustainLevel;
      state = State::Sustain;
    }
    break;
  case State::Sustain:
    break;
  case State::Release:
    level += interval * releaseRate;
    if ((releaseRate > 0 && level >= params.baseLevel) ||
      (releaseRate < 0 && level <= params.baseLevel))
    {
      level = params.baseLevel;
      state = State::Dormant;
      completed = true;
    }
  }
  value = level;
  return completed;
}

Ein paar Audio-Effekte

Sowohl die AmplitudeEnvelopeEffect als auch die FilterEnvelopeEffect Klassen, die von CXAPOParametersBase abgeleitet werden, damit sie Parameter akzeptieren, und beide Klassen auch eine Instanz der EnvelopeGenerator-Klasse pflegen für den Umschlag Berechnungen. Die Parameterstrukturen für diese beiden audio-Effekte sind AmplitudeEnvelopeParameters und FilterEnvelopeParameters benannt.

Die AmplitudeEnvelopeParameters-Struktur ist lediglich eine EnvelopeGeneratorParameters-Struktur und ein Boolean KeyPressed-Feld, das wahr ist, wenn diese Stimme zugeordnete Schlüssel ist gedrückt und false, wenn es freigegeben wird. (Der Filter­EnvelopeParameters Struktur ist nur ein wenig komplexer, denn es einer Basisebene Filter-cutoff-Frequenz und Q-Umgebung zu integrieren muss.) Beide Effekte-Klassen pflegen ihre eigenen KeyPressed Datenelemente, die gegenüber den Parameter-Wert zu bestimmen, wenn der Umschlag angreifen oder Release Zustand ausgelöst werden sollte.

Sie können sehen, wie das funktioniert, Abbildung 6, die zeigt den Code für die Prozess-Überschreibung in AmplitudeEnvelopeEffect. Wenn der Effekt aktiviert ist und der lokalen KeyPressed Wert false ist, aber der KeyPressed-Wert in der Effektparameter wahr ist, macht die Wirkung Aufrufe der Methoden SetParameters und Angriff der EnvelopeGenerator Instanz. Wenn das Gegenteil der Fall ist — der lokalen KeyPressed Wert ist true, aber in die Parameter ist falsch — dann die Wirkung die Release-Methode aufruft.

Abbildung 6 die Prozess-Überschreibung in AmplitudeEnvelopeEffect

void AmplitudeEnvelopeEffect::Process(UINT32 inpParamCount,
    const XAPO_PROCESS_BUFFER_PARAMETERS *pInpParam,
    UINT32 outParamCount,
    XAPO_PROCESS_BUFFER_PARAMETERS *pOutParam,
    BOOL isEnabled)
{
  // Get effect parameters
  AmplitudeEnvelopeParameters * pParams =
    reinterpret_cast<AmplitudeEnvelopeParameters *>
      (CXAPOParametersBase::BeginProcess());
  // Get buffer pointers and other information
  const float * pSrc = static_cast<float const*>(pInpParam[0].pBuffer);
  float * pDst = static_cast<float *>(pOutParam[0].pBuffer);
  int frameCount = pInpParam[0].ValidFrameCount;
  int numChannels = waveFormat.
nChannels;
  switch(pInpParam[0].BufferFlags)
  {
  case XAPO_BUFFER_VALID:
    if (!isEnabled)
    {
      for (int frame = 0; frame < frameCount; frame++)
      {
        for (int channel = 0; channel < numChannels; channel++)
        {
          int index = numChannels * frame + channel;
          pDst[index] = pSrc[index];
        }
      }
    }
    else
    {
      // Key being pressed
      if (!this->keyPressed && pParams->keyPressed)
      {
        this->keyPressed = true;
        this->envelopeGenerator.SetParameters(pParams->envelopeParams);
        this->envelopeGenerator.Attack();
      }
      // Key being released
      else if (this->keyPressed && !pParams->keyPressed)
      {
        this->keyPressed = false;
        this->envelopeGenerator.Release();
      }
      // Calculate interval in msec
      float interval = 1000.0f / waveFormat.
nSamplesPerSec;
      for (int frame = 0; frame < frameCount; frame++)
      {
        float volume;
        envelopeGenerator.GetNextValue(interval, volume);
        for (int channel = 0; channel < numChannels; channel++)
        {
          int index = numChannels * frame + channel;
          pDst[index] = volume * pSrc[index];
        }
      }
    }
    break;
  case XAPO_BUFFER_SILENT:
    break;
  }
  // Set output parameters
  pOutParam[0].ValidFrameCount = pInpParam[0].ValidFrameCount;
  pOutParam[0].BufferFlags = pInpParam[0].BufferFlags;
  CXAPOParametersBase::EndProcess();
}

Der Effekt könnte die GetNextValue-Methode des EnvelopeGenerator bezeichnen, die für jeden Prozess-Aufruf (in diesem Fall das Argument Interval 10 Millisekunden angeben würde) oder für jede Probe (in diesem Fall das Intervall ist eher 21 Mikrosekunden). Obwohl der erste Ansatz ausreichend sein sollte, entschied ich mich für die zweite für theoretisch glattere Übergänge.

Der mit doppelter Lautstärkewert zurückgegeben wird, aus der GetNextValue Anruf reicht von 0 (wenn eine Note ist erste begonnen oder beendet) für den Höhepunkt des Angriffs auf 1. Der Effekt multipliziert einfach die Gleitkomma-Beispiele unter folgender Nummer.

Jetzt der Spaß beginnt

Ich verbrachte so viel Zeit, die Codierung der analogen­Synth-Programm, das ich noch nicht viel Zeit hatte, mit ihm zu spielen, um. Es könnte sehr gut sein, dass einige der Steuerelemente und Parameter einige Feinabstimmung, oder vielleicht eher gröbere tuning brauchen! Insbesondere lange zerfallen und Release-Zeiten auf dem Volume nicht Recht klingen, und sie schlagen vor, daß die Umschlag-Änderungen an Amplituden logarithmische anstatt linear sein sollte.

Ich bin auch fasziniert durch den Einsatz von Touch-Eingaben mit der Bildschirmtastatur. Die Tasten auf ein richtiges Klavier reagieren empfindlich auf die Geschwindigkeit, mit der sie getroffen sind, und Synthesizer Tastaturen haben versucht, das gleiche Gefühl zu emulieren. Die meisten Touchscreens kann jedoch nicht berühren Geschwindigkeit oder Druck erkennen. Aber sie können empfindlich auf geringfügige Finger Bewegungen gemacht werden auf dem Bildschirm, die darüber hinaus die Möglichkeit, eine echte Tastatur ist. Können on-Screen Keyboard stärker auf diese Weise werden gemacht? Es gibt nur einen Weg herauszufinden!

Charles Petzold schreibt seit langem redaktionelle Beiträge für das MSDN Magazin und ist Autor von „Programming Windows, 6th edition“ (O’Reilly Media, 2012), einem Buch über das Schreiben von Anwendungen für Windows 8. Die Adresse seiner Website lautet charlespetzold.com.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: James McNellis (Microsoft)
James McNellis ist eine C++-Fan und ein Software-Entwickler, die Visual C++-Team bei Microsoft, wo er wo er baut C++-Bibliotheken und unterhält die C-Laufzeitbibliotheken (CRT).  Er tweets auf @JamesMcNellis, und kann man anderswo online über http://jamesmcnellis.com/.