Sona 0.50 Source

Sona 0.50/tools/tfi2spat/sonapatch.c

#include <stdio.h>
#include "main.h"
#include "sonapatch.h"

// File format
enum {
   // Algorithm/feedback
   // (register $B0)
   SPAT_ALGO_FEED,
   
   // All slot data
   // (registers $30 to $9C)
   SPAT_S1_MUL_DT,   SPAT_S3_MUL_DT,   SPAT_S2_MUL_DT,   SPAT_S4_MUL_DT,
   SPAT_S1_TL,       SPAT_S3_TL,       SPAT_S2_TL,       SPAT_S4_TL,
   SPAT_S1_AR_RS,    SPAT_S3_AR_RS,    SPAT_S2_AR_RS,    SPAT_S4_AR_RS,
   SPAT_S1_DR_AMON,  SPAT_S3_DR_AMON,  SPAT_S2_DR_AMON,  SPAT_S4_DR_AMON,
   SPAT_S1_SR,       SPAT_S3_SR,       SPAT_S2_SR,       SPAT_S4_SR,
   SPAT_S1_RR_SL,    SPAT_S3_RR_SL,    SPAT_S2_RR_SL,    SPAT_S4_RR_SL,
   SPAT_S1_SSGEG,    SPAT_S3_SSGEG,    SPAT_S2_SSGEG,    SPAT_S4_SSGEG,
   
   // Unused yet, must be zero
   SPAT_RESERVED_1,
   SPAT_RESERVED_2,
   SPAT_RESERVED_3,
   
   // Size of the whole file
   SIZE_SPAT
};

//***************************************************************************
// save_sonapatch
// Takes in the loaded FM patch and generates a SonaPatch file out of it.
//---------------------------------------------------------------------------
// param filename ... file name to save into
// param wave ....... pointer to patch data
//---------------------------------------------------------------------------
// return ........... 0 on success, -1 on failure
//***************************************************************************

int save_sonapatch(const char *filename, const Patch *patch)
{
   // Where we store the data as it's being built
   // We later write it all to the file in one go
   uint8_t blob[SIZE_SPAT] = { 0 };
   
   // YM2612 (and SonaPatch) want detune in a sign+magnitude format, but
   // we get it in a plus-3 format instead, so we use this look-up table
   // to convert from the latter to the former
   static const uint8_t detune_table[] = { 7, 6, 5, 0, 1, 2, 3 };
   
   // Generate blob out of the patch data
   blob[SPAT_ALGO_FEED] = patch->algorithm |
                          patch->feedback << 3;
   
   blob[SPAT_S1_MUL_DT] = patch->slot[0].multiplier |
                          detune_table[patch->slot[0].detune] << 4;
   blob[SPAT_S3_MUL_DT] = patch->slot[1].multiplier |
                          detune_table[patch->slot[1].detune] << 4;
   blob[SPAT_S2_MUL_DT] = patch->slot[2].multiplier |
                          detune_table[patch->slot[2].detune] << 4;
   blob[SPAT_S4_MUL_DT] = patch->slot[3].multiplier |
                          detune_table[patch->slot[3].detune] << 4;
   
   blob[SPAT_S1_TL] = patch->slot[0].total_level;
   blob[SPAT_S3_TL] = patch->slot[1].total_level;
   blob[SPAT_S2_TL] = patch->slot[2].total_level;
   blob[SPAT_S4_TL] = patch->slot[3].total_level;
   
   blob[SPAT_S1_AR_RS] = patch->slot[0].attack_rate |
                         patch->slot[0].rate_scaling << 6;
   blob[SPAT_S3_AR_RS] = patch->slot[1].attack_rate |
                         patch->slot[1].rate_scaling << 6;
   blob[SPAT_S2_AR_RS] = patch->slot[2].attack_rate |
                         patch->slot[2].rate_scaling << 6;
   blob[SPAT_S4_AR_RS] = patch->slot[3].attack_rate |
                         patch->slot[3].rate_scaling << 6;
   
   blob[SPAT_S1_DR_AMON] = patch->slot[0].decay_rate |
                           patch->slot[0].am_enable << 7;
   blob[SPAT_S3_DR_AMON] = patch->slot[1].decay_rate |
                           patch->slot[1].am_enable << 7;
   blob[SPAT_S2_DR_AMON] = patch->slot[2].decay_rate |
                           patch->slot[2].am_enable << 7;
   blob[SPAT_S4_DR_AMON] = patch->slot[3].decay_rate |
                           patch->slot[3].am_enable << 7;
   
   blob[SPAT_S1_SR] = patch->slot[0].sustain_rate;
   blob[SPAT_S3_SR] = patch->slot[1].sustain_rate;
   blob[SPAT_S2_SR] = patch->slot[2].sustain_rate;
   blob[SPAT_S4_SR] = patch->slot[3].sustain_rate;
   
   blob[SPAT_S1_RR_SL] = patch->slot[0].release_rate |
                         patch->slot[0].sustain_level << 4;
   blob[SPAT_S3_RR_SL] = patch->slot[1].release_rate |
                         patch->slot[1].sustain_level << 4;
   blob[SPAT_S2_RR_SL] = patch->slot[2].release_rate |
                         patch->slot[2].sustain_level << 4;
   blob[SPAT_S4_RR_SL] = patch->slot[3].release_rate |
                         patch->slot[3].sustain_level << 4;
   
   blob[SPAT_S1_SSGEG] = patch->slot[0].ssg_eg;
   blob[SPAT_S3_SSGEG] = patch->slot[1].ssg_eg;
   blob[SPAT_S2_SSGEG] = patch->slot[2].ssg_eg;
   blob[SPAT_S4_SSGEG] = patch->slot[3].ssg_eg;
   
   // Write the entire blob into the specified file
   FILE *file = fopen(filename, "wb");
   if (file == NULL) {
      fprintf(stderr, "Error[\"%s\"]: can't create SonaPatch file\n",
                      filename);
      return -1;
   }
   
   if (fwrite(&blob, 1, SIZE_SPAT, file) < SIZE_SPAT) {
      fprintf(stderr, "Error[\"%s\"]: problem writing to SonaPatch file\n",
                      filename);
      fclose(file);
      return -1;
   }
   
   fclose(file);
   return 0;
}