1/*
2 * Copyright (C) 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// Play sine waves using AAudio.
18
19#include <aaudio/AAudio.h>
20#include <aaudio/AAudioTesting.h>
21#include <asm/fcntl.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26
27#include "AAudioExampleUtils.h"
28#include "AAudioSimplePlayer.h"
29#include "AAudioArgsParser.h"
30
31#define NUM_SECONDS           4
32
33int main(int argc, const char **argv)
34{
35    AAudioArgsParser   argParser;
36    AAudioSimplePlayer player;
37    SineThreadedData_t myData;
38    aaudio_result_t    result = AAUDIO_OK;
39
40    int32_t         actualChannelCount = 0;
41    int32_t         actualSampleRate = 0;
42    aaudio_format_t actualDataFormat = AAUDIO_FORMAT_UNSPECIFIED;
43
44    AAudioStream *aaudioStream = nullptr;
45    int32_t  framesPerBurst = 0;
46    int32_t  framesPerWrite = 0;
47    int32_t  bufferCapacity = 0;
48    int32_t  framesToPlay = 0;
49    int32_t  framesLeft = 0;
50    int32_t  xRunCount = 0;
51    float   *floatData = nullptr;
52    int16_t *shortData = nullptr;
53
54    int      testFd = -1;
55
56    // Make printf print immediately so that debug info is not stuck
57    // in a buffer if we hang or crash.
58    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
59
60    printf("%s - Play a sine wave using AAudio V0.1.2\n", argv[0]);
61
62    if (argParser.parseArgs(argc, argv)) {
63        return EXIT_FAILURE;
64    }
65
66    result = player.open(argParser);
67    if (result != AAUDIO_OK) {
68        fprintf(stderr, "ERROR -  player.open() returned %d\n", result);
69        goto finish;
70    }
71
72    aaudioStream = player.getStream();
73
74    argParser.compareWithStream(aaudioStream);
75
76    actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
77    actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
78    actualDataFormat = AAudioStream_getFormat(aaudioStream);
79
80    myData.sineOsc1.setup(440.0, actualSampleRate);
81    myData.sineOsc2.setup(660.0, actualSampleRate);
82
83    // Some DMA might use very short bursts of 16 frames. We don't need to write such small
84    // buffers. But it helps to use a multiple of the burst size for predictable scheduling.
85    framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
86    framesPerWrite = framesPerBurst;
87    while (framesPerWrite < 48) {
88        framesPerWrite *= 2;
89    }
90    printf("Buffer: framesPerBurst = %d\n",framesPerBurst);
91    printf("Buffer: framesPerWrite = %d\n",framesPerWrite);
92
93    // Allocate a buffer for the audio data.
94    if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
95        floatData = new float[framesPerWrite * actualChannelCount];
96    } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
97        shortData = new int16_t[framesPerWrite * actualChannelCount];
98    } else {
99        printf("ERROR Unsupported data format!\n");
100        goto finish;
101    }
102
103    testFd = open("/data/aaudio_temp.raw", O_CREAT | O_RDWR, S_IRWXU);
104    printf("testFd = %d, pid = %d\n", testFd, getpid());
105
106    // Start the stream.
107    printf("call player.start()\n");
108    result = player.start();
109    if (result != AAUDIO_OK) {
110        fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d\n", result);
111        goto finish;
112    }
113
114    printf("after start, state = %s\n",
115            AAudio_convertStreamStateToText(AAudioStream_getState(aaudioStream)));
116
117    // Play for a while.
118    framesToPlay = actualSampleRate * argParser.getDurationSeconds();
119    framesLeft = framesToPlay;
120    while (framesLeft > 0) {
121
122        if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
123            // Render sine waves to left and right channels.
124            myData.sineOsc1.render(&floatData[0], actualChannelCount, framesPerWrite);
125            if (actualChannelCount > 1) {
126                myData.sineOsc2.render(&floatData[1], actualChannelCount, framesPerWrite);
127            }
128        } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
129            // Render sine waves to left and right channels.
130            myData.sineOsc1.render(&shortData[0], actualChannelCount, framesPerWrite);
131            if (actualChannelCount > 1) {
132                myData.sineOsc2.render(&shortData[1], actualChannelCount, framesPerWrite);
133            }
134        }
135
136        // Write audio data to the stream.
137        int64_t timeoutNanos = 1000 * NANOS_PER_MILLISECOND;
138        int32_t minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
139        int32_t actual = 0;
140        if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
141            actual = AAudioStream_write(aaudioStream, floatData, minFrames, timeoutNanos);
142        } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
143            actual = AAudioStream_write(aaudioStream, shortData, minFrames, timeoutNanos);
144        }
145        if (actual < 0) {
146            fprintf(stderr, "ERROR - AAudioStream_write() returned %d\n", actual);
147            goto finish;
148        } else if (actual == 0) {
149            fprintf(stderr, "WARNING - AAudioStream_write() returned %d\n", actual);
150            goto finish;
151        }
152        framesLeft -= actual;
153
154        // Use timestamp to estimate latency.
155        /*
156        {
157            int64_t presentationFrame;
158            int64_t presentationTime;
159            result = AAudioStream_getTimestamp(aaudioStream,
160                                               CLOCK_MONOTONIC,
161                                               &presentationFrame,
162                                               &presentationTime
163                                               );
164            if (result == AAUDIO_OK) {
165                int64_t elapsedNanos = getNanoseconds() - presentationTime;
166                int64_t elapsedFrames = actualSampleRate * elapsedNanos / NANOS_PER_SECOND;
167                int64_t currentFrame = presentationFrame + elapsedFrames;
168                int64_t framesWritten = AAudioStream_getFramesWritten(aaudioStream);
169                int64_t estimatedLatencyFrames = framesWritten - currentFrame;
170                int64_t estimatedLatencyMillis = estimatedLatencyFrames * 1000 / actualSampleRate;
171                printf("estimatedLatencyMillis %d\n", (int)estimatedLatencyMillis);
172            }
173        }
174         */
175    }
176
177    xRunCount = AAudioStream_getXRunCount(aaudioStream);
178    printf("AAudioStream_getXRunCount %d\n", xRunCount);
179
180    printf("call stop()\n");
181    result = player.stop();
182    if (result != AAUDIO_OK) {
183        goto finish;
184    }
185
186finish:
187    printf("testFd = %d, fcntl before aaudio close returns 0x%08X\n",
188           testFd, fcntl(testFd, F_GETFD));
189    player.close();
190    printf("testFd = %d, fcntl after aaudio close returns 0x%08X\n",
191           testFd, fcntl(testFd, F_GETFD));
192    if (::close(testFd) != 0) {
193        printf("ERROR SharedMemoryParcelable::close() of testFd = %d, errno = %s\n",
194               testFd, strerror(errno));
195    }
196    printf("testFd = %d, fcntl after close() returns 0x%08X\n", testFd, fcntl(testFd, F_GETFD));
197
198    delete[] floatData;
199    delete[] shortData;
200    printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
201    return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
202}
203
204