diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 48eaa5779..2462a4189 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,6 +2,12 @@ Closes # +## ⚠️ Breaking changes ⚠️ + + + +- + ## Introduced changes diff --git a/README.md b/README.md index ecfc4b74e..33982a5af 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,13 @@ check out the [Getting Started](https://docs.swmansion.com/react-native-audio-ap Ability to modify playback speed without affecting pitch of the sound
-- [![Released in 0.6.0](https://img.shields.io/badge/Released_in-0.6.0-green)](https://github.com/software-mansion/react-native-audio-api/releases/tag/0.6.0)
**System configuration** 🛠️
+- [![Released in 0.6.0](https://img.shields.io/badge/Released_in-0.6.0-green)](https://github.com/software-mansion/react-native-audio-api/releases/tag/0.6.0) **System configuration** 🛠️
Full control of system audio settings, remote controls, lock screen integration and most importantly configurable background modes
-
**Microphone support** 🎙️
+ +- **Microphone support** 🎙️
Grab audio data from device microphone or connected device, connect it to the audio graph or stream through the internet
-
**Connect audio param** 🤞
+ +- **Connect audio param** 🤞
Ability to connect Audio nodes to audio params, which will allow for powerful and efficient modulation of audio parameters, creating effects like tremolo, vibrato or complex envelope followers.
- **JS Audio Worklets** 🐎
diff --git a/apps/common-app/package.json b/apps/common-app/package.json index 392c3dd90..74060edd5 100644 --- a/apps/common-app/package.json +++ b/apps/common-app/package.json @@ -7,36 +7,36 @@ "react-native": "*" }, "dependencies": { - "@react-native-vector-icons/common": "^11.0.0", - "@react-native-vector-icons/icomoon": "^0.0.1", - "@react-navigation/native": "^7.0.15", - "@react-navigation/native-stack": "^7.2.1", - "@react-navigation/stack": "^7.1.2", - "@shopify/react-native-skia": "1.11.11", + "@react-native-vector-icons/common": "12.0.1", + "@react-native-vector-icons/icomoon": "12.0.1", + "@react-navigation/native": "7.1.13", + "@react-navigation/native-stack": "7.3.18", + "@react-navigation/stack": "7.3.6", + "@shopify/react-native-skia": "2.0.5", "react-native-audio-api": "workspace:*", "react-native-dotenv": "3.4.11", - "react-native-gesture-handler": "2.24.0", - "react-native-reanimated": "3.17.1", - "react-native-safe-area-context": "5.3.0", - "react-native-screens": "4.9.1" + "react-native-gesture-handler": "2.26.0", + "react-native-reanimated": "3.18.0", + "react-native-safe-area-context": "5.4.1", + "react-native-screens": "4.11.1" }, "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.2", "@babel/runtime": "^7.25.0", - "@react-native/babel-preset": "0.77.1", - "@react-native/eslint-config": "0.77.1", - "@react-native/metro-config": "0.77.1", - "@react-native/typescript-config": "0.77.1", + "@react-native/babel-preset": "0.80.0", + "@react-native/eslint-config": "0.80.0", + "@react-native/metro-config": "0.80.0", + "@react-native/typescript-config": "0.80.0", "@types/jest": "^29.5.13", - "@types/react": "^18.2.6", - "@types/react-test-renderer": "^18.0.0", + "@types/react": "^19.1.0", + "@types/react-test-renderer": "^19.1.0", "eslint": "^8.57.0", "jest": "^29.6.3", "prettier": "^3.3.3", - "react": "18.3.1", - "react-native": "0.77.1", - "react-test-renderer": "18.3.1", + "react": "19.1.0", + "react-native": "0.80.0", + "react-test-renderer": "19.1.0", "typescript": "~5.3.0" } } diff --git a/apps/common-app/src/examples/AudioFile/AudioFile.tsx b/apps/common-app/src/examples/AudioFile/AudioFile.tsx index 7c1e10dca..7a6003cd8 100644 --- a/apps/common-app/src/examples/AudioFile/AudioFile.tsx +++ b/apps/common-app/src/examples/AudioFile/AudioFile.tsx @@ -1,93 +1,23 @@ -import React, { useCallback, useEffect, useRef, useState, FC } from 'react'; +import React, { useCallback, useEffect, useState, FC } from 'react'; import { ActivityIndicator } from 'react-native'; -import { - AudioBuffer, - AudioContext, - AudioBufferSourceNode, - AudioManager, -} from 'react-native-audio-api'; - -import { Container, Button, Spacer, Slider } from '../../components'; +import { AudioManager } from 'react-native-audio-api'; +import { Container, Button } from '../../components'; +import AudioPlayer from './AudioPlayer'; const URL = 'https://software-mansion.github.io/react-native-audio-api/audio/voice/example-voice-01.mp3'; -const INITIAL_RATE = 1; -const INITIAL_DETUNE = 0; - -const labelWidth = 80; - const AudioFile: FC = () => { const [isPlaying, setIsPlaying] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [offset, setOffset] = useState(0); - const [playbackRate, setPlaybackRate] = useState(INITIAL_RATE); - const [detune, setDetune] = useState(INITIAL_DETUNE); - - const [audioBuffer, setAudioBuffer] = useState(null); - - const audioContextRef = useRef(null); - const bufferSourceRef = useRef(null); - - const handlePlaybackRateChange = (newValue: number) => { - setPlaybackRate(newValue); - - if (bufferSourceRef.current) { - bufferSourceRef.current.playbackRate.value = newValue; - } - }; - - const handleDetuneChange = (newValue: number) => { - setDetune(newValue); - - if (bufferSourceRef.current) { - bufferSourceRef.current.detune.value = newValue; - } - }; - - const handlePress = async () => { - if (!audioContextRef.current) { - return; - } - + const togglePlayPause = async () => { if (isPlaying) { - bufferSourceRef.current?.stop(audioContextRef.current.currentTime); - AudioManager.setLockScreenInfo({ - state: 'state_paused', - }); - - setTimeout(async () => { - await audioContextRef.current?.suspend(); - }, 5); + await AudioPlayer.pause(); } else { - if (!audioBuffer) { - fetchAudioBuffer(); - } - - await audioContextRef.current.resume(); - - AudioManager.setLockScreenInfo({ - state: 'state_playing', - }); + await AudioPlayer.play(); AudioManager.observeAudioInterruptions(true); - - bufferSourceRef.current = audioContextRef.current.createBufferSource({ - pitchCorrection: true, - }); - bufferSourceRef.current.buffer = audioBuffer; - bufferSourceRef.current.onended = (event) => { - setOffset((_prev) => event.value || 0); - }; - bufferSourceRef.current.playbackRate.value = playbackRate; - bufferSourceRef.current.detune.value = detune; - bufferSourceRef.current.connect(audioContextRef.current.destination); - - bufferSourceRef.current.start( - audioContextRef.current.currentTime, - offset - ); } setIsPlaying((prev) => !prev); @@ -96,26 +26,12 @@ const AudioFile: FC = () => { const fetchAudioBuffer = useCallback(async () => { setIsLoading(true); - const buffer = await fetch(URL) - .then((response) => response.arrayBuffer()) - .then((arrayBuffer) => - audioContextRef.current!.decodeAudioData(arrayBuffer) - ) - .catch((error) => { - console.error('Error decoding audio data source:', error); - return null; - }); - - setAudioBuffer(buffer); + await AudioPlayer.loadBuffer(URL); setIsLoading(false); }, []); useEffect(() => { - if (!audioContextRef.current) { - audioContextRef.current = new AudioContext({ initSuspended: true }); - } - AudioManager.setLockScreenInfo({ title: 'Audio file', artist: 'Software Mansion', @@ -125,30 +41,37 @@ const AudioFile: FC = () => { AudioManager.enableRemoteCommand('remotePlay', true); AudioManager.enableRemoteCommand('remotePause', true); - AudioManager.enableRemoteCommand('remoteChangePlaybackPosition', true); + AudioManager.enableRemoteCommand('remoteSkipForward', true); + AudioManager.enableRemoteCommand('remoteSkipBackward', true); AudioManager.observeAudioInterruptions(true); const remotePlaySubscription = AudioManager.addSystemEventListener( 'remotePlay', - (event) => { - console.log('remotePlay event:', event); + () => { + AudioPlayer.play(); } ); const remotePauseSubscription = AudioManager.addSystemEventListener( 'remotePause', + () => { + AudioPlayer.pause(); + } + ); + + const remoteSkipForwardSubscription = AudioManager.addSystemEventListener( + 'remoteSkipForward', (event) => { - console.log('remotePause event:', event); + AudioPlayer.seekBy(event.value); } ); - const remoteChangePlaybackPositionSubscription = - AudioManager.addSystemEventListener( - 'remoteChangePlaybackPosition', - (event) => { - console.log('remoteChangePlaybackPosition event:', event); - } - ); + const remoteSkipBackwardSubscription = AudioManager.addSystemEventListener( + 'remoteSkipBackward', + (event) => { + AudioPlayer.seekBy(-event.value); + } + ); const interruptionSubscription = AudioManager.addSystemEventListener( 'interruption', @@ -162,10 +85,11 @@ const AudioFile: FC = () => { return () => { remotePlaySubscription?.remove(); remotePauseSubscription?.remove(); - remoteChangePlaybackPositionSubscription?.remove(); + remoteSkipForwardSubscription?.remove(); + remoteSkipBackwardSubscription?.remove(); interruptionSubscription?.remove(); - audioContextRef.current?.close(); AudioManager.resetLockScreenInfo(); + AudioPlayer.reset(); }; }, [fetchAudioBuffer]); @@ -174,28 +98,8 @@ const AudioFile: FC = () => { {isLoading && }