// Copyright 2011-2012 Paulo Augusto Peccin. See licence.txt distributed with this file.
package org.javatari.atari.tia.audio;
import org.javatari.general.av.audio.AudioMonitor;
import org.javatari.general.av.audio.AudioSignal;
import org.javatari.general.board.ClockDriven;
import org.javatari.parameters.Parameters;
public abstract class AudioGenerator implements AudioSignal, ClockDriven {
@Override
public void connectMonitor(AudioMonitor monitor) {
this.monitor = monitor;
}
@Override
public void clockPulse() {
if (frameSamples < samplesPerFrame) generateNextSamples(1);
if (generatedSamples >= SEND_CHUNK) sendGeneratedSamples();
}
public AudioMonitor monitor() {
return monitor;
}
public ChannelStream channel0() {
return channel0;
}
public ChannelStream channel1() {
return channel1;
}
public void fps(double fps) {
// Normal amount is 2 sample per scanline = 31440, 524 for NTSC(60Hz) and 624 for PAL(50hz)
// Calculate total samples per frame based on fps
samplesPerFrame = (int) Math.round(SAMPLE_RATE / fps);
if (samplesPerFrame > MAX_SAMPLES) samplesPerFrame = MAX_SAMPLES;
}
public void signalOff() {
generatedSamples = 0;
frameSamples = 0;
if (monitor != null) monitor.nextSamples(null, 0);
}
public void finishFrame() {
int missingSamples = samplesPerFrame - frameSamples;
if (missingSamples > 0) generateNextSamples(missingSamples);
int available = sendGeneratedSamples();
// Check available samples on monitor to prevent starvation. Send additional samples if needed
if (available >= 0 && available < MIN_MONITOR_BUFFER_CHUNKS * SEND_CHUNK) {
int add = MIN_MONITOR_BUFFER_CHUNKS * SEND_CHUNK - available + SEND_CHUNK / MONITOR_BUFFER_CHUNKS_ADD_FACTOR;
if (add > MAX_SAMPLES) add = MAX_SAMPLES;
generateNextSamples(add);
sendGeneratedSamples();
// System.out.println("Available in Monitor: " + available + ", add: " + add);
}
frameSamples = 0;
// Synch with audio monitor as needed
if (SYNC_WITH_AUDIO_MONITOR && monitor != null) monitor.synchOutput();
}
private int sendGeneratedSamples() {
int available = -1;
if (monitor != null)
available = monitor.nextSamples(samples, generatedSamples);
generatedSamples = 0;
return available;
}
protected abstract void generateNextSamples(int quant);
protected final ChannelStream channel0 = new ChannelStream();
protected final ChannelStream channel1 = new ChannelStream();
protected final byte[] samples = new byte[MAX_SAMPLES];
protected int generatedSamples = 0;
protected int frameSamples = 0;
private int samplesPerFrame = 0;
private AudioMonitor monitor;
private static final int SAMPLE_RATE = Parameters.TIA_AUDIO_SAMPLE_RATE;
private static final int SEND_CHUNK = Parameters.TIA_AUDIO_SEND_CHUNK;
private static final int MIN_MONITOR_BUFFER_CHUNKS = Parameters.TIA_AUDIO_MIN_MONITOR_BUFFER_CHUNKS;
private static final int MONITOR_BUFFER_CHUNKS_ADD_FACTOR = Parameters.TIA_AUDIO_MONITOR_BUFFER_CHUNKS_ADD_FACTOR;
private static final int MAX_SAMPLES = 4 * 1024;
private static final boolean SYNC_WITH_AUDIO_MONITOR = Parameters.TIA_SYNC_WITH_AUDIO_MONITOR;
}