Hi All,
I asked Kyle Buza (of mmonoplayer) about adding the sources for his
console soundchip emulator externals to Pd-CVS so that they could be
included in Pd-Extended.  He's sending me the sources as he digs them
up.  Most of them are written for Max, but I was hoping that someone
would be interested in helping me try to convert those to Pd.

I don't have Dev access, so someone else will have to actually put
them in CVS, in say externals/mmonoplayer.

The first is attached.  This is of course already for Pd.

/*****************************************************************************/
/*                                                                           */
/* TIA Chip Sound Simulator for PD                                           */
/* Purpose: To emulate the sound generation hardware of the Atari TIA chip.  */
/* Author(s):  Kyle Buza, Ron Fries                                          */
/*                                                                           */
/*****************************************************************************/

#include <stdlib.h>
#include "m_pd.h"

static t_class *atari_2600_class;

typedef signed char Int8;
typedef signed int Int16;

#ifdef WIN32
#define int8  char
#define int16 short
#define int32 int
#else
#define int8  char
#define int16 int
#define int32 long
#endif

#define uint8  unsigned int8
#define uint16 unsigned int16
#define uint32 unsigned int32

/* definitions for AUDCx (15, 16) */
#define SET_TO_1     0x00      /* 0000 */
#define POLY4        0x01      /* 0001 */
#define DIV31_POLY4  0x02      /* 0010 */
#define POLY5_POLY4  0x03      /* 0011 */
#define PURE         0x04      /* 0100 */
#define PURE2        0x05      /* 0101 */
#define DIV31_PURE   0x06      /* 0110 */
#define POLY5_2      0x07      /* 0111 */
#define POLY9        0x08      /* 1000 */
#define POLY5        0x09      /* 1001 */
#define DIV31_POLY5  0x0a      /* 1010 */
#define POLY5_POLY5  0x0b      /* 1011 */
#define DIV3_PURE    0x0c      /* 1100 */
#define DIV3_PURE2   0x0d      /* 1101 */
#define DIV93_PURE   0x0e      /* 1110 */
#define DIV3_POLY5   0x0f      /* 1111 */

#define DIV3_MASK    0x0c

#define AUDC0        0x15
#define AUDC1        0x16
#define AUDF0        0x17
#define AUDF1        0x18
#define AUDV0        0x19
#define AUDV1        0x1a

/* the size (in entries) of the 4 polynomial tables */
#define POLY4_SIZE  0x000f
#define POLY5_SIZE  0x001f
#define POLY9_SIZE  0x01ff

/* channel definitions */
#define CHAN1       0
#define CHAN2       1

#define FALSE       0
#define TRUE        1

/* Initialze the bit patterns for the polynomials. */

/* The 4bit and 5bit patterns are the identical ones used in the tia chip. */
/* Though the patterns could be packed with 8 bits per byte, using only a */
/* single bit per byte keeps the math simple, which is important for */
/* efficient processing. */

static uint8 Bit4[POLY4_SIZE] =
     { 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 };

static uint8 Bit5[POLY5_SIZE] =
     { 0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1 };

/* I've treated the 'Div by 31' counter as another polynomial because of */
/* the way it operates.  It does not have a 50% duty cycle, but instead */
/* has a 13:18 ratio (of course, 13+18 = 31).  This could also be */
/* implemented by using counters. */

static uint8 Div31[POLY5_SIZE] =
     { 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 };

typedef struct _atari_2600
{
 t_object x_obj;
 t_float x15;
 t_float x16;
 t_float x17;
 t_float x18;
 t_float x19;
 t_float x1a;
 long Bit9[POLY9_SIZE];
 long P4[2];
 long P5[2];
 long P9[2];
 long Div_n_cnt[2];
 long Div_n_max[2];
 long AUDC[2];
 long AUDF[2];
 long AUDV[2];
 long Outvol[2];
 long volume;
} t_atari_2600;

void *atari_2600_new(void);
t_int *atari_2600_perform(t_int *w);
void atari_2600_dsp(t_atari_2600 *x, t_signal **sp, short *count);

