Sona 0.50 Source

Sona 0.50/tools/tfi2spat/tfi.c

#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "tfi.h"

#define TFI_SIZE        42             // Size of a TFI file
#define MAX_BLOBSIZE    (TFI_SIZE+1)   // How much to allocate to blob

// Macro used to scan through the blob
// It also checks that the value is between 0 and max
// If that isn't the case then it jumps to format_error
#define GET_BYTE(var, max) \
        temp = *ptr++; \
        if (temp > (max)) goto format_error; \
        var = temp;

//***************************************************************************
// load_tfi
// Parses and loads a TFI file into memory.
//---------------------------------------------------------------------------
// param filename ... file name to load from
//---------------------------------------------------------------------------
// return ........... pointer to FM patch (NULL on failure)
//***************************************************************************

Patch *load_tfi(const char *filename)
{
   // Read the entire TFI file into memory
   // We try to read a bit more ahead, this way we can tell for sure
   // if the filesize is correct (TFI files are *exactly* 42 bytes)
   uint8_t blob[MAX_BLOBSIZE];
   
   FILE *file = fopen(filename, "rb");
   if (file == NULL) {
      fprintf(stderr, "Error[\"%s\"]: can't open TFI file\n", filename);
      return NULL;
   }
   
   if (fread(&blob, 1, MAX_BLOBSIZE, file) != TFI_SIZE) {
      fprintf(stderr, feof(file) ?
                      "Error[\"%s\"]: not a valid TFI file\n" :
                      "Error[\"%s\"]: problem reading TFI file\n",
                      filename);
      return NULL;
   }
   
   fclose(file);
   
   // Allocate memory to store the patch data
   Patch *patch = malloc(sizeof(Patch));
   if (patch == NULL) {
      fprintf(stderr, "Error[\"%s\"]: out of memory!\n", filename);
      return NULL;
   }
   
   // Now we read through the blob in memory
   // These two variables are used by the GET_BYTE macro
   const uint8_t *ptr = blob;    // Pointer to next byte
   uint8_t temp;                 // Used for the comparison
   
   // Read common arguments
   GET_BYTE(patch->algorithm, 7);
   GET_BYTE(patch->feedback, 7);
   
   // Read all slots
   for (int i = 0; i < 4; i++) {
      GET_BYTE(patch->slot[i].multiplier, 15);
      GET_BYTE(patch->slot[i].detune, 6);
      GET_BYTE(patch->slot[i].total_level, 127);
      GET_BYTE(patch->slot[i].rate_scaling, 3);
      GET_BYTE(patch->slot[i].attack_rate, 31);
      GET_BYTE(patch->slot[i].decay_rate, 31);
      GET_BYTE(patch->slot[i].sustain_rate, 31);
      GET_BYTE(patch->slot[i].release_rate, 15);
      GET_BYTE(patch->slot[i].sustain_level, 15);
      GET_BYTE(patch->slot[i].ssg_eg, 15);
   }
   
   // TFI doesn't have AMS information, so fake it by setting the AMON bit
   // on output slots (which will make AMS act as tremolo instead of altering
   // the resulting sound)
   patch->slot[0].am_enable = (patch->algorithm == 7) ? 1 : 0;
   patch->slot[1].am_enable = (patch->algorithm >= 4) ? 1 : 0;
   patch->slot[2].am_enable = (patch->algorithm >= 5) ? 1 : 0;
   patch->slot[3].am_enable = 1;
   
   // We're done
   return patch;
   
format_error:
   // If we get here, it means the TFI file is not valid
   free(patch);
   fprintf(stderr, "Error[\"%s\"]: not a valid TFI file\n", filename);
   return NULL;
}