AudioStreamTrack.cpp revision e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3
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 "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
146    return AAUDIO_OK;
147}
148
149aaudio_result_t AudioStreamTrack::close()
150{
151    // TODO maybe add close() or release() to AudioTrack API then call it from here
152    if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
153        mAudioTrack.clear(); // TODO is this right?
154        setState(AAUDIO_STREAM_STATE_CLOSED);
155    }
156    mFixedBlockReader.close();
157    return AAUDIO_OK;
158}
159
160void AudioStreamTrack::processCallback(int event, void *info) {
161
162    switch (event) {
163        case AudioTrack::EVENT_MORE_DATA:
164            processCallbackCommon(AAUDIO_CALLBACK_OPERATION_PROCESS_DATA, info);
165            break;
166
167            // Stream got rerouted so we disconnect.
168        case AudioTrack::EVENT_NEW_IAUDIOTRACK:
169            processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
170            break;
171
172        default:
173            break;
174    }
175    return;
176}
177
178aaudio_result_t AudioStreamTrack::requestStart()
179{
180    std::lock_guard<std::mutex> lock(mStreamMutex);
181
182    if (mAudioTrack.get() == nullptr) {
183        return AAUDIO_ERROR_INVALID_STATE;
184    }
185    // Get current position so we can detect when the track is playing.
186    status_t err = mAudioTrack->getPosition(&mPositionWhenStarting);
187    if (err != OK) {
188        return AAudioConvert_androidToAAudioResult(err);
189    }
190
191    err = mAudioTrack->start();
192    if (err != OK) {
193        return AAudioConvert_androidToAAudioResult(err);
194    } else {
195        setState(AAUDIO_STREAM_STATE_STARTING);
196    }
197    return AAUDIO_OK;
198}
199
200aaudio_result_t AudioStreamTrack::requestPause()
201{
202    std::lock_guard<std::mutex> lock(mStreamMutex);
203
204    if (mAudioTrack.get() == nullptr) {
205        return AAUDIO_ERROR_INVALID_STATE;
206    } else if (getState() != AAUDIO_STREAM_STATE_STARTING
207            && getState() != AAUDIO_STREAM_STATE_STARTED) {
208        ALOGE("requestPause(), called when state is %s",
209              AAudio_convertStreamStateToText(getState()));
210        return AAUDIO_ERROR_INVALID_STATE;
211    }
212    setState(AAUDIO_STREAM_STATE_PAUSING);
213    mAudioTrack->pause();
214    status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
215    if (err != OK) {
216        return AAudioConvert_androidToAAudioResult(err);
217    }
218    return AAUDIO_OK;
219}
220
221aaudio_result_t AudioStreamTrack::requestFlush() {
222    std::lock_guard<std::mutex> lock(mStreamMutex);
223
224    if (mAudioTrack.get() == nullptr) {
225        return AAUDIO_ERROR_INVALID_STATE;
226    } else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
227        return AAUDIO_ERROR_INVALID_STATE;
228    }
229    setState(AAUDIO_STREAM_STATE_FLUSHING);
230    incrementFramesRead(getFramesWritten() - getFramesRead());
231    mAudioTrack->flush();
232    mFramesWritten.reset32();
233    return AAUDIO_OK;
234}
235
236aaudio_result_t AudioStreamTrack::requestStop() {
237    std::lock_guard<std::mutex> lock(mStreamMutex);
238
239    if (mAudioTrack.get() == nullptr) {
240        return AAUDIO_ERROR_INVALID_STATE;
241    }
242    setState(AAUDIO_STREAM_STATE_STOPPING);
243    incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
244    mAudioTrack->stop();
245    mFramesWritten.reset32();
246    return AAUDIO_OK;
247}
248
249aaudio_result_t AudioStreamTrack::updateStateWhileWaiting()
250{
251    status_t err;
252    aaudio_wrapping_frames_t position;
253    switch (getState()) {
254    // TODO add better state visibility to AudioTrack
255    case AAUDIO_STREAM_STATE_STARTING:
256        if (mAudioTrack->hasStarted()) {
257            setState(AAUDIO_STREAM_STATE_STARTED);
258        }
259        break;
260    case AAUDIO_STREAM_STATE_PAUSING:
261        if (mAudioTrack->stopped()) {
262            err = mAudioTrack->getPosition(&position);
263            if (err != OK) {
264                return AAudioConvert_androidToAAudioResult(err);
265            } else if (position == mPositionWhenPausing) {
266                // Has stream really stopped advancing?
267                setState(AAUDIO_STREAM_STATE_PAUSED);
268            }
269            mPositionWhenPausing = position;
270        }
271        break;
272    case AAUDIO_STREAM_STATE_FLUSHING:
273        {
274            err = mAudioTrack->getPosition(&position);
275            if (err != OK) {
276                return AAudioConvert_androidToAAudioResult(err);
277            } else if (position == 0) {
278                // Advance frames read to match written.
279                setState(AAUDIO_STREAM_STATE_FLUSHED);
280            }
281        }
282        break;
283    case AAUDIO_STREAM_STATE_STOPPING:
284        if (mAudioTrack->stopped()) {
285            setState(AAUDIO_STREAM_STATE_STOPPED);
286        }
287        break;
288    default:
289        break;
290    }
291    return AAUDIO_OK;
292}
293
294aaudio_result_t AudioStreamTrack::write(const void *buffer,
295                                      int32_t numFrames,
296                                      int64_t timeoutNanoseconds)
297{
298    int32_t bytesPerFrame = getBytesPerFrame();
299    int32_t numBytes;
300    aaudio_result_t result = AAudioConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
301    if (result != AAUDIO_OK) {
302        return result;
303    }
304
305    // TODO add timeout to AudioTrack
306    bool blocking = timeoutNanoseconds > 0;
307    ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
308    if (bytesWritten == WOULD_BLOCK) {
309        return 0;
310    } else if (bytesWritten < 0) {
311        ALOGE("invalid write, returned %d", (int)bytesWritten);
312        return AAudioConvert_androidToAAudioResult(bytesWritten);
313    }
314    int32_t framesWritten = (int32_t)(bytesWritten / bytesPerFrame);
315    incrementFramesWritten(framesWritten);
316    return framesWritten;
317}
318
319aaudio_result_t AudioStreamTrack::setBufferSize(int32_t requestedFrames)
320{
321    ssize_t result = mAudioTrack->setBufferSizeInFrames(requestedFrames);
322    if (result < 0) {
323        return AAudioConvert_androidToAAudioResult(result);
324    } else {
325        return result;
326    }
327}
328
329int32_t AudioStreamTrack::getBufferSize() const
330{
331    return static_cast<int32_t>(mAudioTrack->getBufferSizeInFrames());
332}
333
334int32_t AudioStreamTrack::getBufferCapacity() const
335{
336    return static_cast<int32_t>(mAudioTrack->frameCount());
337}
338
339int32_t AudioStreamTrack::getXRunCount() const
340{
341    return static_cast<int32_t>(mAudioTrack->getUnderrunCount());
342}
343
344int32_t AudioStreamTrack::getFramesPerBurst() const
345{
346    return 192; // TODO add query to AudioTrack.cpp
347}
348
349int64_t AudioStreamTrack::getFramesRead() {
350    aaudio_wrapping_frames_t position;
351    status_t result;
352    switch (getState()) {
353    case AAUDIO_STREAM_STATE_STARTING:
354    case AAUDIO_STREAM_STATE_STARTED:
355    case AAUDIO_STREAM_STATE_STOPPING:
356        result = mAudioTrack->getPosition(&position);
357        if (result == OK) {
358            mFramesRead.update32(position);
359        }
360        break;
361    default:
362        break;
363    }
364    return AudioStream::getFramesRead();
365}
366
367aaudio_result_t AudioStreamTrack::getTimestamp(clockid_t clockId,
368                                     int64_t *framePosition,
369                                     int64_t *timeNanoseconds) {
370    ExtendedTimestamp extendedTimestamp;
371    status_t status = mAudioTrack->getTimestamp(&extendedTimestamp);
372    if (status != NO_ERROR) {
373        return AAudioConvert_androidToAAudioResult(status);
374    }
375    // TODO Merge common code into AudioStreamLegacy after rebasing.
376    int timebase;
377    switch (clockId) {
378        case CLOCK_BOOTTIME:
379            timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
380            break;
381        case CLOCK_MONOTONIC:
382            timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
383            break;
384        default:
385            ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
386            return AAUDIO_ERROR_UNEXPECTED_VALUE;
387            break;
388    }
389    status = extendedTimestamp.getBestTimestamp(framePosition, timeNanoseconds, timebase);
390    return AAudioConvert_androidToAAudioResult(status);
391}
392