SSD Beispielprogramm 1 - C

Copyright 2007 by Thomas Becker, D-49080 Osnabrück.
All rights reserved.

Inhalt

Dieses Beipiel zeigt, wie der Inhalt einer kompletten TAK-Datei sequentiell dekodiert und in eine neu erstellte Wavedatei geschrieben wird.

Wichtiger Hinweis

Das Beispiel soll vornehmlich den Einsatz der relevanten SDK-Funktionen illustrieren. Deshalb wurde der Code auf das nötigste reduziert. Er enthält weder eine ordentliche Fehlerbehandlung noch einen anständigen Resourcenschutz. Ferner sind einige Hilfsfunktionen (z.B. das Schreiben in die Wavedatei) nur Platzhalter ohne Implementierung.

Hilfsfunktionen

Teilweise Platzhalterfunktionen ohne Implementierung.

void Proto (const char * ALine)

Schreibt die Textzeile ALine auf den Bildschirm oder in eine Datei.

void Error (const char * AWhat)

{
  Proto (AWhat);
  abort ();  /* Programmausführung beenden. */
}

Gibt die Fehlermeldung AWhat aus und beendet die Programmausführung.

void DecoError (TtakSeekableStreamDecoder ADecoder)
{
  char ErrorMsg[tak_ErrorStringSizeMax];

  tak_SSD_GetErrorString (tak_SSD_State (ADecoder), ErrorMsg, tak_ErrorStringSizeMax);
  Error (ErrorMsg);
}

Wird bei jedem Fehler mit dem betroffenen Decoderobjekt als Parameter aufgerufen und tut folgendes:

void WriteToWave (void *    ABuf,
                  TtakInt32 AByteNum)

Schreibt AByteNum Bytes aus ABuf in die Wavedatei.

void CreateWaveHeader (const Ttak_str_StreamInfo * AStreamInfo)

Erzeugt einen Waveheader gemäß dem Audioformat und der Sampleanzahl, die AStreamInfo spezifiziert, und schreibt ihn in die Wavedatei.

Rahmenprogramm

Anmerkung: Die folgende Prozedur ruft einige Funktionen auf, die in den anschließenden Abschnitten detailiert besprochen werden.

void DecodeFile ()
{
  TtakSSDOptions                Options;
  TtakSeekableStreamDecoder     Decoder;
  Ttak_str_StreamInfo           StreamInfo;
  Ttak_str_SimpleWaveDataHeader WaveMetaDesc;
  char *                        WaveMetaBuf;

  Options.Cpu   = tak_Cpu_Any;
  Options.Flags = tak_ssd_opt_SequentialRead;

  Decoder = tak_SSD_Create_FromFile ("d:\YourFilePath.tak", &Options, NULL, NULL);
  if (Decoder != NULL)
  {
    if (tak_SSD_Valid (Decoder) == tak_True)
    {
      if (tak_SSD_GetStreamInfo (ADecoder, &StreamInfo) != tak_res_Ok) 
        DecoError (Decoder);
          /* Kann eigentlich nie passieren, wenn der Decoder nach der
             Erzeugung gültig war...
          */
      ReadWaveMetaData (Decoder, &WaveMetaDesc, &WaveMetaBuf);
      if (WaveMetaBuf != NULL)
        WriteWaveMetaDataHead (&WaveMetaDesc, WaveMetaBuf);
          /* Wenn WaveMetaBuf != NULL, dann wurden die Wavemetaten
             erfolgreich gelesen. 
          */
      else
        CreateWaveHeader (&StreamInfo);
          /* Keine Wavemetadaten verfügbar, wir müssen selbst einen passenden
             Waveheader erzeugen. 
          */
      DecodeAudioData (&StreamInfo);
      if (WaveMetaBuf != NULL)
        {
        WriteWaveMetaDataTail (&WaveMetaDesc, WaveMetaBuf);
        DestroyWaveMetaData (&WaveMetaDesc, &WaveMetaBuf);
        }
    }  
    else
      DecoError (Decoder);
    tak_SSD_Destroy (Decoder);
  }
  else
    Proto ("System error: Unable to create Decoder!"); 
}

