1 /*
2  * Copyright 2021 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 package androidx.benchmark.integration.macrobenchmark.target
18 
19 import android.media.AudioAttributes
20 import android.media.AudioFormat
21 import android.media.AudioTrack
22 import android.os.Build
23 import android.os.Bundle
24 import android.widget.TextView
25 import androidx.annotation.RequiresApi
26 import androidx.appcompat.app.AppCompatActivity
27 import kotlin.concurrent.thread
28 import kotlin.math.sin
29 
30 @RequiresApi(Build.VERSION_CODES.M)
31 class AudioActivity() : AppCompatActivity() {
32     private lateinit var thread: Thread
33         @Synchronized get
34         @Synchronized set
35 
36     private var finished = false
37 
onCreatenull38     override fun onCreate(savedInstanceState: Bundle?) {
39         super.onCreate(savedInstanceState)
40         setContentView(R.layout.activity_audio)
41 
42         findViewById<TextView>(R.id.audioTextNotice).setText(R.string.audio_notice)
43 
44         val sampleRateHz = 22050
45         val bufferDurationMs = 250
46         val buffer = generateBuffer(bufferDurationMs, 500, sampleRateHz)
47 
48         // plays beeps continuously until activity is destroyed
49         thread = thread {
50             var format =
51                 AudioFormat.Builder()
52                     .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
53                     .setSampleRate(sampleRateHz)
54                     .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
55                     .build()
56 
57             var attributes =
58                 AudioAttributes.Builder()
59                     .setUsage(AudioAttributes.USAGE_MEDIA)
60                     .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
61                     .build()
62 
63             val track =
64                 AudioTrack.Builder()
65                     .setAudioAttributes(attributes)
66                     .setAudioFormat(format)
67                     .setBufferSizeInBytes(buffer.size * 2)
68                     .build()
69 
70             track.play()
71 
72             while (!finished) {
73                 var currentTime = System.currentTimeMillis()
74                 track.write(buffer, 0, buffer.size)
75 
76                 // sleep twice as buffer duration to generate pauses
77                 val targetTime = currentTime + bufferDurationMs * 2
78                 Thread.sleep(targetTime - System.currentTimeMillis())
79             }
80 
81             track.stop()
82         }
83     }
84 
onDestroynull85     override fun onDestroy() {
86         finished = true
87         thread.join()
88 
89         super.onDestroy()
90     }
91 
generateBuffernull92     private fun generateBuffer(durationMs: Int, frequency: Int, sampleRateHz: Int): ShortArray {
93         val numSamples = durationMs * sampleRateHz / 1000
94 
95         val buffer = ShortArray(numSamples)
96         for (i in 0 until numSamples) {
97             val sample = sin(2 * Math.PI * i / (sampleRateHz / frequency)) * 0.1
98             buffer[i] = (sample * Short.MAX_VALUE).toInt().toShort()
99         }
100 
101         return buffer
102     }
103 }
104