void Tia_sound_init(t_atari_2600 *x, uint16 sample_freq, uint16 playback_freq);
void Update_tia_sound(t_atari_2600 *x, uint16 addr, uint8 val);
void Tia_process (t_atari_2600 *x, t_float *buffer, uint16 n);

void atari_2600_tilde_setup(void)
{
        atari_2600_class = class_new(gensym("atari_2600~"),
                        (t_newmethod)atari_2600_new, 0, sizeof(t_atari_2600),
CLASS_DEFAULT, A_DEFFLOAT, 0);
        class_addmethod(atari_2600_class, (t_method)atari_2600_dsp, 
gensym("dsp"), 0);
}

void *atari_2600_new(void)
{
 t_atari_2600 *x = (t_atari_2600 *)pd_new(atari_2600_class);
 x->x15 = 0;
 x->x16 = 0;
 x->x17 = 0;
 x->x18 = 0;
 x->x19 = 0;
 x->x1a = 0;
 floatinlet_new(&x->x_obj, &x->x15);
 floatinlet_new(&x->x_obj, &x->x17);
 floatinlet_new(&x->x_obj, &x->x19);
 floatinlet_new(&x->x_obj, &x->x16);
 floatinlet_new(&x->x_obj, &x->x18);
 floatinlet_new(&x->x_obj, &x->x1a);
 outlet_new(&x->x_obj, &s_signal);

 Tia_sound_init(x, 32000, 32000);
 return (x);
}

void atari_2600_dsp(t_atari_2600 *x, t_signal **sp, short *count)
{
 dsp_add(atari_2600_perform, 3, sp[0]->s_vec, sp[0]->s_n, x);
}

t_int *atari_2600_perform(t_int *w)
{
 t_float *outL = (t_float *)(w[1]);
 t_atari_2600 *x = (t_atari_2600 *)(w[3]);

 Update_tia_sound(x, 0x15, x->x15);
 Update_tia_sound(x, 0x16, x->x16);
 Update_tia_sound(x, 0x17, x->x17);
 Update_tia_sound(x, 0x18, x->x18);
 Update_tia_sound(x, 0x19, x->x19);
 Update_tia_sound(x, 0x1a, x->x1a);

 Tia_process(x, outL, (int)(w[2]));

 return (w + 4);
}


/*****************************************************************************/
/*                                                                           */
/* Module:  TIA Chip Sound Simulator                                         */
/* Purpose: To emulate the sound generation hardware of the Atari TIA chip.  */
/* Author:  Ron Fries                                                        */
/*                                                                           */
/* Revision History:                                                         */
/*    10-Sep-96 - V1.0 - Initial Release                                     */
/*    14-Jan-97 - V1.1 - Cleaned up sound output by eliminating counter      */
/*                       reset.                                              */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*                 License Information and Copyright Notice                  */
/*                 ========================================                  */
/*                                                                           */
/* TiaSound is Copyright(c) 1996 by Ron Fries                                */
/*                                                                           */
/* This library is free software; you can redistribute it and/or modify it   */
/* under the terms of version 2 of the GNU Library General Public License    */
/* as published by the Free Software Foundation.                             */
/*                                                                           */
/* This library is distributed in the hope that it will be useful, but       */
/* WITHOUT ANY WARRANTY; without even the implied warranty of                */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library */
/* General Public License for more details.                                  */
/* To obtain a copy of the GNU Library General Public License, write to the  */
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   */
/*                                                                           */
/* Any permitted reproduction of these routines, in whole or in part, must   */
/* bear this legend.                                                         */
/*                                                                           */
/*****************************************************************************/