Zunächste werden die Dekoderoptionen festgelegt. Das Feld Cpu sollte immer mit tak_Cpu_Any belegt werden, damit der Decoder alle verfügbaren Cpu-Features zur Optimierung einsetzt. Nicht verfügbare Features (wie z.B. SSE) werden automatisch deaktiviert.

Das Feld Flags enthält eine Kombination von Optionskonstanten. tak_ssd_opt_SequentialRead bietet maximale Performance, wenn die Datei ausschließlich sequentiell gelesen wird.

Standardmäßig werden die Audiodaten beschädigter Frames durch Stille ersetzt. Sollen sie stattdessen ersatzlos entfernt werden, ist zusätzlich das Flag tak_ssd_opt_SkipDamagedFrames anzugeben:

  Options.Flags = tak_ssd_opt_SequentialRead | tak_ssd_opt_SkipDamagedFrames;

Es wird empfohlen, dem Anwender der Software eine entsprechende Option anzubieten.

Wichtige Hinweise:

Das Ergebnis des Decoderkonstructors tak_SSD_Create_FromFile ist nur dann NULL, wenn ein gravierender Systemfehler aufgetreten ist (z.B. ein OutOfMemory). Nach erfolgreicher Konstruktion ist deshalb unbedingt zusätzlich mittels tak_SSD_Valid zu prüfen, ob ein Decoderfehler vorliegt, z.B. ein Fehler beim Öffnen der zu dekodierenden Ddatei.

tak_SSD_GetStreamInfo liest die StreamInfo, die eine Reihe nützlicher Informationen enthält, die später noch benötigt werden.

Nach Abschluß der Dekodierung zerstört tak_SSD_Destroy den Dekoder.

Wavemetadaten lesen

Standardmäßig enthalten TAK-Dateien im Metadatenbereich den Header der ursprünglichen Wavedatei und ggfs. an die Audiodaten angehängte Metadaten. Beides soll nun gelesen werden.

void DestroyWaveMetaData (const Ttak_str_SimpleWaveDataHeader * ADesc,
                                char **                         ABuf)
  /* Wenn ABuf ungleich NULL ist, wird der von ihm belegte Speicher
    entsprechend den Größenangaben in ADesc freigegeben und ABuf
    dann auf NULL gesetzt.
  */
  
{
  if (*ABuf != NULL)
  {
    free (*ABuf);
    *ABuf = NULL;
  }
}


void ReadWaveMetaData (TtakSeekableStreamDecoder       ADecoder,
                       Ttak_str_SimpleWaveDataHeader * ADesc,
                       char **                         ABuf)
  /* Liest die Wavemetadatenbeschreibung in ADesc und die Metadaten
     selbst in ABuf. Bei Erfolg ist ABuf nach der Rückkehr ungleich
     NULL.
	
     ABuf muß ggfs. später durch Aufruf von DestroyWaveMetaData
     freigegeben werden. 
  */

{   
  *ABuf = NULL;	  
  if (tak_SSD_GetSimpleWaveDataDesc (ADecoder, ADesc) == tak_res_Ok)
  {
    /* Die Beschreibung der Wavemetadaten wurde erfolgreich gelesen.
    */
	 
    *ABuf = malloc (ADesc->HeadSize + ADesc->TailSize);
      /* Speicher für die Metadaten reservieren.
      */
	  
    if (tak_SSD_ReadSimpleWaveData (ADecoder, *ABuf,
                                    ADesc->HeadSize + ADesc->TailSize)
        != tak_res_Ok)
    {
      /* Das Lesen der Metadaten schlug fehl.
      */ 
      if (tak_SSD_Valid (ADecoder) != tak_True) 
         DecoError (ADecoder);
           /* Fataler Fehler!
           */ 
      else
        DestroyWaveMetaData (ADesc, ABuf);
          /* Wir können weitermachen, müssen aber später unseren eigenen Header
             erzeugen.
          */
    }	
  }	
  else
  {
    /* Die Beschreibung der Wavemetadaten konnte nicht gelesen werden.
    */ 
    if (tak_SSD_Valid (ADecoder) != tak_True) 
      DecoError (ADecoder);
        /* Ursache war ein fataler Fehler.
        */
  }
}

tak_SSD_GetSimpleWaveDataDesc liest die Beschreibung der Wavemetadaten aus dem Stream. Da diese optional sind, muß ein Scheitern keinen Fehler bedeuten. Bei Bedarf gibt das Funktionsergebnis Aufschluss über die Ursache des Scheiterns.

