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
3 changes: 3 additions & 0 deletions packages/expo-audio/build/AudioPlayer.web.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/expo-audio/build/AudioPlayer.web.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 33 additions & 23 deletions packages/expo-audio/build/AudioPlayer.web.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/expo-audio/build/AudioPlayer.web.js.map

Large diffs are not rendered by default.

62 changes: 36 additions & 26 deletions packages/expo-audio/src/AudioPlayer.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class AudioPlayerWeb
private sourceNode: MediaElementAudioSourceNode | null = null;
private samplingFrameId: number | null = null;
private samplingEnabled = false;
private samplingBuffer: Float32Array<ArrayBuffer> | null = null;

get playing(): boolean {
return this.isPlaying;
Expand Down Expand Up @@ -99,11 +100,13 @@ export class AudioPlayerWeb
play(): void {
this.media.play();
this.isPlaying = true;
this.startSampling();
}

pause(): void {
this.media.pause();
this.isPlaying = false;
this.stopSampling();
}

replace(source: AudioSource): void {
Expand Down Expand Up @@ -172,30 +175,15 @@ export class AudioPlayerWeb
this.sourceNode.connect(this.analyser);
this.analyser.connect(ctx.destination);

const buffer = new Float32Array(this.analyser.frequencyBinCount);

const sampleLoop = () => {
if (!this.analyser) {
return;
}
if (this.isPlaying) {
this.analyser.getFloatTimeDomainData(buffer);
this.emit(AUDIO_SAMPLE_UPDATE, {
channels: [{ frames: Array.from(buffer) }],
timestamp: this.media.currentTime,
});
}
this.samplingFrameId = requestAnimationFrame(sampleLoop);
};
this.samplingFrameId = requestAnimationFrame(sampleLoop);

this.samplingBuffer = new Float32Array(this.analyser.frequencyBinCount);
this.samplingEnabled = true;

if (this.isPlaying) {
this.startSampling();
}
this.isAudioSamplingSupported = true;
} else {
if (this.samplingFrameId != null) {
cancelAnimationFrame(this.samplingFrameId);
this.samplingFrameId = null;
}
this.stopSampling();

if (this.analyser) {
this.analyser.disconnect();
Expand All @@ -208,10 +196,36 @@ export class AudioPlayerWeb
this.sourceNode.connect(ctx.destination);
}

this.samplingBuffer = null;
this.samplingEnabled = false;
}
}

private startSampling(): void {
if (!this.samplingEnabled || !this.analyser || !this.samplingBuffer) {
return;
}
const sampleLoop = () => {
if (!this.analyser || !this.samplingBuffer) {
return;
}
this.analyser.getFloatTimeDomainData(this.samplingBuffer);
this.emit(AUDIO_SAMPLE_UPDATE, {
channels: [{ frames: Array.from(this.samplingBuffer) }],
timestamp: this.media.currentTime,
});
this.samplingFrameId = requestAnimationFrame(sampleLoop);
};
this.samplingFrameId = requestAnimationFrame(sampleLoop);
}

private stopSampling(): void {
if (this.samplingFrameId != null) {
cancelAnimationFrame(this.samplingFrameId);
this.samplingFrameId = null;
}
}