void Tia_sound_init (t_atari_2600 *x, uint16 sample_freq, uint16 playback_freq)
{
  uint8 chan;
  int16 n;

  /* fill the 9bit polynomial with random bits */
  for (n=0; n<POLY9_SIZE; n++)
  {
     x->Bit9[n] = rand() & 0x01;       /* fill poly9 with random bits */
  }

  /* initialize the local globals */
  for (chan = CHAN1; chan <= CHAN2; chan++)
  {
     x->Outvol[chan] = 0;
     x->Div_n_cnt[chan] = 0;
     x->Div_n_max[chan] = 0;
     x->AUDC[chan] = 0;
     x->AUDF[chan] = 0;
     x->AUDV[chan] = 0;
     x->P4[chan] = 0;
     x->P5[chan] = 0;
     x->P9[chan] = 0;
  }

  x->volume = 100;
}

void Update_tia_sound(t_atari_2600 *x, uint16 addr, uint8 val)
{
   uint16 new_val = 0;
   uint8 chan;

   /* determine which address was changed */
   switch (addr)
   {
      case AUDC0:
         x->AUDC[0] = val & 0x0f;
         chan = 0;
         break;

      case AUDC1:
         x->AUDC[1] = val & 0x0f;
         chan = 1;
         break;

      case AUDF0:
         x->AUDF[0] = val & 0x1f;
         chan = 0;
         break;

      case AUDF1:
         x->AUDF[1] = val & 0x1f;
         chan = 1;
         break;

      case AUDV0:
         x->AUDV[0] = (val & 0x0f) << 3;
         chan = 0;
         break;

      case AUDV1:
         x->AUDV[1] = (val & 0x0f) << 3;
         chan = 1;
         break;

      default:
         chan = 255;
         break;
   }

   /* if the output value changed */
   if (chan != 255)
   {
      /* an AUDC value of 0 is a special case */
      if (x->AUDC[chan] == SET_TO_1)
      {
         /* indicate the clock is zero so no processing will occur */
         new_val = 0;

         /* and set the output to the selected volume */
         x->Outvol[chan] = x->AUDV[chan];
      }
      else
      {
         /* otherwise calculate the 'divide by N' value */
         new_val = x->AUDF[chan] + 1;

         /* if bits 2 & 3 are set, then multiply the 'div by n' count by 3 */
         if ((x->AUDC[chan] & DIV3_MASK) == DIV3_MASK)
         {
            new_val *= 3;
         }
      }

      /* only reset those channels that have changed */
      if (new_val != x->Div_n_max[chan])
      {
         /* reset the divide by n counters */
         x->Div_n_max[chan] = new_val;

         /* if the channel is now volume only or was volume only */
         if ((x->Div_n_cnt[chan] == 0) || (new_val == 0))
         {
            /* reset the counter (otherwise let it complete the previous) */
            x->Div_n_cnt[chan] = new_val;
         }
      }
   }
        
}

