blob: 7d1798a34685b6528d56a7b6fa1ed7c777ccbb28 [file] [log] [blame]
Don Turner0db81a42018-03-29 17:48:12 +01001/*
2 * Copyright 2018 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#include <utils/logging.h>
18#include <thread>
19
20#include "Game.h"
Don Turner0db81a42018-03-29 17:48:12 +010021
22Game::Game(AAssetManager *assetManager): mAssetManager(assetManager) {
23}
24
25void Game::start() {
26
Don Turnerf7923ae2018-04-03 22:43:27 +010027 // Load the RAW PCM data files for both the clap sound and backing track into memory.
Don Turner0db81a42018-03-29 17:48:12 +010028 mClap = SoundRecording::loadFromAssets(mAssetManager, "CLAP.raw");
29 mBackingTrack = SoundRecording::loadFromAssets(mAssetManager, "FUNKY_HOUSE.raw" );
30 mBackingTrack->setPlaying(true);
31 mBackingTrack->setLooping(true);
32
Don Turnerf7923ae2018-04-03 22:43:27 +010033 // Add the clap and backing track sounds to a mixer so that they can be played together
34 // simultaneously using a single audio stream.
Don Turner0db81a42018-03-29 17:48:12 +010035 mMixer.addTrack(mClap);
36 mMixer.addTrack(mBackingTrack);
37
Don Turnerf7923ae2018-04-03 22:43:27 +010038 // Add the audio frame numbers on which the clap sound should be played to the clap event queue.
39 // The backing track tempo is 120 beats per minute, which is 2 beats per second. At a sample
40 // rate of 48000 frames per second this means a beat occurs every 24000 frames, starting at
41 // zero. So the first 3 beats are: 0, 24000, 48000
Don Turner0db81a42018-03-29 17:48:12 +010042 mClapEvents.push(0);
43 mClapEvents.push(24000);
44 mClapEvents.push(48000);
45
Don Turnerf7923ae2018-04-03 22:43:27 +010046 // We want the user to tap on the screen exactly 4 beats after the first clap. 4 beats is
47 // 96000 frames, so we just add 96000 to the above frame numbers.
Don Turner0db81a42018-03-29 17:48:12 +010048 mClapWindows.push(96000);
49 mClapWindows.push(120000);
50 mClapWindows.push(144000);
51
Don Turnerf7923ae2018-04-03 22:43:27 +010052 // Create a builder
Don Turner0db81a42018-03-29 17:48:12 +010053 AudioStreamBuilder builder;
54 builder.setFormat(AudioFormat::I16);
55 builder.setChannelCount(2);
Don Turner7e696612018-04-09 22:11:34 +010056 builder.setSampleRate(kSampleRateHz);
Don Turner0db81a42018-03-29 17:48:12 +010057 builder.setCallback(this);
Don Turner0db81a42018-03-29 17:48:12 +010058 builder.setPerformanceMode(PerformanceMode::LowLatency);
59 builder.setSharingMode(SharingMode::Exclusive);
60
Don Turnerf7923ae2018-04-03 22:43:27 +010061 Result result = builder.openStream(&mAudioStream);
62 if (result != Result::OK){
63 LOGE("Failed to open stream. Error: %s", convertToText(result));
64 }
Don Turner0db81a42018-03-29 17:48:12 +010065
Don Turnerf7923ae2018-04-03 22:43:27 +010066 // Reduce stream latency by setting the buffer size to a multiple of the burst size
Don Turnerb7495ad2018-06-07 16:07:32 +010067 auto setBufferSizeResult = mAudioStream->setBufferSizeInFrames(
Don Turnerf7923ae2018-04-03 22:43:27 +010068 mAudioStream->getFramesPerBurst() * kBufferSizeInBursts);
Don Turnerb7495ad2018-06-07 16:07:32 +010069 if (setBufferSizeResult != Result::OK){
Don Turner75241ba2018-05-07 20:03:40 -070070 LOGW("Failed to set buffer size. Error: %s", convertToText(setBufferSizeResult.error()));
71 }
Don Turnerf7923ae2018-04-03 22:43:27 +010072
73 result = mAudioStream->requestStart();
74 if (result != Result::OK){
75 LOGE("Failed to start stream. Error: %s", convertToText(result));
76 }
Don Turner0db81a42018-03-29 17:48:12 +010077}
78
79void Game::tap(int64_t eventTimeAsUptime) {
80 mClap->setPlaying(true);
81
82 int64_t nextClapWindowFrame;
83 if (mClapWindows.pop(nextClapWindowFrame)){
84
85 int64_t frameDelta = nextClapWindowFrame - mCurrentFrame;
Don Turner7e696612018-04-09 22:11:34 +010086 int64_t timeDelta = convertFramesToMillis(frameDelta, kSampleRateHz);
Don Turner0db81a42018-03-29 17:48:12 +010087 int64_t windowTime = mLastUpdateTime + timeDelta;
88 TapResult result = getTapResult(eventTimeAsUptime, windowTime);
89 mUiEvents.push(result);
90 }
91}
92
93void Game::tick(){
94
95 TapResult r;
96 if (mUiEvents.pop(r)) {
97 renderEvent(r);
98 } else {
Don Turnerf7923ae2018-04-03 22:43:27 +010099 SetGLScreenColor(kScreenBackgroundColor);
Don Turner0db81a42018-03-29 17:48:12 +0100100 }
101}
102
103void Game::onSurfaceCreated() {
Don Turnerf7923ae2018-04-03 22:43:27 +0100104 SetGLScreenColor(kScreenBackgroundColor);
Don Turner0db81a42018-03-29 17:48:12 +0100105}
106
107void Game::onSurfaceChanged(int widthInPixels, int heightInPixels) {
108}
109
110void Game::onSurfaceDestroyed() {
111}
112
113DataCallbackResult Game::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) {
114
115 int64_t nextClapEvent;
116
117 for (int i = 0; i < numFrames; ++i) {
118
119 if (mClapEvents.peek(nextClapEvent) && mCurrentFrame == nextClapEvent){
120 mClap->setPlaying(true);
121 mClapEvents.pop(nextClapEvent);
122 }
123 mMixer.renderAudio(static_cast<int16_t*>(audioData)+(kChannelCount*i), 1);
124 mCurrentFrame++;
125 }
126
127 mLastUpdateTime = nowUptimeMillis();
128
129 return DataCallbackResult::Continue;
130}
131