setPlaybackRate(second: number, pitchCorrectionQuality?: PitchCorrectionQuality): void {
this.media.playbackRate = second;
this.shouldCorrectPitch = pitchCorrectionQuality === 'high';
Expand All @@ -221,11 +235,7 @@ export class AudioPlayerWeb

remove(): void {
mediaSessionController.clear(this);

if (this.samplingFrameId != null) {
cancelAnimationFrame(this.samplingFrameId);
this.samplingFrameId = null;
}
this.stopSampling();

if (this.analyser) {
this.analyser.disconnect();
Expand Down
1 change: 1 addition & 0 deletions packages/expo-modules-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
### 💡 Others

- Removed needless warning when `NativeModulesProxy` is absent. ([#43020](https://github.com/expo/expo/pull/43020) by [@tsapeta](https://github.com/tsapeta))
- [iOS] Removed some unused code. ([#42949](https://github.com/expo/expo/pull/42949) by [@tsapeta](https://github.com/tsapeta))

## 55.0.8 — 2026-02-08

Expand Down
29 changes: 1 addition & 28 deletions packages/expo-modules-core/ios/Core/AppContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,10 @@ public final class AppContext: NSObject, @unchecked Sendable {
*/
@objc
public var appIdentifier: String? {
#if RCT_NEW_ARCH_ENABLED
guard let moduleRegistry = reactBridge?.moduleRegistry else {
return nil
}
return "\(abs(ObjectIdentifier(moduleRegistry).hashValue))"
#else
return nil
#endif
}

/**
Expand Down Expand Up @@ -364,27 +360,6 @@ public final class AppContext: NSObject, @unchecked Sendable {
return moduleRegistry.get(moduleHolderForName: moduleName)?.javaScriptObject
}

/**
Returns an array of event names supported by all Swift modules.
*/
@objc
public func getSupportedEvents() -> [String] {
return moduleRegistry.reduce(into: [String]()) { events, holder in
events.append(contentsOf: holder.definition.eventNames)
}
}

/**
Modifies listeners count for module with given name. Depending on the listeners count,
`onStartObserving` and `onStopObserving` are called.
*/
@objc
public func modifyEventListenersCount(_ moduleName: String, count: Int) {
moduleRegistry
.get(moduleHolderForName: moduleName)?
.modifyListenersCount(count)
}

/**
Asynchronously calls module's function with given arguments.
*/
Expand Down Expand Up @@ -479,19 +454,17 @@ public final class AppContext: NSObject, @unchecked Sendable {

/**
Registers native views defined by registered native modules.
- Note: It should stay private as `registerNativeModules` should be the only call site. Works only with the New Architecture.
- Note: It should stay private as `registerNativeModules` should be the only call site.
- Todo: `RCTComponentViewFactory` is thread-safe, so this function should be as well.
*/
@MainActor
private func registerNativeViews() {
#if RCT_NEW_ARCH_ENABLED
for holder in moduleRegistry {
for (key, viewDefinition) in holder.definition.views {
let viewModule = ViewModuleWrapper(holder, viewDefinition, isDefaultModuleView: key == DEFAULT_MODULE_VIEW)
ExpoFabricView.registerComponent(viewModule, appContext: self)
}
}
#endif
}

// MARK: - Runtime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ public class AsyncFunctionDefinition<Args, FirstArgType, ReturnType>: AnyAsyncFu
let maxRetryCount = 3

queue.async {
#if RCT_NEW_ARCH_ENABLED
// Checks if this is a view function unregistered in the view registry. The check can be performed from the main thread only.
if retryCount < maxRetryCount,
let viewTag = arguments.first as? Int,
Expand All @@ -150,7 +149,6 @@ public class AsyncFunctionDefinition<Args, FirstArgType, ReturnType>: AnyAsyncFu
self.dispatchOnQueueUntilViewRegisters(appContext: appContext, arguments: arguments, queue: queue, retryCount: retryCount + 1, block)
return
}
#endif
// Schedule the block as normal.
block()
}
Expand Down
17 changes: 0 additions & 17 deletions packages/expo-modules-core/ios/Core/ModuleHolder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,23 +135,6 @@ public final class ModuleHolder {
}
}

// MARK: JavaScript events

/**
Modifies module's listeners count and calls `onStartObserving` or `onStopObserving` accordingly.
*/
func modifyListenersCount(_ count: Int) {
guard let appContext else {
return
}
if count > 0 && listenersCount == 0 {
definition.functions["startObserving"]?.call(withArguments: [], appContext: appContext)
} else if count < 0 && listenersCount + count <= 0 {
definition.functions["stopObserving"]?.call(withArguments: [], appContext: appContext)
}
listenersCount = max(0, listenersCount + count)
}

// MARK: Deallocation

deinit {
Expand Down
39 changes: 0 additions & 39 deletions packages/expo-modules-core/ios/Core/Views/ExpoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,4 @@

import React

#if RCT_NEW_ARCH_ENABLED
public typealias ExpoView = ExpoFabricView
#else
/**
The view that extends `RCTView` which handles some styles (e.g. borders) and accessibility.
Inherit from `ExpoView` to keep this behavior and let your view use the associated `AppContext`.
*/
open class ExpoClassicView: RCTView, AnyExpoView {
/**
A weak pointer to the associated `AppContext`.
*/
public private(set) weak var appContext: AppContext?

/**
The required initializer that receives an instance of `AppContext`.
Override it if the subclassing view needs to do something during initialization.
*/
required public init(appContext: AppContext? = nil) {
self.appContext = appContext
super.init(frame: .zero)
}

// Mark the required init as unavailable so that subclasses can avoid overriding it.
@available(*, unavailable)
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public func updateProps(_ rawProps: [String: Any]) {
// Stub function – it's not used on the old architecture and non-SwiftUI views
}

public func supportsProp(withName name: String) -> Bool {
// Stub function – it's not used on the old architecture and non-SwiftUI views
return false
}
}

public typealias ExpoView = ExpoClassicView
#endif
Loading
Loading