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 Namco163SoundChip implements ExpansionSoundChip {
  
      /*
       Warning for anyone making Namco 163 music:
       * As the number of channels used increases, the sample rate decreases.
       * Since the original chip only outputs one channel at a time and cycles
       * between them, using all 8 channels causes very noticeable 10khz noise
       * which is not implemented here.
       */
      private final int[] registers = new int[128], out = new int[8];
      private int numch, cycpos = 0, curch = 0;
  
      @Override
      public void clock(int cycles) {
          numch = 1 + ((registers[127] >> 4) & 7);
          for (int i = 0; i < cycles; ++i) {
              cycpos = ++cycpos % 15;
              if (cycpos == 0) {
                  curch = ++curch % numch;
                  clock_channel(curch);
              }
          }
      }
  
      private void clock_channel(final int ch) {
          //get channel register start position
          final int off = 0x80 - (8 * (ch + 1));
          //get phase/freq value
          int phase = (registers[off + 5] << 16) + (registers[off + 3] << 8) + registers[off + 1];
          final int f = ((registers[off + 4] & 3) << 16) + (registers[off + 2] << 8) + registers[off];
          //get waveform length
          int len = (64 - (registers[off + 4] >> 2)) * 4;
          //ugly heuristics: some NSFs were written with the
          //expectation that you couldn't have more than 32 byte samples
          //and that this field loops. Rolling Thunder shows this isn't the case
          //but the NSFs work in Nintendlator/Nestopia and so have to work
          //well if there's more than 3 zero samples in the wave data i guess
          //the longer length probably isn't legit.
          if (len > 32 && registers[off + 4] != 0) {
              for (int i = 2; (i << 2) < len; ++i) {
                  if (registers[i - 2] == 0 && registers[i - 1] == 0 && registers[i] == 0) {
                      len = 0x20 - (registers[off + 4] & 0x1C);
                      //System.err.println("truncation");
                      break;
                  }
              }
          }
  
          //get waveform start position
          final int wavestart = registers[off + 6];
          //get volume
          phase = (phase + f) % (len << 16);
          final int volume = registers[off + 7] & 0xf;
          final int output = (getWavefromRAM(((phase >> 16) + wavestart) & 0xff) - 8) * volume;
          //store phase back
          registers[off + 5] = (phase >> 16) & 0xff;
          registers[off + 3] = (phase >> 8) & 0xff;
          registers[off + 1] = phase & 0xff;
          out[ch] = output * 16;
          output();
      }
  
      private int getWavefromRAM(final int addr) {
          final int b = registers[(addr) >> 1];
          return ((addr & (utils.BIT0)) != 0) ? b >> 4 : b & 0xf;
      }
  
      @Override
      public void write(int register, int data) {
          //System.err.println(numch);
          registers[register] = data;
      }
  
      public int read(int register) {
          return registers[register];
      }
  
      @Override
      public int getval() {
          return lpaccum << 2;
      }
      int lpaccum = 0;
  
      private void output() {
          int sample = 0;
          for (int i = 0; i < numch; ++i) {
              sample += out[i];
          }
          //this low pass filter is here to reduce noise in games using 8 channels
          //while still letting me output 1 after the other like the real chip does
          sample += lpaccum;
          lpaccum -= sample * (1 / 16.);
      }
  }
  