@capgo/native-audio 7.1.8 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -8
- package/android/build.gradle +14 -5
- package/android/src/main/java/ee/forgr/audio/AudioAsset.java +276 -133
- package/android/src/main/java/ee/forgr/audio/AudioCompletionListener.java +1 -1
- package/android/src/main/java/ee/forgr/audio/AudioDispatcher.java +168 -182
- package/android/src/main/java/ee/forgr/audio/Constant.java +20 -21
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +596 -506
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +599 -166
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +499 -0
- package/dist/docs.json +96 -3
- package/dist/esm/definitions.d.ts +33 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +4 -3
- package/dist/esm/web.js +23 -20
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +22 -19
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +22 -19
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/AudioAsset.swift +200 -94
- package/ios/Plugin/Plugin.swift +55 -49
- package/ios/Plugin/RemoteAudioAsset.swift +217 -62
- package/package.json +18 -20
package/README.md
CHANGED
@@ -481,7 +481,7 @@ Check if an audio file is playing
|
|
481
481
|
### addListener('complete', ...)
|
482
482
|
|
483
483
|
```typescript
|
484
|
-
addListener(eventName:
|
484
|
+
addListener(eventName: 'complete', listenerFunc: CompletedListener) => Promise<PluginListenerHandle>
|
485
485
|
```
|
486
486
|
|
487
487
|
Listen for complete event
|
@@ -499,6 +499,41 @@ return {@link CompletedEvent}
|
|
499
499
|
--------------------
|
500
500
|
|
501
501
|
|
502
|
+
### addListener('currentTime', ...)
|
503
|
+
|
504
|
+
```typescript
|
505
|
+
addListener(eventName: 'currentTime', listenerFunc: CurrentTimeListener) => Promise<PluginListenerHandle>
|
506
|
+
```
|
507
|
+
|
508
|
+
Listen for current time updates
|
509
|
+
Emits every 100ms while audio is playing
|
510
|
+
|
511
|
+
| Param | Type |
|
512
|
+
| ------------------ | ------------------------------------------------------------------- |
|
513
|
+
| **`eventName`** | <code>'currentTime'</code> |
|
514
|
+
| **`listenerFunc`** | <code><a href="#currenttimelistener">CurrentTimeListener</a></code> |
|
515
|
+
|
516
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
517
|
+
|
518
|
+
**Since:** 6.5.0
|
519
|
+
return {@link CurrentTimeEvent}
|
520
|
+
|
521
|
+
--------------------
|
522
|
+
|
523
|
+
|
524
|
+
### clearCache()
|
525
|
+
|
526
|
+
```typescript
|
527
|
+
clearCache() => Promise<void>
|
528
|
+
```
|
529
|
+
|
530
|
+
Clear the audio cache for remote audio files
|
531
|
+
|
532
|
+
**Since:** 6.5.0
|
533
|
+
|
534
|
+
--------------------
|
535
|
+
|
536
|
+
|
502
537
|
### Interfaces
|
503
538
|
|
504
539
|
|
@@ -513,13 +548,13 @@ return {@link CompletedEvent}
|
|
513
548
|
|
514
549
|
#### PreloadOptions
|
515
550
|
|
516
|
-
| Prop | Type | Description
|
517
|
-
| --------------------- | -------------------- |
|
518
|
-
| **`assetPath`** | <code>string</code> | Path to the audio file, relative path of the file, absolute url (file://) or remote url (https://) |
|
519
|
-
| **`assetId`** | <code>string</code> | Asset Id, unique identifier of the file
|
520
|
-
| **`volume`** | <code>number</code> | Volume of the audio, between 0.1 and 1.0
|
521
|
-
| **`audioChannelNum`** | <code>number</code> | Audio channel number, default is 1
|
522
|
-
| **`isUrl`** | <code>boolean</code> | Is the audio file a URL, pass true if assetPath is a `file://` url
|
551
|
+
| Prop | Type | Description |
|
552
|
+
| --------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
553
|
+
| **`assetPath`** | <code>string</code> | Path to the audio file, relative path of the file, absolute url (file://) or remote url (https://) Supported formats: - MP3, WAV (all platforms) - M3U8/HLS streams (iOS and Android) |
|
554
|
+
| **`assetId`** | <code>string</code> | Asset Id, unique identifier of the file |
|
555
|
+
| **`volume`** | <code>number</code> | Volume of the audio, between 0.1 and 1.0 |
|
556
|
+
| **`audioChannelNum`** | <code>number</code> | Audio channel number, default is 1 |
|
557
|
+
| **`isUrl`** | <code>boolean</code> | Is the audio file a URL, pass true if assetPath is a `file://` url or a streaming URL (m3u8) |
|
523
558
|
|
524
559
|
|
525
560
|
#### Assets
|
@@ -543,6 +578,14 @@ return {@link CompletedEvent}
|
|
543
578
|
| **`assetId`** | <code>string</code> | Emit when a play completes | 5.0.0 |
|
544
579
|
|
545
580
|
|
581
|
+
#### CurrentTimeEvent
|
582
|
+
|
583
|
+
| Prop | Type | Description | Since |
|
584
|
+
| ----------------- | ------------------- | ------------------------------------ | ----- |
|
585
|
+
| **`currentTime`** | <code>number</code> | Current time of the audio in seconds | 6.5.0 |
|
586
|
+
| **`assetId`** | <code>string</code> | Asset Id of the audio | 6.5.0 |
|
587
|
+
|
588
|
+
|
546
589
|
### Type Aliases
|
547
590
|
|
548
591
|
|
@@ -550,4 +593,9 @@ return {@link CompletedEvent}
|
|
550
593
|
|
551
594
|
<code>(state: <a href="#completedevent">CompletedEvent</a>): void</code>
|
552
595
|
|
596
|
+
|
597
|
+
#### CurrentTimeListener
|
598
|
+
|
599
|
+
<code>(state: <a href="#currenttimeevent">CurrentTimeEvent</a>): void</code>
|
600
|
+
|
553
601
|
</docgen-api>
|
package/android/build.gradle
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
ext {
|
2
2
|
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
|
3
4
|
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
|
4
5
|
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
|
5
|
-
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
|
6
6
|
}
|
7
7
|
|
8
8
|
buildscript {
|
@@ -11,7 +11,7 @@ buildscript {
|
|
11
11
|
mavenCentral()
|
12
12
|
}
|
13
13
|
dependencies {
|
14
|
-
classpath 'com.android.tools.build:gradle:8.7.
|
14
|
+
classpath 'com.android.tools.build:gradle:8.7.3'
|
15
15
|
}
|
16
16
|
}
|
17
17
|
|
@@ -35,8 +35,10 @@ android {
|
|
35
35
|
}
|
36
36
|
lintOptions {
|
37
37
|
abortOnError false
|
38
|
-
|
39
|
-
|
38
|
+
}
|
39
|
+
compileOptions {
|
40
|
+
sourceCompatibility JavaVersion.VERSION_21
|
41
|
+
targetCompatibility JavaVersion.VERSION_21
|
40
42
|
}
|
41
43
|
}
|
42
44
|
|
@@ -47,10 +49,17 @@ repositories {
|
|
47
49
|
|
48
50
|
|
49
51
|
dependencies {
|
50
|
-
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
51
52
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
52
53
|
implementation project(':capacitor-android')
|
54
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
53
55
|
testImplementation "junit:junit:$junitVersion"
|
54
56
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
55
57
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
58
|
+
implementation 'androidx.media3:media3-exoplayer:1.5.1'
|
59
|
+
implementation 'androidx.media3:media3-exoplayer-hls:1.5.1'
|
60
|
+
implementation 'androidx.media3:media3-session:1.5.1'
|
61
|
+
implementation 'androidx.media3:media3-transformer:1.5.1'
|
62
|
+
implementation 'androidx.media3:media3-ui:1.5.1'
|
63
|
+
implementation 'androidx.media3:media3-database:1.5.1'
|
64
|
+
implementation 'androidx.media3:media3-common:1.5.1'
|
56
65
|
}
|
@@ -2,190 +2,333 @@ package ee.forgr.audio;
|
|
2
2
|
|
3
3
|
import android.content.res.AssetFileDescriptor;
|
4
4
|
import android.os.Build;
|
5
|
+
import android.os.Handler;
|
6
|
+
import android.os.Looper;
|
7
|
+
import android.util.Log;
|
5
8
|
import androidx.annotation.RequiresApi;
|
6
9
|
import java.util.ArrayList;
|
7
10
|
|
8
11
|
public class AudioAsset {
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
float volume
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
13
|
+
private final String TAG = "AudioAsset";
|
14
|
+
|
15
|
+
private final ArrayList<AudioDispatcher> audioList;
|
16
|
+
private int playIndex = 0;
|
17
|
+
protected final NativeAudio owner;
|
18
|
+
protected AudioCompletionListener completionListener;
|
19
|
+
protected String assetId;
|
20
|
+
private Handler currentTimeHandler;
|
21
|
+
private Runnable currentTimeRunnable;
|
22
|
+
private static final float FADE_STEP = 0.05f;
|
23
|
+
private static final int FADE_DELAY_MS = 80;
|
24
|
+
private float initialVolume;
|
25
|
+
|
26
|
+
AudioAsset(NativeAudio owner, String assetId, AssetFileDescriptor assetFileDescriptor, int audioChannelNum, float volume)
|
27
|
+
throws Exception {
|
28
|
+
audioList = new ArrayList<>();
|
29
|
+
this.owner = owner;
|
30
|
+
this.assetId = assetId;
|
31
|
+
this.initialVolume = volume;
|
32
|
+
|
33
|
+
if (audioChannelNum < 0) {
|
34
|
+
audioChannelNum = 1;
|
35
|
+
}
|
36
|
+
|
37
|
+
for (int x = 0; x < audioChannelNum; x++) {
|
38
|
+
AudioDispatcher audioDispatcher = new AudioDispatcher(assetFileDescriptor, volume);
|
39
|
+
audioList.add(audioDispatcher);
|
40
|
+
if (audioChannelNum == 1) audioDispatcher.setOwner(this);
|
41
|
+
}
|
42
|
+
}
|
28
43
|
|
29
|
-
|
30
|
-
|
44
|
+
public void dispatchComplete() {
|
45
|
+
this.owner.dispatchComplete(this.assetId);
|
31
46
|
}
|
32
47
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
48
|
+
public void play(Double time) throws Exception {
|
49
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
50
|
+
if (audio != null) {
|
51
|
+
audio.play(time);
|
52
|
+
playIndex++;
|
53
|
+
playIndex = playIndex % audioList.size();
|
54
|
+
Log.d(TAG, "Starting timer from play"); // Debug log
|
55
|
+
startCurrentTimeUpdates(); // Make sure this is called
|
56
|
+
} else {
|
57
|
+
throw new Exception("AudioDispatcher is null");
|
58
|
+
}
|
40
59
|
}
|
41
|
-
}
|
42
60
|
|
43
|
-
|
44
|
-
|
45
|
-
}
|
61
|
+
public double getDuration() {
|
62
|
+
if (audioList.size() != 1) return 0;
|
46
63
|
|
47
|
-
|
48
|
-
AudioDispatcher audio = audioList.get(playIndex);
|
64
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
49
65
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
} else {
|
55
|
-
throw new Exception("AudioDispatcher is null");
|
66
|
+
if (audio != null) {
|
67
|
+
return audio.getDuration();
|
68
|
+
}
|
69
|
+
return 0;
|
56
70
|
}
|
57
|
-
}
|
58
71
|
|
59
|
-
|
60
|
-
|
72
|
+
public void setCurrentPosition(double time) {
|
73
|
+
if (audioList.size() != 1) return;
|
61
74
|
|
62
|
-
|
75
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
63
76
|
|
64
|
-
|
65
|
-
|
77
|
+
if (audio != null) {
|
78
|
+
audio.setCurrentPosition(time);
|
79
|
+
}
|
66
80
|
}
|
67
|
-
return 0;
|
68
|
-
}
|
69
81
|
|
70
|
-
|
71
|
-
|
82
|
+
public double getCurrentPosition() {
|
83
|
+
if (audioList.size() != 1) return 0;
|
72
84
|
|
73
|
-
|
85
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
74
86
|
|
75
|
-
|
76
|
-
|
87
|
+
if (audio != null) {
|
88
|
+
return audio.getCurrentPosition();
|
89
|
+
}
|
90
|
+
return 0;
|
77
91
|
}
|
78
|
-
}
|
79
92
|
|
80
|
-
|
81
|
-
|
93
|
+
public boolean pause() throws Exception {
|
94
|
+
stopCurrentTimeUpdates(); // Stop updates when pausing
|
95
|
+
boolean wasPlaying = false;
|
96
|
+
|
97
|
+
for (int x = 0; x < audioList.size(); x++) {
|
98
|
+
AudioDispatcher audio = audioList.get(x);
|
99
|
+
wasPlaying |= audio.pause();
|
100
|
+
}
|
82
101
|
|
83
|
-
|
102
|
+
return wasPlaying;
|
103
|
+
}
|
84
104
|
|
85
|
-
|
86
|
-
|
105
|
+
public void resume() throws Exception {
|
106
|
+
if (!audioList.isEmpty()) {
|
107
|
+
AudioDispatcher audio = audioList.get(0);
|
108
|
+
if (audio != null) {
|
109
|
+
audio.resume();
|
110
|
+
Log.d(TAG, "Starting timer from resume"); // Debug log
|
111
|
+
startCurrentTimeUpdates(); // Make sure this is called
|
112
|
+
} else {
|
113
|
+
throw new Exception("AudioDispatcher is null");
|
114
|
+
}
|
115
|
+
}
|
87
116
|
}
|
88
|
-
return 0;
|
89
|
-
}
|
90
117
|
|
91
|
-
|
92
|
-
|
118
|
+
public void stop() throws Exception {
|
119
|
+
stopCurrentTimeUpdates(); // Stop updates when stopping
|
120
|
+
for (int x = 0; x < audioList.size(); x++) {
|
121
|
+
AudioDispatcher audio = audioList.get(x);
|
122
|
+
|
123
|
+
if (audio != null) {
|
124
|
+
audio.stop();
|
125
|
+
} else {
|
126
|
+
throw new Exception("AudioDispatcher is null");
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
93
130
|
|
94
|
-
|
95
|
-
|
96
|
-
|
131
|
+
public void loop() throws Exception {
|
132
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
133
|
+
if (audio != null) {
|
134
|
+
audio.loop();
|
135
|
+
playIndex++;
|
136
|
+
playIndex = playIndex % audioList.size();
|
137
|
+
startCurrentTimeUpdates(); // Add timer start
|
138
|
+
} else {
|
139
|
+
throw new Exception("AudioDispatcher is null");
|
140
|
+
}
|
97
141
|
}
|
98
142
|
|
99
|
-
|
100
|
-
|
143
|
+
public void unload() throws Exception {
|
144
|
+
this.stop();
|
101
145
|
|
102
|
-
|
103
|
-
|
104
|
-
AudioDispatcher audio = audioList.get(0);
|
146
|
+
for (int x = 0; x < audioList.size(); x++) {
|
147
|
+
AudioDispatcher audio = audioList.get(x);
|
105
148
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
149
|
+
if (audio != null) {
|
150
|
+
audio.unload();
|
151
|
+
} else {
|
152
|
+
throw new Exception("AudioDispatcher is null");
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
audioList.clear();
|
111
157
|
}
|
112
|
-
}
|
113
158
|
|
114
|
-
|
115
|
-
|
116
|
-
|
159
|
+
public void setVolume(float volume) throws Exception {
|
160
|
+
for (int x = 0; x < audioList.size(); x++) {
|
161
|
+
AudioDispatcher audio = audioList.get(x);
|
162
|
+
|
163
|
+
if (audio != null) {
|
164
|
+
audio.setVolume(volume);
|
165
|
+
} else {
|
166
|
+
throw new Exception("AudioDispatcher is null");
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
117
170
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
171
|
+
@RequiresApi(api = Build.VERSION_CODES.M)
|
172
|
+
public void setRate(float rate) throws Exception {
|
173
|
+
for (int x = 0; x < audioList.size(); x++) {
|
174
|
+
AudioDispatcher audio = audioList.get(x);
|
175
|
+
if (audio != null) {
|
176
|
+
audio.setRate(rate);
|
177
|
+
}
|
178
|
+
}
|
123
179
|
}
|
124
|
-
}
|
125
180
|
|
126
|
-
|
127
|
-
|
181
|
+
public boolean isPlaying() throws Exception {
|
182
|
+
if (audioList.size() != 1) return false;
|
128
183
|
|
129
|
-
|
130
|
-
audio.loop();
|
131
|
-
playIndex++;
|
132
|
-
playIndex = playIndex % audioList.size();
|
133
|
-
} else {
|
134
|
-
throw new Exception("AudioDispatcher is null");
|
184
|
+
return audioList.get(playIndex).isPlaying();
|
135
185
|
}
|
136
|
-
}
|
137
186
|
|
138
|
-
|
139
|
-
|
187
|
+
public void setCompletionListener(AudioCompletionListener listener) {
|
188
|
+
this.completionListener = listener;
|
189
|
+
}
|
140
190
|
|
141
|
-
|
142
|
-
|
191
|
+
protected void notifyCompletion() {
|
192
|
+
if (completionListener != null) {
|
193
|
+
completionListener.onCompletion(this.assetId);
|
194
|
+
}
|
195
|
+
}
|
143
196
|
|
144
|
-
|
145
|
-
|
146
|
-
} else {
|
147
|
-
throw new Exception("AudioDispatcher is null");
|
148
|
-
}
|
197
|
+
protected String getAssetId() {
|
198
|
+
return assetId;
|
149
199
|
}
|
150
200
|
|
151
|
-
|
152
|
-
|
201
|
+
public void setCurrentTime(double time) throws Exception {
|
202
|
+
owner
|
203
|
+
.getActivity()
|
204
|
+
.runOnUiThread(
|
205
|
+
new Runnable() {
|
206
|
+
@Override
|
207
|
+
public void run() {
|
208
|
+
if (audioList.size() != 1) {
|
209
|
+
return;
|
210
|
+
}
|
211
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
212
|
+
if (audio != null) {
|
213
|
+
audio.setCurrentPosition(time);
|
214
|
+
}
|
215
|
+
}
|
216
|
+
}
|
217
|
+
);
|
218
|
+
}
|
153
219
|
|
154
|
-
|
155
|
-
|
156
|
-
|
220
|
+
protected void startCurrentTimeUpdates() {
|
221
|
+
Log.d(TAG, "Starting timer updates in AudioAsset");
|
222
|
+
if (currentTimeHandler == null) {
|
223
|
+
currentTimeHandler = new Handler(Looper.getMainLooper());
|
224
|
+
}
|
225
|
+
|
226
|
+
// Add small delay to let audio start playing
|
227
|
+
currentTimeHandler.postDelayed(
|
228
|
+
new Runnable() {
|
229
|
+
@Override
|
230
|
+
public void run() {
|
231
|
+
startTimeUpdateLoop();
|
232
|
+
}
|
233
|
+
},
|
234
|
+
100
|
235
|
+
); // 100ms delay
|
236
|
+
}
|
157
237
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
238
|
+
private void startTimeUpdateLoop() {
|
239
|
+
currentTimeRunnable = new Runnable() {
|
240
|
+
@Override
|
241
|
+
public void run() {
|
242
|
+
try {
|
243
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
244
|
+
if (audio != null && audio.isPlaying()) {
|
245
|
+
double currentTime = getCurrentPosition();
|
246
|
+
Log.d(TAG, "Timer update: currentTime = " + currentTime);
|
247
|
+
owner.notifyCurrentTime(assetId, currentTime);
|
248
|
+
currentTimeHandler.postDelayed(this, 100);
|
249
|
+
} else {
|
250
|
+
Log.d(TAG, "Stopping timer - not playing");
|
251
|
+
stopCurrentTimeUpdates();
|
252
|
+
}
|
253
|
+
} catch (Exception e) {
|
254
|
+
Log.e(TAG, "Error getting current time", e);
|
255
|
+
stopCurrentTimeUpdates();
|
256
|
+
}
|
257
|
+
}
|
258
|
+
};
|
259
|
+
currentTimeHandler.post(currentTimeRunnable);
|
163
260
|
}
|
164
|
-
}
|
165
261
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
audio.setRate(rate);
|
172
|
-
}
|
262
|
+
void stopCurrentTimeUpdates() {
|
263
|
+
Log.d(TAG, "Stopping timer updates in AudioAsset");
|
264
|
+
if (currentTimeHandler != null && currentTimeRunnable != null) {
|
265
|
+
currentTimeHandler.removeCallbacks(currentTimeRunnable);
|
266
|
+
}
|
173
267
|
}
|
174
|
-
}
|
175
268
|
|
176
|
-
|
177
|
-
|
269
|
+
public void playWithFade(Double time) throws Exception {
|
270
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
271
|
+
if (audio != null) {
|
272
|
+
audio.setVolume(0);
|
273
|
+
audio.play(time);
|
274
|
+
fadeIn(audio);
|
275
|
+
startCurrentTimeUpdates();
|
276
|
+
}
|
277
|
+
}
|
178
278
|
|
179
|
-
|
180
|
-
|
279
|
+
private void fadeIn(final AudioDispatcher audio) {
|
280
|
+
final Handler handler = new Handler(Looper.getMainLooper());
|
281
|
+
final Runnable fadeRunnable = new Runnable() {
|
282
|
+
float currentVolume = 0;
|
283
|
+
|
284
|
+
@Override
|
285
|
+
public void run() {
|
286
|
+
if (currentVolume < initialVolume) {
|
287
|
+
currentVolume += FADE_STEP;
|
288
|
+
try {
|
289
|
+
audio.setVolume(currentVolume);
|
290
|
+
handler.postDelayed(this, FADE_DELAY_MS);
|
291
|
+
} catch (Exception e) {
|
292
|
+
Log.e(TAG, "Error during fade in", e);
|
293
|
+
}
|
294
|
+
}
|
295
|
+
}
|
296
|
+
};
|
297
|
+
handler.post(fadeRunnable);
|
298
|
+
}
|
181
299
|
|
182
|
-
|
183
|
-
|
184
|
-
|
300
|
+
public void stopWithFade() throws Exception {
|
301
|
+
AudioDispatcher audio = audioList.get(playIndex);
|
302
|
+
if (audio != null && audio.isPlaying()) {
|
303
|
+
fadeOut(audio);
|
304
|
+
}
|
305
|
+
}
|
185
306
|
|
186
|
-
|
187
|
-
|
188
|
-
|
307
|
+
private void fadeOut(final AudioDispatcher audio) {
|
308
|
+
final Handler handler = new Handler(Looper.getMainLooper());
|
309
|
+
final Runnable fadeRunnable = new Runnable() {
|
310
|
+
float currentVolume = initialVolume;
|
311
|
+
|
312
|
+
@Override
|
313
|
+
public void run() {
|
314
|
+
if (currentVolume > FADE_STEP) {
|
315
|
+
currentVolume -= FADE_STEP;
|
316
|
+
try {
|
317
|
+
audio.setVolume(currentVolume);
|
318
|
+
handler.postDelayed(this, FADE_DELAY_MS);
|
319
|
+
} catch (Exception e) {
|
320
|
+
Log.e(TAG, "Error during fade out", e);
|
321
|
+
}
|
322
|
+
} else {
|
323
|
+
try {
|
324
|
+
audio.setVolume(0);
|
325
|
+
stop();
|
326
|
+
} catch (Exception e) {
|
327
|
+
Log.e(TAG, "Error stopping after fade", e);
|
328
|
+
}
|
329
|
+
}
|
330
|
+
}
|
331
|
+
};
|
332
|
+
handler.post(fadeRunnable);
|
189
333
|
}
|
190
|
-
}
|
191
334
|
}
|