void Tia_process(t_atari_2600 *x, t_float *buffer, uint16 n)
{
   uint8 audc0,audv0,audc1,audv1;
   uint8 div_n_cnt0,div_n_cnt1;
   uint8 p5_0, p5_1,outvol_0,outvol_1;

   audc0 = x->AUDC[0];
   audv0 = x->AUDV[0];
   audc1 = x->AUDC[1];
   audv1 = x->AUDV[1];

   /* make temporary local copy */
   p5_0 = x->P5[0];
   p5_1 = x->P5[1];
   outvol_0 = x->Outvol[0];
   outvol_1 = x->Outvol[1];
   div_n_cnt0 = x->Div_n_cnt[0];
   div_n_cnt1 = x->Div_n_cnt[1];

   /* loop until the buffer is filled */
   while (n)
   {
      /* Process channel 0 */
      if (div_n_cnt0 > 1)
      {
         div_n_cnt0--;
      }
      else if (div_n_cnt0 == 1)
      {
         div_n_cnt0 = x->Div_n_max[0];

         /* the P5 counter has multiple uses, so we inc it here */
         p5_0++;
         if (p5_0 == POLY5_SIZE)
            p5_0 = 0;

         /* check clock modifier for clock tick */
         if  (((audc0 & 0x02) == 0) ||
             (((audc0 & 0x01) == 0) && Div31[p5_0]) ||
             (((audc0 & 0x01) == 1) &&  Bit5[p5_0]))
         {
                
            if (audc0 & 0x04)       /* pure modified clock selected */
            {
               if (outvol_0)        /* if the output was set */
                  outvol_0 = 0;     /* turn it off */
               else {
                  outvol_0 = audv0; /* else turn it on */
                                }
            }
            else if (audc0 & 0x08)    /* check for p5/p9 */
            {
               if (audc0 == POLY9)    /* check for poly9 */
               {
                  /* inc the poly9 counter */
                  x->P9[0]++;
                  if (x->P9[0] == POLY9_SIZE)
                     x->P9[0] = 0;

                  if (x->Bit9[x->P9[0]]) {
                     outvol_0 = audv0;
                                        }
                  else
                     outvol_0 = 0;
               }
               else                        /* must be poly5 */
               {
                  if (Bit5[p5_0]) {
                     outvol_0 = audv0;
                                          }
                  else
                     outvol_0 = 0;
               }
            }
            else  /* poly4 is the only remaining option */
            {
               /* inc the poly4 counter */
               x->P4[0]++;
               if (x->P4[0] == POLY4_SIZE)
                  x->P4[0] = 0;

               if (Bit4[x->P4[0]]) {
                  outvol_0 = audv0;
                                   }
               else
                  outvol_0 = 0;
            }
         }
      }

      /* Process channel 1 */
      if (div_n_cnt1 > 1)
      {
         div_n_cnt1--;
      }
      else if (div_n_cnt1 == 1)
      {
         div_n_cnt1 = x->Div_n_max[1];

         /* the P5 counter has multiple uses, so we inc it here */
         p5_1++;
         if (p5_1 == POLY5_SIZE)
            p5_1 = 0;

         /* check clock modifier for clock tick */
         if  (((audc1 & 0x02) == 0) ||
             (((audc1 & 0x01) == 0) && Div31[p5_1]) ||
             (((audc1 & 0x01) == 1) &&  Bit5[p5_1]))
         {
            if (audc1 & 0x04)       /* pure modified clock selected */
            {
               if (outvol_1)        /* if the output was set */
                  outvol_1 = 0;     /* turn it off */
               else
                  outvol_1 = audv1; /* else turn it on */
            }
            else if (audc1 & 0x08)    /* check for p5/p9 */
            {
               if (audc1 == POLY9)    /* check for poly9 */
               {
                  /* inc the poly9 counter */
                  x->P9[1]++;
                  if (x->P9[1] == POLY9_SIZE)
                     x->P9[1] = 0;

                  if (x->Bit9[x->P9[1]])
                     outvol_1 = audv1;
                  else
                     outvol_1 = 0;
               }
               else                        /* must be poly5 */
               {
                  if (Bit5[p5_1])
                     outvol_1 = audv1;
                  else
                     outvol_1 = 0;
               }
            }
            else  /* poly4 is the only remaining option */
            {
               /* inc the poly4 counter */
               x->P4[1]++;
               if (x->P4[1] == POLY4_SIZE)
                  x->P4[1] = 0;

               if (Bit4[x->P4[1]])
                  outvol_1 = audv1;
               else
                  outvol_1 = 0;
            }
         }
      }
      {
                  uint32 val = ((((uint32)outvol_0 + (uint32)outvol_1) * 
x->volume)/100);
                  float va = (((float)val) - 100.0)/100.0;
                  *(buffer++) = va;
         n--;
      }
   }

   /* save for next round */
   x->P5[0] = p5_0;
   x->P5[1] = p5_1;
   x->Outvol[0] = outvol_0;
   x->Outvol[1] = outvol_1;
   x->Div_n_cnt[0] = div_n_cnt0;
   x->Div_n_cnt[1] = div_n_cnt1;
}

_______________________________________________
PD-list@iem.at mailing list
UNSUBSCRIBE and account-management -> 
http://lists.puredata.info/listinfo/pd-list

Reply via email to