【问题标题】:Precisely Sync Looped Audio with Animation in Android在 Android 中将循环音频与动画精确同步
【发布时间】:2014-01-07 10:02:26
【问题描述】:

问题: 有什么方法可以使用 AudioTrack 和 setLoopPoints() 来根据每毫秒的样本数/帧数准确配置循环吗?

编辑:我知道大多数 android 设备拥有的处理能力不能指望完美的准确性。但是,我希望平均循环时间接近节奏的“真实”间隔(以毫秒为单位),因为这就是我基于“动画”的方式,它也应该与节奏同步(动画是一个 SurfaceView,它重绘在速度间隔期间的一条线的坐标)。

详情: 我正在尝试将 AudioTrack 与 setLoopPoints 一起使用来创建准确的节拍器。为此,我使用了两个 wav 文件(Tick 和 Tock)来填充 byte[] 数组以提供给 AudioTrack。考虑一个 4/4 时间的示例,我将填充一个字节 [],一次使用 Tick 从 [0] 开始,三次使用 Tock(使用 arrayCopy())到 [length/4],[length/2],并且[3*length/4],并假设 wav 数据不会相互重叠。

我的代码的粗略示例:

// read each wav file's header into its own ByteBuffer (with LITTLE_ENDIAN order)
// ... then get the size of the audio data
tickDataSize = tickHeaderBuffer.getInt(40); 
tockDataSize = tockHeaderBuffer.getInt(40); 

// allocate space for one loop at the current tempo into a byte[] array (to be given to    AudioTrack) 
// e.g. 22050hz * 2 Bytes (1 per channel) * 2 Bytes (1 per 8 bit PCM sample) = 22050*4 Bytes/second
//      If my tempo were 60 BPM I'd have 88200 Bytes/second (where 1 second is the whole loop interval);
// 110 BPM = 48109.0909091 Bytes per loop interval (where 1 loop interval is 0.54545 seconds)

int tempo = 110;
int bytesPerSecond  = sampleRate * 2 * 2;
int bytesPerInterval = (int)((((float)bytesPerSecond * 60.0F)/(float)tempo) * 4); 

byte[] wavData = new byte[bytesPerInterval];

// ... then fill wavData[] as mentioned above with 1*wavOne and 3*wavTwo

// Then feed to an instance of AudioTrack and set loop points
audioTrack.write(wavData, 0, bytesPerInterval);
int numSamples = bytesPerInterval/4;
audioTrack.setLoopPoints(0, numSamples, -1);
audioTrack.play();

希望您已经开始看到问题所在。 在某些节奏下,我只能在循环中进行静态播放(但仅在第 1 和第 3 个 Tock [循环中的第 2 和第 4 个样本] 期间)。

如果我:

  • 不要用任何 wav 数据填充 byte[],而是保留 bytesPerInterval 和 numSamples 相同(正确持续时间的无声循环)。
  • 设置 bytesPerInterval = bytesPerInterval % 4(从而失去节奏准确性)

工作(非静态)和不工作(静态)速度的示例及其所需的帧数(假设一秒 = 88200 帧):

tempo: 110 (static)
wavData.length: 192436 
numFrames: 48109

tempo: 120 (no static)
wavData.length: 176400 
numFrames: 44100

tempo: 130 (static)
wavData.length: 162828 
numFrames: 40707

tempo: 140 (no static)
wavData.length: 151200 
numFrames: 37800

tempo: 150 (no static)
wavData.length: 141120 
numFrames: 35280

tempo: 160 (static)
wavData.length: 132300 
numFrames: 33075

tempo: 170 (static)
wavData.length: 124516 
numFrames: 31129

tempo: 180 (no static)
wavData.length: 117600 
numFrames: 29400

如果问题的答案是“不,您不能使用 setLoopPoints() 来配置精确到任何毫秒的循环”,那么我想知道任何其他选项。 NDK、SoundPool 或 MediaPlayer 中的 OpenSL ES 是否更适合生成精确循环?

编辑 2:我已经缩小了导致静态问题的位置:

// Assume a tempo of 160 BPM which requires byte[132300]
wavStream1 = this.context.getResources().openRawResource(R.raw.tick);
wavStream2 = this.context.getResources().openRawResource(R.raw.tock);

ByteBuffer headerBuffer1 = ByteBuffer.allocate(44);
ByteBuffer headerBuffer2 = ByteBuffer.allocate(44);
headerBuffer1.order(ByteOrder.LITTLE_ENDIAN);
headerBuffer2.order(ByteOrder.LITTLE_ENDIAN);
wavStream1.read(headerBuffer1.array(), 0, 44);   
wavStream2.read(headerBuffer2.array(), 0, 44);
int tickDataSize = headerBuffer1.getInt(40); 
int tockDataSize = headerBuffer2.getInt(40);    

byte[] wavData = new byte[bytesPerInterval * 4];

byte[] tickWavData = new byte[bytesPerInterval];
byte[] tockWavData = new byte[bytesPerInterval];

wavStream1.read(accentWavData, 0, tickDataSize);
wavStream2.read(normalWavData, 0, tockDataSize);

System.arraycopy(tickWavData, 0, wavData, 0, bytesPerInterval);
System.arraycopy(tockWavData, 0, wavData, 33075, bytesPerInterval);
System.arraycopy(tockWavData, 0, wavData, 66150, bytesPerInterval);
System.arraycopy(tockWavData, 0, wavData, 99225, bytesPerInterval);
// bytesPerInterval of 33075 and 99225 (or any odd number) will be 
// static when wavData is played 

AudioTrack audioTrack = new AudioTrack(3, 22050, 12, 2, wavData.length, 0);
audioTrack.write(wavData, 0, wavData.length);
audioTrack.setLoopPoints(0, bytesPerInterval, -1);
audioTrack.play();  

最重要的是,我想了解为什么以 wavData 奇数索引开始的音频数据会生成静态而不是预期的声音,以及是否有任何补救措施。

【问题讨论】:

标签: java android audio audiotrack opensl


【解决方案1】:

阅读您的编辑后,我认为奇数索引导致问题的原因是因为您正在使用 ENCODING_PCM_16BIT(在构造函数中传递的“2”)创建 AudioTrack。这意味着每个样本都应该是 16 位。如果样本为 8 位,请尝试使用“3”或ENCODING_PCM_8BIT

【讨论】:

  • 当我读到你的评论时,我想“..必须是它!”。这很有意义;但是,我尝试使用 ENCODING_PCM_8BIT 以及玩弄通道、采样率等,甚至修改 WAV 文件以适应每种情况,但最终得到更多静态。我认为原因可能是我正在测试的设备(Nexus 7 2012)不支持 AudioTrack 文档所警告的 8 位 PCM。我重视您的建议,但相信必须有更好的选择。目前我坚持在 byte[] 中添加“%2”,这会为每个循环增加大约 0.00005 毫秒。
  • 我认为也必须如此 :) 我唯一的其他倾向是原生和 OpenSL 可能产生更好的结果。在本机方面,AudioTrack 和 OpenSL 应该共享相同的底层机制,但我对 AudioTrack 源还不够熟悉,无法排除某处挂断的可能性。至于不支持 8 位 PCM 的设备,可能是这样,但你不能用 16 位源来测试吗?我很好奇,因为我有一个相关的项目正在进行中......
  • 另外,如果您还没有阅读提供的通用假日名称链接,请阅读。它证明了 OpenSL 是必经之路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多