I'm working on an Android program that needs to playback audio files repeatedly with very exact timing (music program).
I'm using an "AudioTrack" right now that has the PCM data loaded in from a WAV sample.
here is the code im testing this feature out with. it just loops until its time to play the sample, then plays it, and repeats this 8 times.
Here is the code that I am using, let me know if you need to see more:
class Sequencer {
private final double BPM = 120;
private final double BPMS = (BPM / 60 / 1000);
private long QUARTER_NOTE_DELAY = (long)(1/BPMS);
private final int PCM_BYTE_OFFSET = 44;
private long lastTick;
private long now = 0;
private int sampleRate = 8000;
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat)*5;
private AudioTrack playbackBuffer = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,
channelConfig,audioFormat,bufferSize,AudioTrack.MODE_STATIC);;
private byte[] pcmArray;
public Sequencer()
{
Log.d("AudioTrack", "BUFFER SIZE: " +bufferSize);
String path = "samples/classical_guitar_c5_16bit.wav";
try {
pcmArray = parsePCMData(path);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e("Error Initializng Sequencer", "File not Found: " + path );
e.printStackTrace();
}
}
public void play() {
new Thread() {
public void run() {
lastTick = 0;
// play 10 times
Log.d("Sequencer", "QUARTER-NOTE = " + QUARTER_NOTE_DELAY + "ms");
for (int i = 0; i < 8; i++)
{
lastTick = System.currentTimeMillis();
while (!update(i));
}
playbackBuffer.flush();
}
}.start();
}
public void stop() {
}
//load PCM data into the buffer
private void loadPCMData(byte[] pcmData)
{
int numBytesWritten = playbackBuffer.write(pcmData, PCM_BYTE_OFFSET, (pcmData.length-PCM_BYTE_OFFSET));
Log.d("AudioTrack", "LOADED " + numBytesWritten + " BYTES" + " IN " + (System.currentTimeMillis()- now) + "ms!");
}
private byte[] parsePCMData(String path) throws IOException
{
InputStream fileIn = assetManager.open(path);
BufferedInputStream buffIn = new BufferedInputStream(fileIn, 8000);
DataInputStream dataIn = new DataInputStream(buffIn);
byte[] pcmArray;
ArrayList<Byte> pcmVector = new ArrayList<Byte>();
int numBytes;
//Read the file into the "music" array
for(int i=0; dataIn.available() > 0; i++)
{
pcmVector.add(dataIn.readByte());
}
//Close the input streams
dataIn.close();
buffIn.close();
fileIn.close();
numBytes = pcmVector.size();
pcmArray = new byte[numBytes];
//Copy the data from the arrayLast to the byte array
for(int i=0; i<numBytes; i++)
{
pcmArray[i] = (Byte) pcmVector.get(i);
}
return pcmArray;
}
//Start Playback of PCM data
private void startPlayback()
{
//stop playback
if(playbackBuffer.getState() != AudioTrack.STATE_NO_STATIC_DATA)
{
playbackBuffer.stop();
playbackBuffer.flush();
}
//start loading the next batch of sounds
loadPCMData(pcmArray);
//reset head position
playbackBuffer.setPlaybackHeadPosition(0);
//playbacksounds
playbackBuffer.play();
}
private boolean update(int i) {
now = System.currentTimeMillis();
if (now - lastTick >= QUARTER_NOTE_DELAY) {
//start Sounds
Log.d("AudioTrack: ", "Starting Playback. Current State: " + Integer.toString(playbackBuffer.getState()));
startPlayback();
Log.d("AudioTrack: ", "Playback Started. Current State: " + Integer.toString(playbackBuffer.getState()));
Log.d("MainMenu.java: ", "Event Triggered after " + Long.toString(now-lastTick) +"ms");
lastTick = now;
//update UI in a seperate thread
beatCounter.post(updateUI);
return true;
}
else{
return false;
}
}
}
It plays perfectly on the first of the eight loops, but after that there is just silence, no visible errors or warnings. (although the state will remain at 1 which is " STATE_INITIALIZED" meaning "State of an AudioTrack that is ready to be used."
I am aware that AudioTrack has a looping feature, as well as the "reloadStaticData" method, but when I start writing the actual app I am testing this out for the data will change each time based on a sequence generated by the user)
In addition, i initially tried doing this with both mediaplayer and soundpool but both gave me too much latency.
As a test i also tried it with re-initializing the AudioTrack entirely every loop through but that gave me too much latency to be really useful.
I apologize for the messy code, I really hope its just something stupid i'm doing wrong, as i'm getting very frustrated.
Thanks!
testing on Android 2.2.2, on a handset device (not emulated)
Here is my LogCat output:
10-01 05:07:22.016: DEBUG/AndroidRuntime(23316): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
10-01 05:07:22.016: DEBUG/AndroidRuntime(23316): CheckJNI is OFF
10-01 05:07:22.016: DEBUG/dalvikvm(23316): creating instr width table
10-01 05:07:22.094: DEBUG/AndroidRuntime(23316): --- registering native functions ---
10-01 05:07:22.461: DEBUG/dalvikvm(22329): GC_EXPLICIT freed 236 objects / 13984 bytes in 39ms
10-01 05:07:23.547: DEBUG/PackageParser(1089): Scanning package: /data/app/vmdl67886.tmp
10-01 05:07:23.680: DEBUG/KeyguardViewMediator(1089): wakeWhenReadyLocked(26)
10-01 05:07:23.680: DEBUG/KeyguardViewMediator(1089): handleWakeWhenReady(26)
10-01 05:07:23.680: DEBUG/KeyguardViewMediator(1089): pokeWakelock(5000)
10-01 05:07:23.680: INFO/power(1089): *** set_screen_state 1
10-01 05:07:23.696: DEBUG/Sensors(1089): using sensors (name=sensors)
10-01 05:07:24.088: INFO/PackageManager(1089): Removing non-system package:com.android.test
10-01 05:07:24.088: INFO/Process(1089): Sending signal. PID: 23293 SIG: 9
10-01 05:07:24.088: INFO/ActivityManager(1089): Force stopping package com.android.test uid=10081
10-01 05:07:24.102: INFO/WindowManager(1089): WIN DEATH: Window{44b41118 com.android.test/com.android.test.MainMenu paused=false}
10-01 05:07:24.118: INFO/UsageStats(1089): Unexpected resume of com.android.launcher while already resumed in com.android.test
10-01 05:07:24.196: DEBUG/SurfaceFlinger(1089): Screen about to return, flinger = 0x120f38
10-01 05:07:24.446: DEBUG/PackageManager(1089): Scanning package com.android.test
10-01 05:07:24.446: INFO/PackageManager(1089): Package com.android.test codePath changed from /data/app/com.android.test-2.apk to /data/app/com.android.test-1.apk; Retaining data and using new
10-01 05:07:24.453: INFO/PackageManager(1089): /data/app/com.android.test-1.apk changed; unpacking
10-01 05:07:24.453: DEBUG/installd(1012): DexInv: --- BEGIN '/data/app/com.android.test-1.apk' ---
10-01 05:07:24.602: DEBUG/dalvikvm(23325): creating instr width table
10-01 05:07:24.641: DEBUG/dalvikvm(23325): DexOpt: load 11ms, verify 23ms, opt 0ms
10-01 05:07:24.649: DEBUG/installd(1012): DexInv: --- END '/data/app/com.android.test-1.apk' (success) ---
10-01 05:07:24.657: DEBUG/PackageManager(1089): Activities: com.android.test.AndroidTestActivity com.android.test.MainMenu
10-01 05:07:24.657: INFO/ActivityManager(1089): Force stopping package com.android.test uid=10081
10-01 05:07:24.657: WARN/PackageManager(1089): Code path for pkg : com.android.test changing from /data/app/com.android.test-2.apk to /data/app/com.android.test-1.apk
10-01 05:07:24.657: WARN/PackageManager(1089): Resource path for pkg : com.android.test changing from /data/app/com.android.test-2.apk to /data/app/com.android.test-1.apk
10-01 05:07:24.829: INFO/installd(1012): move /data/dalvik-cache/data@app@com.android.test-1.apk@classes.dex -> /data/dalvik-cache/data@app@com.android.test-1.apk@classes.dex
10-01 05:07:24.829: DEBUG/PackageManager(1089): New package installed in /data/app/com.android.test-1.apk
10-01 05:07:24.868: DEBUG/KeyguardViewMediator(1089): pokeWakelock(5000)
10-01 05:07:25.000: DEBUG/KeyguardViewMediator(1089): pokeWakelock(5000)
10-01 05:07:25.211: WARN/InputManagerService(1089): Got RemoteException sending setActive(false) notification to pid 23293 uid 10081
10-01 05:07:25.274: INFO/ActivityManager(1089): Force stopping package com.android.test uid=10081
10-01 05:07:25.571: DEBUG/dalvikvm(1089): GC_EXPLICIT freed 24967 objects / 1341840 bytes in 159ms
10-01 05:07:25.672: DEBUG/VoiceDialerReceiver(22354): onReceive Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:com.android.test flg=0x10000000 cmp=com.android.voicedialer/.VoiceDialerReceiver (has extras) }
10-01 05:07:25.977: DEBUG/dalvikvm(1089): GC_EXPLICIT freed 6005 objects / 330448 bytes in 141ms
10-01 05:07:26.016: DEBUG/VoiceDialerReceiver(22354): onReceive Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.android.test flg=0x10000000 cmp=com.android.voicedialer/.VoiceDialerReceiver (has extras) }
10-01 05:07:26.250: INFO/installd(1012): unlink /data/dalvik-cache/data@app@com.android.test-2.apk@classes.dex
10-01 05:07:26.258: DEBUG/AndroidRuntime(23316): Shutting down VM
10-01 05:07:26.266: DEBUG/dalvikvm(23316): Debugger has detached; object registry had 1 entries
10-01 05:07:26.282: INFO/AndroidRuntime(23316): NOTE: attach of thread 'Binder Thread #3' failed
10-01 05:07:26.680: DEBUG/AndroidRuntime(23330): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
10-01 05:07:26.680: DEBUG/AndroidRuntime(23330): CheckJNI is OFF
10-01 05:07:26.680: DEBUG/dalvikvm(23330): creating instr width table
10-01 05:07:26.735: DEBUG/AndroidRuntime(23330): --- registering native functions ---
10-01 05:07:27.047: INFO/ActivityManager(1089): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.android.test/.AndroidTestActivity }
10-01 05:07:27.110: DEBUG/AndroidRuntime(23330): Shutting down VM
10-01 05:07:27.110: DEBUG/dalvikvm(23330): Debugger has detached; object registry had 1 entries
10-01 05:07:27.110: INFO/ActivityManager(1089): Start proc com.android.test for activity com.android.test/.AndroidTestActivity: pid=23337 uid=10081 gids={}
10-01 05:07:27.125: INFO/AndroidRuntime(23330): NOTE: attach of thread 'Binder Thread #3' failed
10-01 05:07:27.203: INFO/WindowManager(1089): Setting rotation to 1, animFlags=1
10-01 05:07:27.227: INFO/ActivityManager(1089): Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=2/1/2 nav=2/2 orien=2 layout=34 uiMode=17 seq=554}
10-01 05:07:27.613: INFO/ActivityManager(1089): Displayed activity com.android.test/.AndroidTestActivity: 513 ms (total 513 ms)
10-01 05:07:27.657: WARN/IInputConnectionWrapper(17894): showStatusIcon on inactive InputConnection
10-01 05:07:28.073: INFO/ActivityManager(1089): Starting activity: Intent { act=com.android.test.CLEARSPLASH cmp=com.android.test/.MainMenu }
10-01 05:07:28.141: DEBUG/AudioTrack(23337): BUFFER SIZE: 14860
10-01 05:07:28.571: INFO/ActivityManager(1089): Displayed activity com.android.test/.MainMenu: 488 ms (total 488 ms)
10-01 05:07:29.930: DEBUG/dalvikvm(1089): GC_EXPLICIT freed 3588 objects / 165776 bytes in 182ms
10-01 05:07:40.172: DEBUG/Sequencer(23337): QUARTER-NOTE = 500ms
10-01 05:07:40.680: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 2
10-01 05:07:40.680: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 1ms!
10-01 05:07:40.688: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:40.688: DEBUG/MainMenu.java:(23337): Event Triggered after 502ms
10-01 05:07:41.203: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:41.203: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 1ms!
10-01 05:07:41.203: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:41.203: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:41.711: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:41.711: DEBUG/AudioTrack(23337): LOADED 1486开发者_如何学JAVA0 BYTES IN 1ms!
10-01 05:07:41.719: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:41.719: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:42.227: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:42.235: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 1ms!
10-01 05:07:42.235: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:42.235: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:42.743: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:42.743: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 1ms!
10-01 05:07:42.750: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:42.750: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:43.258: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:43.258: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 1ms!
10-01 05:07:43.258: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:43.258: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:43.774: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:43.774: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 1ms!
10-01 05:07:43.782: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:43.782: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:44.289: DEBUG/AudioTrack:(23337): Starting Playback. Current State: 1
10-01 05:07:44.297: DEBUG/AudioTrack(23337): LOADED 14860 BYTES IN 2ms!
10-01 05:07:44.305: DEBUG/AudioTrack:(23337): Playback Started. Current State: 1
10-01 05:07:44.305: DEBUG/MainMenu.java:(23337): Event Triggered after 500ms
10-01 05:07:44.344: DEBUG/dalvikvm(22433): GC_EXPLICIT freed 827 objects / 40752 bytes in 67ms
10-01 05:07:49.422: DEBUG/dalvikvm(20018): GC_EXPLICIT freed 596 objects / 30440 bytes in 67ms
10-01 05:07:54.563: DEBUG/dalvikvm(22329): GC_EXPLICIT freed 143 objects / 10072 bytes in 80ms
10-01 05:07:59.696: DEBUG/dalvikvm(22354): GC_EXPLICIT freed 547 objects / 30048 bytes in 68ms
10-01 05:08:09.977: DEBUG/dalvikvm(22363): GC_EXPLICIT freed 483 objects / 22512 bytes in 111ms
10-01 05:08:10.219: INFO/power(1089): *** set_screen_state 0
10-01 05:08:10.237: DEBUG/SurfaceFlinger(1089): About to give-up screen, flinger = 0x120f38
10-01 05:08:10.258: DEBUG/Sensors(1089): using accelerometer (name=accelerometer)
10-01 05:08:15.430: DEBUG/StatusBar(1089): DISABLE_EXPAND: yes
10-01 05:08:15.469: DEBUG/GoogleLoginService(16965): onBind: Intent { act=android.accounts.AccountAuthenticator cmp=com.google.android.gsf/.loginservice.GoogleLoginService }
10-01 05:08:15.469: INFO/WindowManager(1089): Setting rotation to 0, animFlags=1
10-01 05:08:15.493: INFO/ActivityManager(1089): Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=2/1/2 nav=2/2 orien=1 layout=34 uiMode=17 seq=555}
10-01 05:08:15.821: DEBUG/dalvikvm(23337): GC_FOR_MALLOC freed 1766 objects / 427920 bytes in 193ms
10-01 05:08:15.899: DEBUG/AudioTrack(23337): BUFFER SIZE: 14860
Maybe you should try to use streamming mode of AudioTrack instead of static one.
精彩评论