I fixed the error due to the ThreadPool being closed prematurely. (Error shown by teletubo.)
I didn’t realize until just now that opiop65 message was saying the same thing as KevinWorkman explained about the ‘other sources’ box in the security settings. So thanks for that!
My reading comprehension level: iffy. It helps me a lot to have an example as well as a statement of general theory/purpose.
So far though, while I am excited the gist of the program runs, I don’t understand the error (can’t recreate) where the sustain tone refused to turn off. Also, I may have to scale back hopes, if the code I wrote is unable to run without hogging cpu, if it can run at all without dropouts. Maybe this library, as currently written, will only work with the most powerful Android devices, or only with future devices, as they come up to or exceed the performance standards of desk tops of the last couple years.
Is the following useful/interesting? Maybe I should post elsewhere (audio thread, or under its own subject). Here is the Android wrapper code I wrote for the library. OK to share. Maybe it can be edited to allow another library to work on Android, e.g., TinySound. It only covers playback, though, not loading of sound files. I haven’t written that part yet.
public class AudioWrapper {
private CoreMixer coreMix;
public final int FRAME_SIZE;
public final int SAMPLE_RATE;
private volatile boolean playing;
private Thread t;
public AudioWrapper(CoreMixer cm) {
this.FRAME_SIZE = 4;
this.SAMPLE_RATE = 44100;
this.coreMix = cm;
}
public void start() throws IllegalStateException {
if(this.playing) {
throw new IllegalStateException("Already running.");
} else {
coreMix.prepareToGo();
playing = true;
AudioWrapper.FrameMixerPlayer mixerPlayer =
new AudioWrapper.FrameMixerPlayer();
t = new Thread(mixerPlayer);
t.start();
}
}
public void stop() throws IllegalStateException {
if(!this.playing) {
throw new IllegalStateException("Already stopped.");
} else {
playing = false;
coreMix.setToStop();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t = null;
}
}
private class FrameMixerPlayer implements Runnable {
private FrameMixerPlayer() {}
public void run() {
int bufferSize = AudioTrack.getMinBufferSize(
SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
short[] samples = new short[bufferSize];
float[] e = new float[2];
int i = 0;
while(AudioWrapper.this.playing) {
while(i < bufferSize) {
e[0] = 0;
e[1] = 0;
coreMix.getFrame(e);
samples[i++] = (short)(e[0] * 32767);
samples[i++] = (short)(e[1] * 32767);
}
audioTrack.write(samples, 0, bufferSize);
i = 0;
}
System.out.println("FrameMixerPlayer.run exiting");
}
}
}
AudioTrack is the Android class used for audio playback. In later implementations, other forms besides loading an array of Shorts has become possible. I’ve not tested yet which might be the fastest. The data from my end is currently -1 to 1 (floats).
The only link to my AudioLibrary is with the class I wrote called CoreMixer. An instance is passed in when the wrapper is instantiated.
The CoreMixer class exposes a read() method which returns a frame of sound. The read() can easily be altered to retrieve a full buffer’s worth instead, depending on how the target audio library works. Perhaps I will try making that change in my library, though a lot of the functionality in it assumes working one frame at a time.
I also exposed prepareToGo() and setToStart() methods to handle some starting and cleanup chores in the library class.