/*
SoundTMS9919Generator.java
(c) 2011-2013 Edward Swartz
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
*/
package v9t9.audio.sound;
import java.util.HashMap;
import java.util.Map;
import v9t9.common.cpu.ICpu;
import v9t9.common.machine.IMachine;
import v9t9.common.machine.IRegisterAccess;
import v9t9.common.machine.IRegisterAccess.RegisterInfo;
import v9t9.common.sound.TMS9919Consts;
import ejs.base.properties.IProperty;
import ejs.base.sound.ISoundVoice;
/**
* Generator for the TMS9919 sound chip
* @author ejs
*
*/
public class SoundTMS9919Generator extends BaseSoundChipSoundGenerator {
final public static int
VOICE_TONE_0 = 0,
VOICE_TONE_1 = 1,
VOICE_TONE_2 = 2,
VOICE_NOISE = 3,
VOICE_AUDIO = 4,
VOICE_CASSETTE = 5;
protected final Map<Integer, SoundVoice> regIdToVoices = new HashMap<Integer, SoundVoice>();
protected final Map<Integer, IRegisterAccess.IRegisterWriteListener> regIdToListener = new HashMap<Integer, IRegisterAccess.IRegisterWriteListener>();
public SoundTMS9919Generator(IMachine machine, String name, int regBase) {
super(machine);
init(name, regBase);
}
@Override
public synchronized void registerChanged(int reg, int value) {
SoundVoice v = regIdToVoices.get(reg);
if (v == null)
return;
IRegisterAccess.IRegisterWriteListener listener = regIdToListener.get(reg);
if (listener == null)
throw new IllegalStateException();
listener.registerChanged(reg, value);
}
protected int init(String name, int regBase) {
regBase = doInitVoices(name, regBase);
for (ISoundVoice voice : getSoundVoices()) {
if (voice instanceof ClockedSoundVoice) {
((ClockedSoundVoice) voice).setReferenceClock(TMS9919Consts.CHIP_CLOCK);
}
}
return regBase;
}
/**
* @param name
* @param regBase
* @return
*/
protected int doInitVoices(String name, int regBase) {
for (int i = VOICE_TONE_0; i <= VOICE_TONE_2; i++) {
ToneGeneratorVoice v = new ToneGeneratorVoice(name, i);
soundVoicesList.add(v);
regBase += setupToneVoice(regBase, i, v);
}
NoiseGeneratorVoice nv = new NoiseGeneratorVoice(
name);
soundVoicesList.add(nv);
regBase += setupNoiseVoice(regBase, nv);
AudioGateSoundVoice av = new AudioGateSoundVoice(name);
soundVoicesList.add(av);
regBase += setupAudioGateVoice(regBase, av);
return regBase;
}
/**
* @param regBase
*/
protected int setupAudioGateVoice(int regBase, final AudioGateSoundVoice voice) {
RegisterInfo info;
info = soundChip.getRegisterInfo(regBase);
assert info != null && info.id.endsWith("A:G");
regIdToVoices.put(regBase + TMS9919Consts.REG_OFFS_AUDIO_GATE, voice);
final IProperty cyclesPerSecond = getMachine().getSettings().get(ICpu.settingCyclesPerSecond);
regIdToListener.put(regBase + TMS9919Consts.REG_OFFS_AUDIO_GATE,
new IRegisterAccess.IRegisterWriteListener() {
@Override
public void registerChanged(int reg, int value) {
float pos = (value < 0) ? (-value + 1) : value;
voice.setState(pos / cyclesPerSecond.getInt(),
value >= 0);
}
});
return TMS9919Consts.REG_COUNT_AUDIO_GATE;
}
/**
* @param regBase
* @param voice
* @return
*/
protected int setupNoiseVoice(int regBase, final NoiseGeneratorVoice voice) {
RegisterInfo info;
info = soundChip.getRegisterInfo(regBase);
assert info != null && info.id.endsWith("N:Ctl");
regIdToVoices.put(regBase + TMS9919Consts.REG_OFFS_ATTENUATION, voice);
regIdToVoices.put(regBase + TMS9919Consts.REG_OFFS_NOISE_CONTROL, voice);
regIdToListener.put(regBase + TMS9919Consts.REG_OFFS_NOISE_CONTROL,
new IRegisterAccess.IRegisterWriteListener() {
@Override
public void registerChanged(int reg, int value) {
voice.setNoiseControl(value);
if (isNoiseTrackingTone2()) {
updateNoisePeriod();
}
}
}
);
regIdToListener.put(regBase + TMS9919Consts.REG_OFFS_ATTENUATION,
new IRegisterAccess.IRegisterWriteListener() {
@Override
public void registerChanged(int reg, int value) {
voice.setVolume(getVolume(value));
}
}
);
return TMS9919Consts.REG_COUNT_NOISE;
}
/**
*
*/
protected void updateNoisePeriod() {
ISoundVoice[] voices = getSoundVoices();
((ClockedSoundVoice) voices[VOICE_NOISE]).setPeriod(
((ClockedSoundVoice) voices[VOICE_TONE_2]).getPeriod());
}
/**
* @param value
* @return
*/
protected int getVolume(int value) {
return 0xff - ((value & 0xf) * 0xff / 0xf);
}
/**
* @param regBase
* @param i
* @return
*/
protected int setupToneVoice(final int regBase, int num, final ClockedSoundVoice voice) {
RegisterInfo info;
info = soundChip.getRegisterInfo(regBase);
assert info != null && info.id.contains(num + ":Per");
regIdToVoices.put(regBase + TMS9919Consts.REG_OFFS_FREQUENCY_PERIOD,
voice);
regIdToVoices.put(regBase + TMS9919Consts.REG_OFFS_ATTENUATION,
voice);
regIdToListener.put(regBase + TMS9919Consts.REG_OFFS_FREQUENCY_PERIOD,
new IRegisterAccess.IRegisterWriteListener() {
@Override
public void registerChanged(int reg, int value) {
voice.setPeriod(value);
if (reg == regBase + 2 && isNoiseTrackingTone2()) {
updateNoisePeriod();
}
}
}
);
regIdToListener.put(regBase + TMS9919Consts.REG_OFFS_ATTENUATION,
new IRegisterAccess.IRegisterWriteListener() {
@Override
public void registerChanged(int reg, int value) {
voice.setVolume(getVolume(value));
}
}
);
return TMS9919Consts.REG_COUNT_TONE;
}
/**
* @return
*/
protected boolean isNoiseTrackingTone2() {
ISoundVoice[] voices = getSoundVoices();
return (((NoiseGeneratorVoice) voices[VOICE_NOISE]).getNoiseControl() & TMS9919Consts.NOISE_PERIOD_MASK)
== TMS9919Consts.NOISE_PERIOD_VARIABLE;
}
// public void tick() {
// if (soundHandler != null) {
// int pos = machine.getCpu().getCurrentCycleCount();
// int total = machine.getCpu().getCurrentTargetCycleCount();
// long baseCount = machine.getCpu().getTotalCurrentCycleCount();
//
// soundHandler.flushAudio(pos, total, baseCount);
// }
// }
}