AudioStreamTrack.cpp revision c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fb
1/*
2 * Copyright 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "AudioStreamTrack"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <stdint.h>
22#include <media/AudioTrack.h>
23
24#include <aaudio/AAudio.h>
25#include "utility/AudioClock.h"
26#include "legacy/AudioStreamLegacy.h"
27#include "legacy/AudioStreamTrack.h"
28#include "utility/FixedBlockReader.h"
29
30using namespace android;
31using namespace aaudio;
32
33// Arbitrary and somewhat generous number of bursts.
34#define DEFAULT_BURSTS_PER_BUFFER_CAPACITY     8
35
36/*
37 * Create a stream that uses the AudioTrack.
38 */
39AudioStreamTrack::AudioStreamTrack()
40    : AudioStreamLegacy()
41    , mFixedBlockReader(*this)
42{
43}
44
45AudioStreamTrack::~AudioStreamTrack()
46{
47    const aaudio_stream_state_t state = getState();
48    bool bad = !(state == AAUDIO_STREAM_STATE_UNINITIALIZED || state == AAUDIO_STREAM_STATE_CLOSED);
49    ALOGE_IF(bad, "stream not closed, in state %d", state);
50}
51
52aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)
53{
54    aaudio_result_t result = AAUDIO_OK;
55
56    result = AudioStream::open(builder);
57    if (result != OK) {
58        return result;
59    }
60
61    ALOGD("AudioStreamTrack::open = %p", this);
62
63    // Try to create an AudioTrack
64    // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
65    int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED)
66                              ? 2 : getSamplesPerFrame();
67    audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame);
68    ALOGD("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
69            samplesPerFrame, channelMask);
70
71    // TODO add more performance options
72    audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
73
74    int32_t frameCount = builder.getBufferCapacity();
75    ALOGD("AudioStreamTrack::open(), requested buffer capacity %d", frameCount);
76
77    int32_t notificationFrames = 0;
78
79    // TODO implement an unspecified AudioTrack format then use that.
80    audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
81            ? AUDIO_FORMAT_PCM_FLOAT
82            : AAudioConvert_aaudioToAndroidDataFormat(getFormat());
83
84    // Setup the callback if there is one.
85    AudioTrack::callback_t callback = nullptr;
86    void *callbackData = nullptr;
87    // Note that TRANSFER_SYNC does not allow FAST track
88    AudioTrack::transfer_type streamTransferType = AudioTrack::transfer_type::TRANSFER_SYNC;
89    if (builder.getDataCallbackProc() != nullptr) {
90        streamTransferType = AudioTrack::transfer_type::TRANSFER_CALLBACK;
91        callback = getLegacyCallback();
92        callbackData = this;
93
94        notificationFrames = builder.getFramesPerDataCallback();
95        // If the total buffer size is unspecified then base the size on the burst size.
96        if (frameCount == AAUDIO_UNSPECIFIED) {
97            // Take advantage of a special trick that allows us to create a buffer
98            // that is some multiple of the burst size.
99            notificationFrames = 0 - DEFAULT_BURSTS_PER_BUFFER_CAPACITY;
100        }
101    }
102    mCallbackBufferSize = builder.getFramesPerDataCallback();
103
104    ALOGD("AudioStreamTrack::open(), notificationFrames = %d", notificationFrames);
105    mAudioTrack = new AudioTrack(
106            (audio_stream_type_t) AUDIO_STREAM_MUSIC,
107            getSampleRate(),
108            format,
109            channelMask,
110            frameCount,
111            flags,
112            callback,
113            callbackData,
114            notificationFrames,
115            AUDIO_SESSION_ALLOCATE,
116            streamTransferType
117            );
118
119    // Did we get a valid track?
120    status_t status = mAudioTrack->initCheck();
121    ALOGD("AudioStreamTrack::open(), initCheck() returned %d", status);
122    if (status != NO_ERROR) {
123        close();
124        ALOGE("AudioStreamTrack::open(), initCheck() returned %d", status);
125        return AAudioConvert_androidToAAudioResult(status);
126    }
127
128    // Get the actual values from the AudioTrack.
129    setSamplesPerFrame(mAudioTrack->channelCount());
130    setSampleRate(mAudioTrack->getSampleRate());
131    aaudio_audio_format_t aaudioFormat =
132            AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format());
133    setFormat(aaudioFormat);
134
135    // We may need to pass the data through a block size adapter to guarantee constant size.
136    if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
137        int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
138        mFixedBlockReader.open(callbackSizeBytes);
139        mBlockAdapter = &mFixedBlockReader;
140    } else {
141        mBlockAdapter = nullptr;
142    }
143
144    setState(AAUDIO_STREAM_STATE_OPEN);
145    setDeviceId(mAudioTrack->getRoutedDeviceId());
146
147    return AAUDIO_OK;
148}
149
150aaudio_result_t AudioStreamTrack::close()
151{
152    // TODO maybe add close() or release() to AudioTrack API then call it from here
153    if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
154        mAudioTrack.clear(); // TODO is this right?
155        setState(AAUDIO_STREAM_STATE_CLOSED);
156    }
157    mFixedBlockReader.close();
158    return AAUDIO_OK;
159}
160
161void AudioStreamTrack::processCallback(int event, void *info) {
162
163    switch (event) {
164        case AudioTrack::EVENT_MORE_DATA:
165            processCallbackCommon(AAUDIO_CALLBACK_OPERATION_PROCESS_DATA, info);
166            break;
167
168            // Stream got rerouted so we disconnect.
169        case AudioTrack::EVENT_NEW_IAUDIOTRACK:
170            processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
171            break;
172
173        default:
174            break;
175    }
176    return;
177}
178
179aaudio_result_t AudioStreamTrack::requestStart()
180{
181    std::lock_guard<std::mutex> lock(mStreamMutex);
182
183    if (mAudioTrack.get() == nullptr) {
184        return AAUDIO_ERROR_INVALID_STATE;
185    }
186    // Get current position so we can detect when the track is playing.
187    status_t err = mAudioTrack->getPosition(&mPositionWhenStarting);
188    if (err != OK) {
189        return AAudioConvert_androidToAAudioResult(err);
190    }
191
192    err = mAudioTrack->start();
193    if (err != OK) {
194        return AAudioConvert_androidToAAudioResult(err);
195    } else {
196        setState(AAUDIO_STREAM_STATE_STARTING);
197    }
198    return AAUDIO_OK;
199}
200
201aaudio_result_t AudioStreamTrack::requestPause()
202{
203    std::lock_guard<std::mutex> lock(mStreamMutex);
204
205    if (mAudioTrack.get() == nullptr) {
206        return AAUDIO_ERROR_INVALID_STATE;
207    } else if (getState() != AAUDIO_STREAM_STATE_STARTING
208            && getState() != AAUDIO_STREAM_STATE_STARTED) {
209        ALOGE("requestPause(), called when state is %s",
210              AAudio_convertStreamStateToText(getState()));
211        return AAUDIO_ERROR_INVALID_STATE;
212    }
213    setState(AAUDIO_STREAM_STATE_PAUSING);
214    mAudioTrack->pause();
215    status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
216    if (err != OK) {
217        return AAudioConvert_androidToAAudioResult(err);
218    }
219    return AAUDIO_OK;
220}
221
222aaudio_result_t AudioStreamTrack::requestFlush() {
223    std::lock_guard<std::mutex> lock(mStreamMutex);
224
225    if (mAudioTrack.get() == nullptr) {
226        return AAUDIO_ERROR_INVALID_STATE;
227    } else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
228        return AAUDIO_ERROR_INVALID_STATE;
229    }
230    setState(AAUDIO_STREAM_STATE_FLUSHING);
231    incrementFramesRead(getFramesWritten() - getFramesRead());
232    mAudioTrack->flush();
233    mFramesWritten.reset32();
234    return AAUDIO_OK;
235}
236
237aaudio_result_t AudioStreamTrack::requestStop() {
238    std::lock_guard<std::mutex> lock(mStreamMutex);
239
240    if (mAudioTrack.get() == nullptr) {
241        return AAUDIO_ERROR_INVALID_STATE;
242    }
243    setState(AAUDIO_STREAM_STATE_STOPPING);
244    incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
245    mAudioTrack->stop();
246    mFramesWritten.reset32();
247    return AAUDIO_OK;
248}
249
250aaudio_result_t AudioStreamTrack::updateStateWhileWaiting()
251{
252    status_t err;
253    aaudio_wrapping_frames_t position;
254    switch (getState()) {
255    // TODO add better state visibility to AudioTrack
256    case AAUDIO_STREAM_STATE_STARTING:
257        if (mAudioTrack->hasStarted()) {
258            setState(AAUDIO_STREAM_STATE_STARTED);
259        }
260        break;
261    case AAUDIO_STREAM_STATE_PAUSING:
262        if (mAudioTrack->stopped()) {
263            err = mAudioTrack->getPosition(&position);
264            if (err != OK) {
265                return AAudioConvert_androidToAAudioResult(err);
266            } else if (position == mPositionWhenPausing) {
267                // Has stream really stopped advancing?
268                setState(AAUDIO_STREAM_STATE_PAUSED);
269            }
270            mPositionWhenPausing = position;
271        }
272        break;
273    case AAUDIO_STREAM_STATE_FLUSHING:
274        {
275            err = mAudioTrack->getPosition(&position);
276            if (err != OK) {
277                return AAudioConvert_androidToAAudioResult(err);
278            } else if (position == 0) {
279                // Advance frames read to match written.
280                setState(AAUDIO_STREAM_STATE_FLUSHED);
281            }
282        }
283        break;
284    case AAUDIO_STREAM_STATE_STOPPING:
285        if (mAudioTrack->stopped()) {
286            setState(AAUDIO_STREAM_STATE_STOPPED);
287        }
288        break;
289    default:
290        break;
291    }
292    return AAUDIO_OK;
293}
294
295aaudio_result_t AudioStreamTrack::write(const void *buffer,
296                                      int32_t numFrames,
297                                      int64_t timeoutNanoseconds)
298{
299    int32_t bytesPerFrame = getBytesPerFrame();
300    int32_t numBytes;
301    aaudio_result_t result = AAudioConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
302    if (result != AAUDIO_OK) {
303        return result;
304    }
305
306    // TODO add timeout to AudioTrack
307    bool blocking = timeoutNanoseconds > 0;
308    ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
309    if (bytesWritten == WOULD_BLOCK) {
310        return 0;
311    } else if (bytesWritten < 0) {
312        ALOGE("invalid write, returned %d", (int)bytesWritten);
313        return AAudioConvert_androidToAAudioResult(bytesWritten);
314    }
315    int32_t framesWritten = (int32_t)(bytesWritten / bytesPerFrame);
316    incrementFramesWritten(framesWritten);
317    return framesWritten;
318}
319
320aaudio_result_t AudioStreamTrack::setBufferSize(int32_t requestedFrames)
321{
322    ssize_t result = mAudioTrack->setBufferSizeInFrames(requestedFrames);
323    if (result < 0) {
324        return AAudioConvert_androidToAAudioResult(result);
325    } else {
326        return result;
327    }
328}
329
330int32_t AudioStreamTrack::getBufferSize() const
331{
332    return static_cast<int32_t>(mAudioTrack->getBufferSizeInFrames());
333}
334
335int32_t AudioStreamTrack::getBufferCapacity() const
336{
337    return static_cast<int32_t>(mAudioTrack->frameCount());
338}
339
340int32_t AudioStreamTrack::getXRunCount() const
341{
342    return static_cast<int32_t>(mAudioTrack->getUnderrunCount());
343}
344
345int32_t AudioStreamTrack::getFramesPerBurst() const
346{
347    return 192; // TODO add query to AudioTrack.cpp
348}
349
350int64_t AudioStreamTrack::getFramesRead() {
351    aaudio_wrapping_frames_t position;
352    status_t result;
353    switch (getState()) {
354    case AAUDIO_STREAM_STATE_STARTING:
355    case AAUDIO_STREAM_STATE_STARTED:
356    case AAUDIO_STREAM_STATE_STOPPING:
357        result = mAudioTrack->getPosition(&position);
358        if (result == OK) {
359            mFramesRead.update32(position);
360        }
361        break;
362    default:
363        break;
364    }
365    return AudioStream::getFramesRead();
366}
367
368aaudio_result_t AudioStreamTrack::getTimestamp(clockid_t clockId,
369                                     int64_t *framePosition,
370                                     int64_t *timeNanoseconds) {
371    ExtendedTimestamp extendedTimestamp;
372    status_t status = mAudioTrack->getTimestamp(&extendedTimestamp);
373    if (status != NO_ERROR) {
374        return AAudioConvert_androidToAAudioResult(status);
375    }
376    // TODO Merge common code into AudioStreamLegacy after rebasing.
377    int timebase;
378    switch (clockId) {
379        case CLOCK_BOOTTIME:
380            timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
381            break;
382        case CLOCK_MONOTONIC:
383            timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
384            break;
385        default:
386            ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
387            return AAUDIO_ERROR_UNEXPECTED_VALUE;
388            break;
389    }
390    status = extendedTimestamp.getBestTimestamp(framePosition, timeNanoseconds, timebase);
391    return AAudioConvert_androidToAAudioResult(status);
392}
393