WebAudioMediaCodecBridge.java revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)package org.chromium.media;
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.content.Context;
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.media.AudioFormat;
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.media.MediaCodec;
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.media.MediaCodec.BufferInfo;
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.media.MediaExtractor;
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.media.MediaFormat;
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.os.ParcelFileDescriptor;
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import android.util.Log;
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import java.io.File;
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import java.nio.ByteBuffer;
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import org.chromium.base.CalledByNative;
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import org.chromium.base.JNINamespace;
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)@JNINamespace("media")
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class WebAudioMediaCodecBridge {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    private static final boolean DEBUG = true;
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    static final String LOG_TAG = "WebAudioMediaCodec";
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // TODO(rtoy): What is the correct timeout value for reading
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // from a file in memory?
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    static final long TIMEOUT_MICROSECONDS = 500;
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    @CalledByNative
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private static String CreateTempFile(Context ctx) throws java.io.IOException {
31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        File outputDirectory = ctx.getCacheDir();
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        File outputFile = File.createTempFile("webaudio", ".dat", outputDirectory);
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        return outputFile.getAbsolutePath();
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    @CalledByNative
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    private static boolean decodeAudioFile(Context ctx,
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           int nativeMediaCodecBridge,
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           int inputFD,
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           long dataSize) {
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (dataSize < 0 || dataSize > 0x7fffffff)
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            return false;
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        MediaExtractor extractor = new MediaExtractor();
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ParcelFileDescriptor encodedFD;
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        encodedFD = ParcelFileDescriptor.adoptFd(inputFD);
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        try {
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            extractor.setDataSource(encodedFD.getFileDescriptor(), 0, dataSize);
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } catch (Exception e) {
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            e.printStackTrace();
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            encodedFD.detachFd();
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            return false;
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (extractor.getTrackCount() <= 0) {
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            encodedFD.detachFd();
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            return false;
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        MediaFormat format = extractor.getTrackFormat(0);
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
64ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Number of channels specified in the file
65ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        int inputChannelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
66ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
67ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Number of channels the decoder will provide. (Not
68ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // necessarily the same as inputChannelCount.  See
69ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // crbug.com/266006.)
70ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        int outputChannelCount = inputChannelCount;
71ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        String mime = format.getString(MediaFormat.KEY_MIME);
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        long durationMicroseconds = 0;
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (format.containsKey(MediaFormat.KEY_DURATION)) {
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            try {
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                durationMicroseconds = format.getLong(MediaFormat.KEY_DURATION);
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            } catch (Exception e) {
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                Log.d(LOG_TAG, "Cannot get duration");
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            }
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (DEBUG) {
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            Log.d(LOG_TAG, "Tracks: " + extractor.getTrackCount()
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  + " Rate: " + sampleRate
87ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                  + " Channels: " + inputChannelCount
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  + " Mime: " + mime
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  + " Duration: " + durationMicroseconds + " microsec");
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        nativeInitializeDestination(nativeMediaCodecBridge,
93ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                    inputChannelCount,
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    sampleRate,
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    durationMicroseconds);
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // Create decoder
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        MediaCodec codec = MediaCodec.createDecoderByType(mime);
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        codec.start();
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // A track must be selected and will be used to read samples.
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        extractor.selectTrack(0);
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        boolean sawInputEOS = false;
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        boolean sawOutputEOS = false;
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // Keep processing until the output is done.
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        while (!sawOutputEOS) {
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            if (!sawInputEOS) {
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                // Input side
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECONDS);
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                if (inputBufIndex >= 0) {
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    int sampleSize = extractor.readSampleData(dstBuf, 0);
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    long presentationTimeMicroSec = 0;
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    if (sampleSize < 0) {
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        sawInputEOS = true;
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        sampleSize = 0;
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    } else {
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        presentationTimeMicroSec = extractor.getSampleTime();
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    }
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    codec.queueInputBuffer(inputBufIndex,
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           0, /* offset */
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           sampleSize,
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           presentationTimeMicroSec,
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    if (!sawInputEOS) {
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        extractor.advance();
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    }
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                }
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            }
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            // Output side
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            MediaCodec.BufferInfo info = new BufferInfo();
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            final int outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_MICROSECONDS);
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            if (outputBufIndex >= 0) {
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                ByteBuffer buf = codecOutputBuffers[outputBufIndex];
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                if (info.size > 0) {
149ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                    nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size,
150ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                         inputChannelCount, outputChannelCount);
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                }
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                buf.clear();
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                codec.releaseOutputBuffer(outputBufIndex, false /* render */);
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    sawOutputEOS = true;
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                }
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                codecOutputBuffers = codec.getOutputBuffers();
161ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
162ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                MediaFormat newFormat = codec.getOutputFormat();
163ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                outputChannelCount = newFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
164ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                Log.d(LOG_TAG, "output format changed to " + newFormat);
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            }
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        encodedFD.detachFd();
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        codec.stop();
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        codec.release();
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        codec = null;
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    private static native void nativeOnChunkDecoded(
178ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size,
179ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        int inputChannelCount, int outputChannelCount);
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    private static native void nativeInitializeDestination(
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        int nativeWebAudioMediaCodecBridge,
183ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        int inputChannelCount,
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        int sampleRate,
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        long durationMicroseconds);
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
187