AudioStream.cpp revision 17fff38dd9d467bc5fb6cd5b9a6b183951c7750d
1/*
2 * Copyright 2015 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 "AAudio"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <atomic>
22#include <stdint.h>
23#include <aaudio/AAudio.h>
24
25#include "AudioStreamBuilder.h"
26#include "AudioStream.h"
27#include "AudioClock.h"
28
29using namespace aaudio;
30
31AudioStream::AudioStream()
32        : mCallbackEnabled(false)
33{
34    // mThread is a pthread_t of unknown size so we need memset.
35    memset(&mThread, 0, sizeof(mThread));
36    setPeriodNanoseconds(0);
37}
38
39aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
40{
41    // Copy parameters from the Builder because the Builder may be deleted after this call.
42    mSamplesPerFrame = builder.getSamplesPerFrame();
43    mSampleRate = builder.getSampleRate();
44    mDeviceId = builder.getDeviceId();
45    mFormat = builder.getFormat();
46    mSharingMode = builder.getSharingMode();
47    mSharingModeMatchRequired = builder.isSharingModeMatchRequired();
48
49    mPerformanceMode = builder.getPerformanceMode();
50
51    // callbacks
52    mFramesPerDataCallback = builder.getFramesPerDataCallback();
53    mDataCallbackProc = builder.getDataCallbackProc();
54    mErrorCallbackProc = builder.getErrorCallbackProc();
55    mDataCallbackUserData = builder.getDataCallbackUserData();
56    mErrorCallbackUserData = builder.getErrorCallbackUserData();
57
58    // This is very helpful for debugging in the future.
59    ALOGI("AudioStream.open(): rate = %d, channels = %d, format = %d, sharing = %d",
60          mSampleRate, mSamplesPerFrame, mFormat, mSharingMode);
61
62    // Check for values that are ridiculously out of range to prevent math overflow exploits.
63    // The service will do a better check.
64    if (mSamplesPerFrame < 0 || mSamplesPerFrame > 128) {
65        ALOGE("AudioStream::open(): samplesPerFrame out of range = %d", mSamplesPerFrame);
66        return AAUDIO_ERROR_OUT_OF_RANGE;
67    }
68
69    switch(mFormat) {
70        case AAUDIO_FORMAT_UNSPECIFIED:
71        case AAUDIO_FORMAT_PCM_I16:
72        case AAUDIO_FORMAT_PCM_FLOAT:
73            break; // valid
74        default:
75            ALOGE("AudioStream::open(): audioFormat not valid = %d", mFormat);
76            return AAUDIO_ERROR_INVALID_FORMAT;
77            // break;
78    }
79
80    if (mSampleRate != AAUDIO_UNSPECIFIED && (mSampleRate < 8000 || mSampleRate > 1000000)) {
81        ALOGE("AudioStream::open(): mSampleRate out of range = %d", mSampleRate);
82        return AAUDIO_ERROR_INVALID_RATE;
83    }
84
85    switch(mPerformanceMode) {
86        case AAUDIO_PERFORMANCE_MODE_NONE:
87        case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
88        case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
89            break;
90        default:
91            ALOGE("AudioStream::open(): illegal performanceMode %d", mPerformanceMode);
92            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
93    }
94
95    return AAUDIO_OK;
96}
97
98AudioStream::~AudioStream() {
99    close();
100}
101
102aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
103                                                aaudio_stream_state_t *nextState,
104                                                int64_t timeoutNanoseconds)
105{
106    aaudio_result_t result = updateStateWhileWaiting();
107    if (result != AAUDIO_OK) {
108        return result;
109    }
110
111    int64_t durationNanos = 20 * AAUDIO_NANOS_PER_MILLISECOND; // arbitrary
112    aaudio_stream_state_t state = getState();
113    while (state == currentState && timeoutNanoseconds > 0) {
114        if (durationNanos > timeoutNanoseconds) {
115            durationNanos = timeoutNanoseconds;
116        }
117        AudioClock::sleepForNanos(durationNanos);
118        timeoutNanoseconds -= durationNanos;
119
120        aaudio_result_t result = updateStateWhileWaiting();
121        if (result != AAUDIO_OK) {
122            return result;
123        }
124
125        state = getState();
126    }
127    if (nextState != nullptr) {
128        *nextState = state;
129    }
130    return (state == currentState) ? AAUDIO_ERROR_TIMEOUT : AAUDIO_OK;
131}
132
133// This registers the callback thread with the server before
134// passing control to the app. This gives the server an opportunity to boost
135// the thread's performance characteristics.
136void* AudioStream::wrapUserThread() {
137    void* procResult = nullptr;
138    mThreadRegistrationResult = registerThread();
139    if (mThreadRegistrationResult == AAUDIO_OK) {
140        // Run callback loop. This may take a very long time.
141        procResult = mThreadProc(mThreadArg);
142        mThreadRegistrationResult = unregisterThread();
143    }
144    return procResult;
145}
146
147// This is the entry point for the new thread created by createThread().
148// It converts the 'C' function call to a C++ method call.
149static void* AudioStream_internalThreadProc(void* threadArg) {
150    AudioStream *audioStream = (AudioStream *) threadArg;
151    return audioStream->wrapUserThread();
152}
153
154// This is not exposed in the API.
155// But it is still used internally to implement callbacks for MMAP mode.
156aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds,
157                                     aaudio_audio_thread_proc_t threadProc,
158                                     void* threadArg)
159{
160    if (mHasThread) {
161        return AAUDIO_ERROR_INVALID_STATE;
162    }
163    if (threadProc == nullptr) {
164        return AAUDIO_ERROR_NULL;
165    }
166    // Pass input parameters to the background thread.
167    mThreadProc = threadProc;
168    mThreadArg = threadArg;
169    setPeriodNanoseconds(periodNanoseconds);
170    int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
171    if (err != 0) {
172        return AAudioConvert_androidToAAudioResult(-errno);
173    } else {
174        mHasThread = true;
175        return AAUDIO_OK;
176    }
177}
178
179aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds)
180{
181    if (!mHasThread) {
182        return AAUDIO_ERROR_INVALID_STATE;
183    }
184#if 0
185    // TODO implement equivalent of pthread_timedjoin_np()
186    struct timespec abstime;
187    int err = pthread_timedjoin_np(mThread, returnArg, &abstime);
188#else
189    int err = pthread_join(mThread, returnArg);
190#endif
191    mHasThread = false;
192    return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult;
193}
194
195