SSD program example 1 - C

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

Content

This example shows how the content of a complete TAK file is sequentially decoded and written to a new Wave file.

Important Notice

The example is intended primarily to illustrate the use of the relevant SDK functions. Accordingly the code is stripped down to a minimum. It includes neither proper dealing with errors nor appropriate resource protection. Furthermore, some help functions (writing to a Wave file, for example) are only placeholders without any implementation.

Help functions

Partially placeholder functions without implementation.

void Proto (const char * ALine)

Writes the text line ALine to the screen or a file.

void Error (const char * AWhat)
{
  Proto (AWhat);
  abort ();  /* Stop program execution. */
}

Shows the error message AWhat and stops program execution.

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

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

This procedure is called at any error, supplied the affected decoder object as a parameter. It does the following:

void WriteToWave (void *    ABuf,
                  TtakInt32 AByteNum)

Writes AByteNum bytes out of ABuf to the Wave file.

void CreateWaveHeader (const Ttak_str_StreamInfo * AStreamInfo)

Creates a Wave header according to audio format and number of samples specified by AStreamInfo and writes it to the Wave file.

Main program

Note: This procedure calls a few functions explained in detail in the subsequent paragraphs.

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);
          /* Can virtually never happen if the decoder was valid after
             its construction...
          */
      ReadWaveMetaData (Decoder, &WaveMetaDesc, &WaveMetaBuf);
      if (WaveMetaBuf != NULL)
        WriteWaveMetaDataHead (&WaveMetaDesc, WaveMetaBuf);
          /* If WaveMetaBuf != NULL, the Wave metadata was
             read successfully. 
          */
      else
        CreateWaveHeader (&StreamInfo);
          /* No Wave metadata available, we have to create an appropriate
             Wave header. 
          */
      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!"); 
}

First the decoder options are defined. The Cpu field should always be set to tak_Cpu_Any for the decoder to use all available CPU features for optimizations. Features not available (such as SSE, for example) are deactivated automatically.

The Flags field contains a combination of option constants. tak_ssd_opt_SequentialRead gives maximum performance if sequential file reading is used exclusively.

By default, audio data of damaged frames is replaced with silence. If you want this data to be skipped, the tak_ssd_opt_SkipDamagedFrames flag is additionally required:

  Options.Flags = tak_ssd_opt_SequentialRead | tak_ssd_opt_SkipDamagedFrames;

It is recommended to offer an appropriate option to the end user of the software.

Important notes:

The result of the decoder constructor tak_SSD_Create_FromFile is only NULL if a serious system error occurred (such as OutOfMemory). Because of this, after a successful construction it is additionally required to use tak_SSD_Valid to test for decoder errors such as an error while opening the source file.

tak_SSD_GetStreamInfo reads the StreamInfo containing some useful information that is required later on.

After decoding is finished, tak_SSD_Destroy destroys the decoder construct.

Reading Wave metadata

By default, the metadata block of a TAK file contains the original Wave file header and - where applicable - metadata attached to the audio data. Both will be read now.

void DestroyWaveMetaData (const Ttak_str_SimpleWaveDataHeader * ADesc,
                                char **                         ABuf)
  /* If ABuf != NULL, the memory allocated by it is deallocated
     according to the size information in ADesc. ABuf
     is then set to NULL.
  */
  
{
  if (*ABuf != NULL)
  {
    free (*ABuf);
    *ABuf = NULL;
  }
}


void ReadWaveMetaData (TtakSeekableStreamDecoder       ADecoder,
                       Ttak_str_SimpleWaveDataHeader * ADesc,
                       char **                         ABuf)
  /* Reads the Wave metadata description in ADesc and the metadata itself
     in ABuf. Success results in ABuf != NULL after return.
	
     Possibly ABuf has to be deallocated later, using DestroyWaveMetaData. 
  */

{   
  *ABuf = NULL;	  
  if (tak_SSD_GetSimpleWaveDataDesc (ADecoder, ADesc) == tak_res_Ok)
  {
    /* The Wave metadata description was read successfully.
    */
	 
    *ABuf = malloc (ADesc->HeadSize + ADesc->TailSize);
      /* Allocate memory for metadata.
      */
	  
    if (tak_SSD_ReadSimpleWaveData (ADecoder, *ABuf,
                                    ADesc->HeadSize + ADesc->TailSize)
        != tak_res_Ok)
    {
      /* Reading metadata failed.
      */ 
      if (tak_SSD_Valid (ADecoder) != tak_True) 
         DecoError (ADecoder);
           /* Fatal error!
           */ 
      else
        DestroyWaveMetaData (ADesc, ABuf);
          /* We can continue, but we have to create our own header.
          */
    }	
  }	
  else
  {
    /* The Wave metadata description could not be read.
    */ 
    if (tak_SSD_Valid (ADecoder) != tak_True) 
      DecoError (ADecoder);
        /* The cause was a fatal error.
        */
  }
}

tak_SSD_GetSimpleWaveDataDesc reads the Wave metadata description from the stream. As this is optional, a failure not necessarily implies an error. If required, the function result sheds light on the reason for the failure.

The metadata description ADesc contains the sizes for leading (header) and trailing metadata. The sum of these is the required size of the buffer ABuf which the metadata is written to, using tak_SSD_ReadSimpleWaveData.

Writing Wave metadata

The following two procedures write the Wave header and, if required, trailing metadata to the destination file:

void WriteWaveMetaDataHead (Ttak_str_SimpleWaveDataHeader * ADesc,
                            char *                          ABuf)
  /* Writes the Wave header (if there is any) from ABuf to the Wave file.
  */
  
{
  if (ABuf != NULL)
    WriteToWave (ABuf, ADesc->HeadSize);
}


void WriteWaveMetaDataTail (Ttak_str_SimpleWaveDataHeader * ADesc,
                            char *                          ABuf);
  /* Writes trailing metadata (if there is any) from ABuf to the Wave file.
  */

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

Decoding and outputting audio data

The audio data is sequentially decoded and written to the Wave file.

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 / Sample size.
    */
  BufSize = SamplesPerBuf * SampleSize;
    /* Enough space to hold a decoded frame.
    */
  Audio = malloc (BufSize);
    /* For optimum performance, the memory should be aligned to an integer
       multiple of 4 or - even better - 8.
    */

AStreamInfo contains some useful information which is copied to local variables for easier handling:

If the stream is read sequentially, the highest speed can be achieved by reading the audio data frame by frame. The product of the frame size SamplesPerBuf and the sample size SampleSize is the appropriate size BufSize of the buffer memory Audio for read operations.

On every iteration of the following loop a frame is decoded and the resulting audio data is written to the Wave file:

  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);
}

tak_SSD_ReadAudio reads a maximum of SamplesPerBuf samples to the buffer Audio. ReadNum contains the number of samples actually read and can be less than SamplesPerBuf only when reading the last frame of the file.

The result tak_res_ssd_FrameDamaged indicates that damaged audio data was skipped or replaced with silence. The decoder can proceed without problems nonetheless; it is up to the programmer whether decoding is cancelled in case of damaged frames. The example ignores them.

It is not necessary to record messages regarding damaged frames. A tak_SSD_GetStateInfo call provides detailed status information at any time, including damages.