Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/bare-expo/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ PODS:
- React-utils
- ReactNativeDependencies
- Yoga
- React-Core-prebuilt (0.84.0):
- React-Core-prebuilt (0.84.1):
- ReactNativeDependencies
- React-Core/CoreModulesHeaders (0.84.1):
- hermes-engine
Expand Down Expand Up @@ -3900,7 +3900,7 @@ SPEC CHECKSUMS:
EXUpdates: c5a64985f393cf4f8beb4463f86a885c90b4fccc
EXUpdatesInterface: 26412751a0f7a7130614655929e316f684552aab
FBLazyVector: e97c19a5a442429d1988f182a1940fb08df514da
hermes-engine: f17b9ba9fc7fc0b2418d3f51964ef51edd76cb49
hermes-engine: 2322b1974d93af0d15d30d5bafd5886cd7475677
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
Expand All @@ -3918,7 +3918,7 @@ SPEC CHECKSUMS:
React: 1ba7d364ade7d883a1ec055bfc3606f35fdee17b
React-callinvoker: bc2a26f8d84fb01f003fc6de6c9337b64715f95b
React-Core: bdaa87b276ca31877632a982ecf7c36f8c826414
React-Core-prebuilt: 8d3b8ca928d5ec4acbd02a85ad31693483710ca8
React-Core-prebuilt: 3a18d3c3d7f34cd70923b0b359b41e78163d9877
React-CoreModules: b24989f62d56390ae08ca4f65e6f38fe6802de42
React-cxxreact: 1a2dfcbc18a6b610664dba152adf327f063a0d12
React-debug: 755200a6e7f5e6e0a40ff8d215493d43cce285fc
Expand Down Expand Up @@ -3988,7 +3988,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 26bbf1e26768d08dd965a2b5e372e53f67b21fee
ReactCodegen: 3737b2746b303ce8636a461f8ee4cb2525078267
ReactCommon: 309419492d417c4cbb87af06f67735afa40ecb9d
ReactNativeDependencies: 73db4772e50c35a1670768d3694ce715638d3b5f
ReactNativeDependencies: e7a1b2c1da219bfbb50fab4af2d5a18d2ddbf770
RNCAsyncStorage: e85a99325df9eb0191a6ee2b2a842644c7eb29f4
RNCMaskedView: 3c9d7586e2b9bbab573591dcb823918bc4668005
RNCPicker: e0149590451d5eae242cf686014a6f6d808f93c7
Expand Down
2 changes: 1 addition & 1 deletion apps/expo-go/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4770,7 +4770,7 @@ SPEC CHECKSUMS:
GoogleAppMeasurement: 8a82b93a6400c8e6551c0bcd66a9177f2e067aed
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
hermes-engine: bfb748a8f1f841309151b371e25491acbdf94a8a
hermes-engine: 00d01f3145a01aca9d0940d02c403832806eaf82
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ function AudioEvents() {
highlight={status.didJustFinish}
/>
<StatusRow label="isLoaded" value={status.isLoaded} />
<StatusRow label="loop" value={status.loop} />
<StatusRow label="currentTime" value={status.currentTime.toFixed(2)} />
<StatusRow label="duration" value={status.duration.toFixed(2)} />
</View>
Expand All @@ -128,6 +129,12 @@ function AudioEvents() {
</View>
<View style={styles.controlsRow}>
<Button title="Seek Near End (-2s)" onPress={seekToEnd} />
<Button
title={`Loop: ${player.loop ? 'ON' : 'OFF'}`}
onPress={() => {
player.loop = !player.loop;
}}
/>
<Button title="Clear Log" onPress={clearLog} />
</View>
</View>
Expand Down
2 changes: 1 addition & 1 deletion docs/ui/components/Prerequisites/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const Prerequisites: ComponentType<PrerequisitesProps> = withHeadingManager(
}, [open]);
return (
<details
id={anchorId}
id={heading.current.slug}
className={mergeClasses(
'mb-3 scroll-m-4 rounded-md border border-default p-0',
'[&[open]]:shadow-xs',
Expand Down
1 change: 1 addition & 0 deletions packages/@expo/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

- Correctly handle JavaScript assets when `asyncRoutes: true` in SSR ([#43446](https://github.com/expo/expo/pull/43446) by [@hassankhan](https://github.com/hassankhan))
- Fix server being started before Metro is ready, or, if it's started, status middleware responding too soon ([#43557](https://github.com/expo/expo/pull/43557) by [@kitten](https://github.com/kitten))
- Don't let `expo start`'s dependency validation fail the `start` command ([#43619](https://github.com/expo/expo/pull/43619) by [@kitten](https://github.com/kitten))

### 💡 Others

Expand Down
7 changes: 6 additions & 1 deletion packages/@expo/cli/src/start/startAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ export async function startAsync(
}

if (!env.EXPO_NO_DEPENDENCY_VALIDATION && !settings.webOnly && !options.devClient) {
await profile(validateDependenciesVersionsAsync)(projectRoot, exp, pkg);
try {
await profile(validateDependenciesVersionsAsync)(projectRoot, exp, pkg);
} catch {
// We don't show the dependency validation error, since it's non-essential
// for the user to know it ran or failed
}
}

// Open project on devices.
Expand Down
1 change: 1 addition & 0 deletions packages/expo-audio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### 🐛 Bug fixes

- [iOS] Fix crash during seek. ([#43564](https://github.com/expo/expo/pull/43564) by [@alanjhughes](https://github.com/alanjhughes))
- [iOS] Improve looping support. ([#43600](https://github.com/expo/expo/pull/43600) by [@alanjhughes](https://github.com/alanjhughes))

### 💡 Others

Expand Down
4 changes: 2 additions & 2 deletions packages/expo-audio/ios/AudioModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ public class AudioModule: Module {
player.ref.volume = originalVolume
}
case .doNotMix, .mixWithOthers:
player.ref.play()
player.play(at: player.currentRate > 0 ? player.currentRate : 1.0)
}
}
}
Expand Down Expand Up @@ -705,7 +705,7 @@ public class AudioModule: Module {
private func resumeAllPlayers() {
registry.allPlayers.values.forEach { player in
if player.wasPlaying {
player.ref.play()
player.play(at: player.currentRate > 0 ? player.currentRate : 1.0)
player.wasPlaying = false
}
}
Expand Down
106 changes: 92 additions & 14 deletions packages/expo-audio/ios/AudioPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ private enum AudioConstants {

public class AudioPlayer: SharedRef<AVPlayer> {
let id = UUID().uuidString
var isLooping = false
var shouldCorrectPitch = true
var pitchCorrectionQuality: AVAudioTimePitchAlgorithm = .timeDomain
var isActiveForLockScreen = false
var metadata: Metadata?
var currentRate: Float = 0.0
var currentRate: Float = 1.0 {
didSet {
currentRate = max(0, currentRate)
}
}
let interval: Double
var wasPlaying = false
var isPaused: Bool {
Expand All @@ -22,6 +25,22 @@ public class AudioPlayer: SharedRef<AVPlayer> {
var samplingEnabled = false
var keepAudioSessionActive = false

var isLooping = false {
didSet {
guard isLooping != oldValue else {
return
}
if isLooping {
ref.actionAtItemEnd = .advance
enqueueNextLoopItem()
} else {
removeQueuedLoopItems()
ref.actionAtItemEnd = .pause
}
updateStatus(with: [:])
}
}

private var source: AudioSource?

// MARK: Observers
Expand Down Expand Up @@ -65,7 +84,15 @@ public class AudioPlayer: SharedRef<AVPlayer> {
playerIsBuffering()
}

private var effectiveRate: Float {
currentRate > 0 ? currentRate : 1.0
}

func play(at rate: Float) {
ref.actionAtItemEnd = isLooping ? .advance : .pause
if isLooping {
enqueueNextLoopItem()
}
addPlaybackEndNotification()
registerTimeObserver()
ref.playImmediately(atRate: rate)
Expand Down Expand Up @@ -165,6 +192,9 @@ public class AudioPlayer: SharedRef<AVPlayer> {
self.updateStatus(with: [
"isLoaded": true
])
if self.isLooping {
self.enqueueNextLoopItem()
}
if shouldInstallAudioTap || samplingEnabled {
installTap()
shouldInstallAudioTap = false
Expand All @@ -178,7 +208,12 @@ public class AudioPlayer: SharedRef<AVPlayer> {
guard let self else {
return
}
if self.isLooping {
self.enqueueNextLoopItem()
self.addPlaybackEndNotification()
}
if self.samplingEnabled && self.isLoaded {
self.uninstallTap()
self.installTap()
}
}
Expand All @@ -188,41 +223,44 @@ public class AudioPlayer: SharedRef<AVPlayer> {
func replaceWithPreloadedItem(_ item: AVPlayerItem?) {
let wasPlaying = ref.timeControlStatus == .playing
let wasSamplingEnabled = samplingEnabled
removeQueuedLoopItems()
ref.pause()

if samplingEnabled {
uninstallTap()
}
ref.replaceCurrentItem(with: item)
replacePlayerItem(with: item)

if wasSamplingEnabled {
shouldInstallAudioTap = true
}

if wasPlaying {
ref.play()
play(at: effectiveRate)
}
}

func replaceCurrentSource(source: AudioSource) {
self.source = source
let wasPlaying = ref.timeControlStatus == .playing
let wasSamplingEnabled = samplingEnabled
removeQueuedLoopItems()
ref.pause()

if samplingEnabled {
uninstallTap()
}
let item = AudioUtils.createAVPlayerItem(from: source)
ref.replaceCurrentItem(with: item)
replacePlayerItem(with: item)

if wasSamplingEnabled {
shouldInstallAudioTap = true
}

if wasPlaying {
onReady { [weak self] in
self?.ref.play()
guard let self else { return }
self.play(at: self.effectiveRate)
}
}
}
Expand Down Expand Up @@ -261,6 +299,7 @@ public class AudioPlayer: SharedRef<AVPlayer> {
}

private func teardownPlayer() {
removeQueuedLoopItems()
if samplingEnabled {
uninstallTap()
}
Expand Down Expand Up @@ -360,27 +399,66 @@ public class AudioPlayer: SharedRef<AVPlayer> {
audioProcessor?.sampleBufferCallback = nil
}

private func replacePlayerItem(with item: AVPlayerItem?) {
if let queuePlayer = ref as? AVQueuePlayer {
queuePlayer.removeAllItems()
if let item {
queuePlayer.insert(item, after: nil)
}
} else {
ref.replaceCurrentItem(with: item)
}
}

private func enqueueNextLoopItem() {
guard let queuePlayer = ref as? AVQueuePlayer,
let currentItem = queuePlayer.currentItem else {
return
}

guard queuePlayer.items().count <= 1 else {
return
}
let nextItem = AVPlayerItem(asset: currentItem.asset)
nextItem.audioTimePitchAlgorithm = currentItem.audioTimePitchAlgorithm
queuePlayer.insert(nextItem, after: currentItem)
}

private func removeQueuedLoopItems() {
guard let queuePlayer = ref as? AVQueuePlayer else {
return
}

for item in queuePlayer.items() where item != queuePlayer.currentItem {
queuePlayer.remove(item)
}
}

private func addPlaybackEndNotification() {
if let endObserver {
NotificationCenter.default.removeObserver(endObserver)
}

endObserver = NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
object: ref.currentItem,
object: nil,
queue: nil
) { [weak self] _ in
guard let self else {
) { [weak self] notification in
guard let self,
let finishedItem = notification.object as? AVPlayerItem else {
return
}

if self.isLooping {
self.ref.seek(to: CMTime.zero)
self.ref.play()
} else {
guard let queuePlayer = self.ref as? AVQueuePlayer,
finishedItem == queuePlayer.currentItem || queuePlayer.items().contains(finishedItem) else {
return
}

if !self.isLooping {
let currentTime = finishedItem.duration.seconds
self.updateStatus(with: [
"playing": false,
"currentTime": self.duration,
"currentTime": currentTime.isNaN ? 0.0 : currentTime,
"didJustFinish": true
])
self.onPlaybackComplete?()
Expand Down
4 changes: 2 additions & 2 deletions packages/expo-audio/ios/AudioUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ struct AudioUtils {

static func createAVPlayer(from source: AudioSource?) -> AVPlayer {
if let item = createAVPlayerItem(from: source) {
return AVPlayer(playerItem: item)
return AVQueuePlayer(items: [item])
}
return AVPlayer()
return AVQueuePlayer()
}

static func createAVPlayerItem(from source: AudioSource?) -> AVPlayerItem? {
Expand Down
Loading
Loading