Skip to content

Commit 40aba0c

Browse files
authored
docs: added section about file loading using react image component (#850)
1 parent b30bac9 commit 40aba0c

File tree

16 files changed

+262
-118
lines changed

16 files changed

+262
-118
lines changed

apps/common-app/src/examples/AudioFile/AudioFile.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import { Button, Container, Spacer } from '../../components';
1010
import { colors } from '../../styles';
1111
import AudioPlayer from './AudioPlayer';
1212

13-
const URL =
14-
'https://software-mansion.github.io/react-native-audio-api/audio/voice/example-voice-01.mp3';
13+
// const remoteAsset =
14+
// 'https://software-mansion.github.io/react-native-audio-api/audio/voice/example-voice-01.mp3';
15+
16+
import staticAsset from './voice-sample-landing.mp3';
1517

1618
const AudioFile: FC = () => {
1719
const [isPlaying, setIsPlaying] = useState(false);
@@ -56,7 +58,7 @@ const AudioFile: FC = () => {
5658
const fetchAudioBuffer = useCallback(async () => {
5759
setIsLoading(true);
5860

59-
await AudioPlayer.loadBuffer(URL);
61+
await AudioPlayer.loadBuffer(staticAsset);
6062

6163
setIsLoading(false);
6264
}, []);
@@ -83,7 +85,7 @@ const AudioFile: FC = () => {
8385
const setup = async () => {
8486
await fetchAudioBuffer();
8587
await setupNotification();
86-
}
88+
};
8789
setup();
8890
return () => {
8991
AudioPlayer.reset();
@@ -92,7 +94,6 @@ const AudioFile: FC = () => {
9294
}, [fetchAudioBuffer]);
9395

9496
useEffect(() => {
95-
9697
AudioManager.observeAudioInterruptions(true);
9798

9899
// Listen to notification control events

apps/common-app/src/examples/AudioFile/AudioPlayer.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
} from 'react-native-audio-api';
55
import {
66
AudioContext,
7+
decodeAudioData,
78
PlaybackNotificationManager,
89
} from 'react-native-audio-api';
910

@@ -36,7 +37,7 @@ class AudioPlayer {
3637
this.isPlaying = true;
3738
PlaybackNotificationManager.update({
3839
state: 'playing',
39-
})
40+
});
4041

4142
if (this.audioContext.state === 'suspended') {
4243
await this.audioContext.resume();
@@ -53,11 +54,16 @@ class AudioPlayer {
5354
this.sourceNode.onPositionChanged = (event) => {
5455
this.currentElapsedTime = event.value;
5556
if (this.onPositionChanged) {
56-
this.onPositionChanged(this.currentElapsedTime / this.audioBuffer!.duration);
57+
this.onPositionChanged(
58+
this.currentElapsedTime / this.audioBuffer!.duration
59+
);
5760
}
5861
};
5962

60-
this.sourceNode.start(this.audioContext.currentTime, this.currentElapsedTime);
63+
this.sourceNode.start(
64+
this.audioContext.currentTime,
65+
this.currentElapsedTime
66+
);
6167
};
6268

6369
pause = async () => {
@@ -71,7 +77,7 @@ class AudioPlayer {
7177
await this.audioContext.suspend();
7278
PlaybackNotificationManager.update({
7379
state: 'paused',
74-
})
80+
});
7581

7682
this.isPlaying = false;
7783
};
@@ -94,19 +100,13 @@ class AudioPlayer {
94100
}
95101
};
96102

97-
loadBuffer = async (url: string) => {
98-
const buffer = await fetch(url, {
103+
loadBuffer = async (asset: string | number) => {
104+
const buffer = await decodeAudioData(asset, 0, {
99105
headers: {
100106
'User-Agent':
101107
'Mozilla/5.0 (Android; Mobile; rv:122.0) Gecko/122.0 Firefox/122.0',
102108
},
103-
})
104-
.then((response) => response.arrayBuffer())
105-
.then((arrayBuffer) => this.audioContext.decodeAudioData(arrayBuffer))
106-
.catch((error) => {
107-
console.error('Error decoding audio data source:', error);
108-
return null;
109-
});
109+
});
110110

111111
if (buffer) {
112112
this.audioBuffer = buffer;
@@ -141,7 +141,7 @@ class AudioPlayer {
141141

142142
getElapsedTime = (): number => {
143143
return this.currentElapsedTime;
144-
}
144+
};
145145
}
146146

147147
export default new AudioPlayer();
968 KB
Binary file not shown.

apps/common-app/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module '*.mp3';

apps/fabric-example/babel.config.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@ module.exports = function (api) {
22
api.cache(false);
33
return {
44
presets: ['module:@react-native/babel-preset'],
5-
plugins: ['react-native-worklets/plugin'],
5+
plugins: [
6+
'react-native-worklets/plugin',
7+
[
8+
'module-resolver',
9+
{
10+
alias: {
11+
'common-app': '../common-app',
12+
},
13+
extensions: [
14+
'.js',
15+
'.jsx',
16+
'.ts',
17+
'.tsx',
18+
'.ios.js',
19+
'.android.js',
20+
'.json',
21+
'.ios.ts',
22+
'.android.ts',
23+
'.ios.tsx',
24+
'.android.tsx',
25+
],
26+
},
27+
],
28+
],
629
};
730
};

apps/fabric-example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3287,7 +3287,7 @@ SPEC CHECKSUMS:
32873287
FBLazyVector: 309703e71d3f2f1ed7dc7889d58309c9d77a95a4
32883288
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
32893289
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
3290-
hermes-engine: f93b5009d8ccd9429fe2a772351980df8a22a413
3290+
hermes-engine: 42d6f09ee6ede2feb220e2fb772e8bebb42ca403
32913291
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
32923292
RCTDeprecation: a41bbdd9af30bf2e5715796b313e44ec43eefff1
32933293
RCTRequired: 7be34aabb0b77c3cefe644528df0fa0afad4e4d0
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
1+
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
22

33
const path = require('path');
44

5-
const root = path.resolve(__dirname, '../..');
5+
const monorepoRoot = path.resolve(__dirname, '../..');
6+
const appsRoot = path.resolve(monorepoRoot, 'apps');
67

78
/**
89
* Metro configuration https://reactnative.dev/docs/metro
910
*
1011
* @type {import('@react-native/metro-config').MetroConfig}
1112
*/
1213
const config = {
13-
watchFolders: [root],
14+
projectRoot: __dirname,
15+
watchFolders: [monorepoRoot, appsRoot],
1416
};
1517

1618
module.exports = mergeConfig(getDefaultConfig(__dirname), config);

packages/audiodocs/docs/core/base-audio-context.mdx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -256,25 +256,13 @@ Creates [`WorkletProcessingNode`](/docs/worklets/worklet-processing-node).
256256

257257
#### Returns `WorkletProcessingNode`.
258258

259-
:::caution
260-
Supported file formats:
261-
- flac
262-
- mp3
263-
- ogg
264-
- opus
265-
- wav
266-
- aac
267-
- m4a
268-
- mp4
269-
270-
Last three formats are decoded with ffmpeg, [see for more info](/docs/other/ffmpeg-info).
271-
:::
272-
273259
### `decodeAudioData`
274260

275261
Decodes audio data from either a file path or an ArrayBuffer. The optional `sampleRate` parameter lets you resample the decoded audio.
276262
If not provided, the audio will be automatically resampled to match the audio context's `sampleRate`.
277263

264+
**For the list of supported formats visit [this page](/docs/utils/decoding).**
265+
278266
<table>
279267
<thead>
280268
<tr>
@@ -285,18 +273,22 @@ If not provided, the audio will be automatically resampled to match the audio co
285273
</thead>
286274
<tbody>
287275
<tr>
288-
<td rowspan="2" align="center"><code>input</code></td>
276+
<td rowspan="3" align="center"><code>input</code></td>
289277
<td align="center"><code>ArrayBuffer</code></td>
290278
<td align="center">ArrayBuffer with audio data.</td>
291279
</tr>
292280
<tr>
293281
<td align="center"><code>string</code></td>
294-
<td align="center">Path to audio file located on the device.</td>
282+
<td align="center">Path to remote or local audio file.</td>
295283
</tr>
296284
<tr>
297-
<td align="center"><code>sampleRate</code><Optional /></td>
298285
<td align="center"><code>number</code></td>
299-
<td align="center">Target sample rate for the decoded audio.</td>
286+
<td align="center">Asset module id. <MobileOnly/> </td>
287+
</tr>
288+
<tr>
289+
<td align="center"><code>fetchOptions</code><Optional /></td>
290+
<td align="center"><code>[RequestInit](https://github.com/facebook/react-native/blob/ac06f3bdc76a9fd7c65ab899e82bff5cad9b94b6/packages/react-native/src/types/globals.d.ts#L265)</code></td>
291+
<td align="center">Additional headers parameters when passing url to fetch.</td>
300292
</tr>
301293
</tbody>
302294
</table>

packages/audiodocs/docs/system/recording-notification-manager.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
sidebar_label: RecordingNotificationManager
23
sidebar_position: 4
34
---
45

packages/audiodocs/docs/utils/decoding.mdx

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,25 @@ import { Optional, MobileOnly } from '@site/src/components/Badges';
99
You can decode audio data independently, without creating an AudioContext, using the exported functions [`decodeAudioData`](/docs/utils/decoding#decodeaudiodata) and
1010
[`decodePCMInBase64`](/docs/utils/decoding#decodepcminbase64).
1111

12+
:::warning
13+
Decoding on the web has to be done via `AudioContext` only.
14+
:::
15+
1216
If you already have an audio context, you can decode audio data directly using its [`decodeAudioData`](/docs/core/base-audio-context#decodeaudiodata) function;
1317
the decoded audio will then be automatically resampled to match the context's `sampleRate`.
1418

1519
:::caution
1620
Supported file formats:
17-
- aac
1821
- flac
19-
- m4a
2022
- mp3
21-
- mp4
2223
- ogg
2324
- opus
2425
- wav
26+
- aac
27+
- m4a
28+
- mp4
29+
30+
Last three formats are decoded with ffmpeg on the mobile, [see for more info](/docs/other/ffmpeg-info).
2531
:::
2632

2733
### `decodeAudioData`
@@ -39,54 +45,77 @@ if not provided, the original sample rate from the file is used.
3945
</thead>
4046
<tbody>
4147
<tr>
42-
<td rowspan="2" align="center"><code>input</code></td>
48+
<td rowspan="3" align="center"><code>input</code></td>
4349
<td align="center"><code>ArrayBuffer</code></td>
4450
<td align="center">ArrayBuffer with audio data.</td>
4551
</tr>
4652
<tr>
4753
<td align="center"><code>string</code></td>
48-
<td align="center">Path to audio file located on the device.</td>
54+
<td align="center">Path to remote or local audio file.</td>
55+
</tr>
56+
<tr>
57+
<td align="center"><code>number</code></td>
58+
<td align="center">Asset module id. <MobileOnly/> </td>
4959
</tr>
5060
<tr>
5161
<td align="center"><code>sampleRate</code><Optional /></td>
5262
<td align="center"><code>number</code></td>
5363
<td align="center">Target sample rate for the decoded audio.</td>
5464
</tr>
65+
<tr>
66+
<td align="center"><code>fetchOptions</code><Optional /></td>
67+
<td align="center"><code>[RequestInit](https://github.com/facebook/react-native/blob/ac06f3bdc76a9fd7c65ab899e82bff5cad9b94b6/packages/react-native/src/types/globals.d.ts#L265)</code></td>
68+
<td align="center">Additional headers parameters when passing url to fetch.</td>
69+
</tr>
5570
</tbody>
5671
</table>
5772

5873
#### Returns `Promise<AudioBuffer>`.
5974

75+
:::caution
76+
If you are passing number to decode function, bear in mind that it uses Image component provided
77+
by React Native internally. By default only support .mp3, .wav, .mp4, .m4a, .aac audio file formats.
78+
If you want to use other types, refer to [this section](https://reactnative.dev/docs/images#static-non-image-resources) for more info.
79+
:::
80+
6081
<details>
61-
<summary>Example decoding with memory block</summary>
82+
<summary>Example decoding remote URL</summary>
6283
```tsx
84+
import { decodeAudioData } from 'react-native-audio-api';
85+
6386
const url = ... // url to an audio
6487

65-
const buffer = await fetch(url)
66-
.then((response) => response.arrayBuffer())
67-
// resample decoded audio to 48000 Hz
68-
.then((arrayBuffer) => decodeAudioData(arrayBuffer, 48000))
69-
.catch((error) => {
70-
console.error('Error decoding audio data source:', error);
71-
return null;
72-
});
88+
const buffer = await decodeAudioData(url);
7389
```
7490
</details>
7591

92+
:::caution
93+
Internally decoding local files uses Image component to retrieve asset uri, but it does not work on the web platform.
94+
You can use expo-asset library for this purpose or retrieve ArrayBuffer on your own and pass it to decoding function.
95+
:::
7696
<details>
7797
<summary>Example using expo-asset library</summary>
7898
```tsx
7999
import { Asset } from 'expo-asset';
100+
import { AudioContext } from 'react-native-audio-api';
101+
102+
const uri = await Asset.fromModule(require('@/assets/music/example.mp3'))
103+
.downloadAsync()
104+
.then((asset) => {
105+
if (!asset.localUri) {
106+
console.error('Failed to load audio asset');
107+
}
108+
return asset.localUri;
109+
})
110+
111+
const context = new AudioContext();
112+
if (uri) {
113+
const buffer = await fetch(uri)
114+
.then((response) => response.arrayBuffer())
115+
.then((arrayBuffer) => context.decodeAudioData(arrayBuffer));
116+
console.log('Audio buffer loaded:', buffer);
117+
}
80118

81-
const buffer = await Asset.fromModule(require('@/assets/music/example.mp3'))
82-
.downloadAsync()
83-
.then((asset) => {
84-
if (!asset.localUri) {
85-
throw new Error('Failed to load audio asset');
86-
}
87-
// sampleRate not provided, so file will be decoded in original sampleRate
88-
return decodeAudioData(asset.localUri);
89-
})
90119
```
91120
</details>
92121

0 commit comments

Comments
 (0)