// Path: src/main/java/eu/rekawek/coffeegb/AddressSpace.java
// public interface AddressSpace {
// 
//     boolean accepts(int address);
// 
//     void setByte(int address, int value);
// 
//     int getByte(int address);
// 
// }
// 
// Path: src/main/java/eu/rekawek/coffeegb/debug/command/apu/Channel.java
// public class Channel implements Command {
// 
//     private static final CommandPattern PATTERN = CommandPattern.Builder
//             .create("apu chan")
//             .withDescription("enable given channels (1-4)")
//             .build();
// 
//     private final Sound sound;
// 
//     public Channel(Sound sound) {
//         this.sound = sound;
//     }
// 
//     @Override
//     public CommandPattern getPattern() {
//         return PATTERN;
//     }
// 
//     @Override
//     public void run(CommandPattern.ParsedCommandLine commandLine) {
//         Set<String> channels = new HashSet<>(commandLine.getRemainingArguments());
//         for (int i = 1; i <= 4; i++) {
//             sound.enableChannel(i - 1, channels.contains(String.valueOf(i)));
//         }
//     }
// }
// 
// Path: src/main/java/eu/rekawek/coffeegb/memory/Ram.java
// public class Ram implements AddressSpace {
// 
//     private int[] space;
// 
//     private int length;
// 
//     private int offset;
// 
//     public Ram(int offset, int length) {
//         this.space = new int[length];
//         this.length = length;
//         this.offset = offset;
//     }
// 
//     private Ram(int offset, int length, Ram ram) {
//         this.offset = offset;
//         this.length = length;
//         this.space = ram.space;
//     }
// 
//     public static Ram createShadow(int offset, int length, Ram ram) {
//         return new Ram(offset, length, ram);
//     }
// 
//     @Override
//     public boolean accepts(int address) {
//         return address >= offset && address < offset + length;
//     }
// 
//     @Override
//     public void setByte(int address, int value) {
//         space[address - offset] = value;
//     }
// 
//     @Override
//     public int getByte(int address) {
//         int index = address - offset;
//         if (index < 0 || index >= space.length) {
//             throw new IndexOutOfBoundsException("Address: " + address);
//         }
//         return space[index];
//     }
// }


import eu.rekawek.coffeegb.AddressSpace;
import eu.rekawek.coffeegb.debug.command.apu.Channel;
import eu.rekawek.coffeegb.memory.Ram;
package eu.rekawek.coffeegb.sound;


public class Sound implements AddressSpace {

    private static final int[] MASKS = new int[] {
            0x80, 0x3f, 0x00, 0xff, 0xbf,
            0xff, 0x3f, 0x00, 0xff, 0xbf,
            0x7f, 0xff, 0x9f, 0xff, 0xbf,
            0xff, 0xff, 0x00, 0x00, 0xbf,
            0x00, 0x00, 0x70,
            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };

    private final AbstractSoundMode[] allModes = new AbstractSoundMode[4];

    private final Ram r = new Ram(0xff24, 0x03);

    private final SoundOutput output;

    private int[] channels = new int[4];

    private boolean enabled;

    private boolean[] overridenEnabled = {true, true, true, true};

    public Sound(SoundOutput output, boolean gbc) {
        allModes[0] = new SoundMode1(gbc);
        allModes[1] = new SoundMode2(gbc);
        allModes[2] = new SoundMode3(gbc);
// Path: src/main/java/eu/rekawek/coffeegb/AddressSpace.java
// public interface AddressSpace {
// 
//     boolean accepts(int address);
// 
//     void setByte(int address, int value);
// 
//     int getByte(int address);
// 
// }
// 
// Path: src/main/java/eu/rekawek/coffeegb/debug/command/apu/Channel.java
// public class Channel implements Command {
// 
//     private static final CommandPattern PATTERN = CommandPattern.Builder
//             .create("apu chan")
//             .withDescription("enable given channels (1-4)")
//             .build();
// 
//     private final Sound sound;
// 
//     public Channel(Sound sound) {
//         this.sound = sound;
//     }
// 
//     @Override
//     public CommandPattern getPattern() {
//         return PATTERN;
//     }
// 
//     @Override
//     public void run(CommandPattern.ParsedCommandLine commandLine) {
//         Set<String> channels = new HashSet<>(commandLine.getRemainingArguments());
//         for (int i = 1; i <= 4; i++) {
//             sound.enableChannel(i - 1, channels.contains(String.valueOf(i)));
//         }
//     }
// }
// 
// Path: src/main/java/eu/rekawek/coffeegb/memory/Ram.java
// public class Ram implements AddressSpace {
// 
//     private int[] space;
// 
//     private int length;
// 
//     private int offset;
// 
//     public Ram(int offset, int length) {
//         this.space = new int[length];
//         this.length = length;
//         this.offset = offset;
//     }
// 
//     private Ram(int offset, int length, Ram ram) {
//         this.offset = offset;
//         this.length = length;
//         this.space = ram.space;
//     }
// 
//     public static Ram createShadow(int offset, int length, Ram ram) {
//         return new Ram(offset, length, ram);
//     }
// 
//     @Override
//     public boolean accepts(int address) {
//         return address >= offset && address < offset + length;
//     }
// 
//     @Override
//     public void setByte(int address, int value) {
//         space[address - offset] = value;
//     }
// 
//     @Override
//     public int getByte(int address) {
//         int index = address - offset;
//         if (index < 0 || index >= space.length) {
//             throw new IndexOutOfBoundsException("Address: " + address);
//         }
//         return space[index];
//     }
// }


// Path: src/main/java/eu/rekawek/coffeegb/sound/Sound.java
import eu.rekawek.coffeegb.AddressSpace;
import eu.rekawek.coffeegb.debug.command.apu.Channel;
import eu.rekawek.coffeegb.memory.Ram;

package eu.rekawek.coffeegb.sound;


public class Sound implements AddressSpace {

    private static final int[] MASKS = new int[] {
            0x80, 0x3f, 0x00, 0xff, 0xbf,
            0xff, 0x3f, 0x00, 0xff, 0xbf,
            0x7f, 0xff, 0x9f, 0xff, 0xbf,
            0xff, 0xff, 0x00, 0x00, 0xbf,
            0x00, 0x00, 0x70,
            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };

    private final AbstractSoundMode[] allModes = new AbstractSoundMode[4];

    private final Ram r = new Ram(0xff24, 0x03);

    private final SoundOutput output;

    private int[] channels = new int[4];

    private boolean enabled;

    private boolean[] overridenEnabled = {true, true, true, true};

    public Sound(SoundOutput output, boolean gbc) {
        allModes[0] = new SoundMode1(gbc);
        allModes[1] = new SoundMode2(gbc);
        allModes[2] = new SoundMode3(gbc);
        allModes[3] = new SoundMode4(gbc);