content: /*
   * HalfNES by Andrew Hoffman
   * Licensed under the GNU GPL Version 3. See LICENSE file
   */
  package com.grapeshot.halfnes.audio;
  
  import com.grapeshot.halfnes.utils;
  
  /**
   *
   * @author Andrew
   */
  public class Sunsoft5BSoundChip implements ExpansionSoundChip {
      //not complete... missing volume envelopes and noise channel at the moment.
      //sound test for Gimmick - Hold Select, push Start on title screen
  
      private final Timer[] timers = {new SquareTimer(32), new SquareTimer(32), new SquareTimer(32)};
      private final boolean[] enable = {false, false, false};
      private final boolean[] useenvelope = {false, false, false};
      private final int[] volume = {0, 0, 0};
      int enval = 0;
      private static final int[] VOLTBL = getvoltbl();
  
      @Override
      public final void write(final int register, final int data) {
          //System.err.println(register + " " + data);
          switch (register) {
              case 0:
                  timers[0].setperiod((timers[0].getperiod() & 0xf00) + data);
                  break;
              case 1:
                  timers[0].setperiod((timers[0].getperiod() & 0xff) + ((data & 0xf) << 8));
                  break;
              case 2:
                  timers[1].setperiod((timers[1].getperiod() & 0xf00) + data);
                  break;
              case 3:
                  timers[1].setperiod((timers[1].getperiod() & 0xff) + ((data & 0xf) << 8));
                  break;
              case 4:
                  timers[2].setperiod((timers[2].getperiod() & 0xf00) + data);
                  break;
              case 5:
                  timers[2].setperiod((timers[2].getperiod() & 0xff) + ((data & 0xf) << 8));
                  break;
              case 7:
                  for (int i = 0; i < 3; ++i) {
                      enable[i] = !(((data & (1 << i)) != 0));
                  }
                  break;
              case 8:
                  volume[0] = data & 0xf;
                  useenvelope[0] = ((data & (utils.BIT4)) != 0);
                  break;
              case 9:
                  volume[1] = data & 0xf;
                  useenvelope[1] = ((data & (utils.BIT4)) != 0);
                  break;
              case 10:
                  volume[2] = data & 0xf;
                  useenvelope[2] = ((data & (utils.BIT4)) != 0);
                  break;
              case 13:
                  enval = 15;
                  break;
              default:
              //System.err.println("Unrecognized register write to " + register + " ," + data);
          }
      }
  
      @Override
      public final void clock(final int cycle) {
          clockenvelope(cycle);
          timers[0].clock(cycle);
          timers[1].clock(cycle);
          timers[2].clock(cycle);
      }
  
      @Override
      public final int getval() {
          return (enable[0] ? ((useenvelope[0] ? enval : VOLTBL[volume[0]]) * timers[0].getval()) : 0)
                  + (enable[1] ? ((useenvelope[1] ? enval : VOLTBL[volume[1]]) * timers[1].getval()) : 0)
                  + (enable[2] ? ((useenvelope[2] ? enval : VOLTBL[volume[2]]) * timers[2].getval()) : 0);
      }
  
      public static int[] getvoltbl() {
          //the AY-3-8910 volume levels are NOT linear, but logarithmic.
          int[] vols = new int[16];
          for (int i = 0; i < 16; ++i) {
              vols[i] = (int) (90 * Math.pow(1.4, i));
              //matches YMZ284 datasheet info
          }
          //utils.printarray(vols);
          return vols;
      }
  
      private void clockenvelope(final int cycles) {
          enval = 0; //gimmick only uses the envelope to mute a channel.
      }
  }
  