Die Metadatenbeschreibung ADesc enthält die Größen für führende (Header) und abschließende Metadaten. Die Summe beider Werte ergibt die erforderliche Größe des Puffers ABuf, in den die Metadaten mittels tak_SSD_ReadSimpleWaveData eingelesen werden.

Wavemetadaten schreiben

Die folgenden zwei Funktionen schreiben den Waveheader und ggfs. abschließende Metadaten in die Zieldatei:

void WriteWaveMetaDataHead (Ttak_str_SimpleWaveDataHeader * ADesc,
                            char *                          ABuf)
  /* Schreibt, falls vorhanden, den Waveheader aus ABuf in die Wavedatei.
  */
  
{
  if (ABuf != NULL)
    WriteToWave (ABuf, ADesc->HeadSize);
}


void WriteWaveMetaDataTail (Ttak_str_SimpleWaveDataHeader * ADesc,
                            char *                          ABuf);
  /* Schreibt, falls vorhanden, abschließende Metadaten aus ABuf in die
     Wavedatei.
  */

{
  if ((ABuf != NULL) && (ADesc->TailSize != 0))
    WriteToWave (&ABuf[ADesc->HeadSize], ADesc->TailSize);
}

Audiodaten dekodieren und ausgeben

Die Audiodaten werden sequentiell dekodiert und in die Wavedatei geschrieben.

void DecodeAudioData (Ttak_str_StreamInfo * AStreamInfo)
{
  TtakInt32  SamplesPerBuf;
  TtakInt32  SampleSize;
  TtakInt32  BufSize;
  char *     Audio;
  TtakInt32  ReadNum;
  TtakResult OpResult;

  SamplesPerBuf = AStreamInfo->Sizes.FrameSizeInSamples;
  SampleSize    = AStreamInfo->Audio.BlockSize;
    /* Frame- und Samplegröße.
    */
  BufSize = SamplesPerBuf * SampleSize;
    /* Genug Platz um einen dekodierten Frame aufzunehemen.
    */
  Audio = malloc (BufSize);
    /* Für optimale Performance sollte der Speicher auf ein ganzzahliges
      Vielfaches von 4 oder besser noch 8 ausgerichtet sein.
    */

AStreamInfo enthält eine Reihe nützlicher Informationen, die zwecks einfacherer Handhabung in lokale Variablen kopiert werden:

Wird ein Stream sequentiell dekodiert, läßt sich die größte Geschwindigkeit erzielen, wenn die Audiodaten frameweise gelesen werden. Das Produkt aus der Framegröße SamplesPerBuf und der Samplegröße SampleSize ergibt die passende Größe BufSize des Pufferspeichers Audio für die Leseoperationen.

Bei jedem Durchlauf der folgenden Schleife wird ein Frame dekodiert und in die Wavedatei geschrieben:

  ReadNum = 1;
  while (ReadNum > 0)
  {
    OpResult = tak_SSD_ReadAudio (Decoder, Audio, SamplesPerBuf, &ReadNum);
    if (   (OpResult != tak_res_Ok)
        && (OpResult != tak_res_ssd_FrameDamaged)) 
      DecoError (Decoder);
    if (ReadNum > 0)
      WriteToWave (Audio, ReadNum * SampleSize);
  }
  
  free (Audio);
}

Mit tak_SSD_ReadAudio werden maximal SamplesPerBuf Samples in den Puffer Audio gelesen. ReadNum enthält die Anzahl tatsächlich gelesener Samples und kann nur beim Lesen des letzten Frames am Dateiende kleiner als SamplesPerBuf werden.

Das Leseergebnis tak_res_ssd_FrameDamaged zeigt an, daß beschädigte Audiodaten übersprungen bzw. durch Stille ersetzt wurden. Der Decoder kann nach einem solchen Fehler problemlos weiterarbeiten; es ist die Entscheidung des Programmierers, ob die Dekodierung beim Auftreten beschädigter Frames abgebrochen werden soll. Das Beispiel ignoriert sie.

Es ist nicht nötig, Meldungen über beschädigte Frames festzuhalten. Ein Aufruf von tak_SSD_GetStateInfo liefert jederzeit ausführliche Statusinformationen incl. Beschädigungen.