sezo-audio-engine 0.0.2

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.
@@ -0,0 +1,108 @@
1
+ export type PlaybackState = 'stopped' | 'playing' | 'paused' | 'recording';
2
+ export interface AudioEngineConfig {
3
+ sampleRate?: number;
4
+ bufferSize?: number;
5
+ maxTracks?: number;
6
+ enableProcessing?: boolean;
7
+ }
8
+ export interface AudioTrack {
9
+ id: string;
10
+ uri: string;
11
+ type?: 'local' | 'remote';
12
+ volume?: number;
13
+ pan?: number;
14
+ muted?: boolean;
15
+ startTimeMs?: number;
16
+ }
17
+ export interface RecordingConfig {
18
+ sampleRate?: number;
19
+ channels?: number;
20
+ format?: 'aac' | 'mp3' | 'wav';
21
+ bitrate?: number;
22
+ quality?: 'low' | 'medium' | 'high';
23
+ enableNoiseGate?: boolean;
24
+ enableNormalization?: boolean;
25
+ }
26
+ export interface RecordingResult {
27
+ uri: string;
28
+ duration: number;
29
+ startTimeMs: number;
30
+ startTimeSamples?: number;
31
+ sampleRate: number;
32
+ channels: number;
33
+ format: 'aac' | 'mp3' | 'wav';
34
+ bitrate?: number;
35
+ fileSize: number;
36
+ }
37
+ export interface ExtractionConfig {
38
+ format?: 'aac' | 'mp3' | 'wav';
39
+ bitrate?: number;
40
+ includeEffects?: boolean;
41
+ outputDir?: string;
42
+ }
43
+ export interface ExtractionResult {
44
+ trackId?: string;
45
+ uri: string;
46
+ duration: number;
47
+ format: 'aac' | 'mp3' | 'wav';
48
+ bitrate?: number;
49
+ fileSize: number;
50
+ }
51
+ export interface MediaMetadata {
52
+ title: string;
53
+ artist?: string;
54
+ album?: string;
55
+ artwork?: string;
56
+ }
57
+ export type AudioEngineEvent = 'playbackStateChange' | 'positionUpdate' | 'playbackComplete' | 'trackLoaded' | 'trackUnloaded' | 'recordingStarted' | 'recordingStopped' | 'extractionProgress' | 'extractionComplete' | 'error';
58
+ export interface AudioEngineError {
59
+ code: string;
60
+ message: string;
61
+ details?: unknown;
62
+ }
63
+ export interface AudioEngine {
64
+ initialize(config: AudioEngineConfig): Promise<void>;
65
+ release(): Promise<void>;
66
+ loadTracks(tracks: AudioTrack[]): Promise<void>;
67
+ unloadTrack(trackId: string): Promise<void>;
68
+ unloadAllTracks(): Promise<void>;
69
+ getLoadedTracks(): AudioTrack[];
70
+ play(): void;
71
+ pause(): void;
72
+ stop(): void;
73
+ seek(positionMs: number): void;
74
+ isPlaying(): boolean;
75
+ getCurrentPosition(): number;
76
+ getDuration(): number;
77
+ setTrackVolume(trackId: string, volume: number): void;
78
+ setTrackMuted(trackId: string, muted: boolean): void;
79
+ setTrackSolo(trackId: string, solo: boolean): void;
80
+ setTrackPan(trackId: string, pan: number): void;
81
+ setTrackPitch(trackId: string, semitones: number): void;
82
+ getTrackPitch(trackId: string): number;
83
+ setTrackSpeed(trackId: string, rate: number): void;
84
+ getTrackSpeed(trackId: string): number;
85
+ setMasterVolume(volume: number): void;
86
+ getMasterVolume(): number;
87
+ setPitch(semitones: number): void;
88
+ getPitch(): number;
89
+ setSpeed(rate: number): void;
90
+ getSpeed(): number;
91
+ setTempoAndPitch(tempo: number, pitch: number): void;
92
+ startRecording(config?: RecordingConfig): Promise<void>;
93
+ stopRecording(): Promise<RecordingResult>;
94
+ isRecording(): boolean;
95
+ setRecordingVolume(volume: number): void;
96
+ extractTrack(trackId: string, config?: ExtractionConfig): Promise<ExtractionResult>;
97
+ extractAllTracks(config?: ExtractionConfig): Promise<ExtractionResult[]>;
98
+ cancelExtraction(jobId?: number): boolean;
99
+ getInputLevel(): number;
100
+ getOutputLevel(): number;
101
+ getTrackLevel(trackId: string): number;
102
+ enableBackgroundPlayback(metadata: MediaMetadata): Promise<void>;
103
+ updateNowPlayingInfo(metadata: Partial<MediaMetadata>): void;
104
+ disableBackgroundPlayback(): Promise<void>;
105
+ addListener(event: AudioEngineEvent, callback: Function): {
106
+ remove: () => void;
107
+ };
108
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { AudioEngineModule } from './AudioEngineModule';
2
+ export * from './AudioEngineModule.types';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { AudioEngineModule } from './AudioEngineModule';
2
+ export * from './AudioEngineModule.types';
@@ -0,0 +1,9 @@
1
+ {
2
+ "platforms": ["android", "ios"],
3
+ "android": {
4
+ "modules": ["expo.modules.audioengine.ExpoAudioEngineModule"]
5
+ },
6
+ "ios": {
7
+ "modules": ["ExpoAudioEngineModule"]
8
+ }
9
+ }
@@ -0,0 +1,13 @@
1
+ struct AudioEngineConfig {
2
+ let sampleRate: Double?
3
+ let bufferSize: Double?
4
+ let maxTracks: Int?
5
+ let enableProcessing: Bool?
6
+
7
+ init(dictionary: [String: Any]) {
8
+ sampleRate = dictionary["sampleRate"] as? Double
9
+ bufferSize = dictionary["bufferSize"] as? Double
10
+ maxTracks = dictionary["maxTracks"] as? Int
11
+ enableProcessing = dictionary["enableProcessing"] as? Bool
12
+ }
13
+ }
@@ -0,0 +1,61 @@
1
+ import AVFoundation
2
+
3
+ final class AudioSessionManager {
4
+ private let session = AVAudioSession.sharedInstance()
5
+
6
+ func configure(with config: AudioEngineConfig) {
7
+ do {
8
+ try session.setCategory(
9
+ .playAndRecord,
10
+ mode: .default,
11
+ options: [.defaultToSpeaker, .allowBluetooth]
12
+ )
13
+ } catch {
14
+ return
15
+ }
16
+
17
+ if let sampleRate = config.sampleRate {
18
+ try? session.setPreferredSampleRate(sampleRate)
19
+ }
20
+
21
+ if let bufferSize = config.bufferSize {
22
+ let sampleRate = config.sampleRate ?? session.sampleRate
23
+ if sampleRate > 0 {
24
+ let duration = bufferSize / sampleRate
25
+ try? session.setPreferredIOBufferDuration(duration)
26
+ }
27
+ }
28
+
29
+ try? session.setActive(true)
30
+ }
31
+
32
+ func enableBackgroundPlayback(with config: AudioEngineConfig) {
33
+ do {
34
+ try session.setCategory(
35
+ .playback,
36
+ mode: .default,
37
+ options: [.allowBluetooth, .allowAirPlay]
38
+ )
39
+ } catch {
40
+ return
41
+ }
42
+
43
+ if let sampleRate = config.sampleRate {
44
+ try? session.setPreferredSampleRate(sampleRate)
45
+ }
46
+
47
+ if let bufferSize = config.bufferSize {
48
+ let sampleRate = config.sampleRate ?? session.sampleRate
49
+ if sampleRate > 0 {
50
+ let duration = bufferSize / sampleRate
51
+ try? session.setPreferredIOBufferDuration(duration)
52
+ }
53
+ }
54
+
55
+ try? session.setActive(true)
56
+ }
57
+
58
+ func deactivate() {
59
+ try? session.setActive(false, options: .notifyOthersOnDeactivation)
60
+ }
61
+ }
@@ -0,0 +1,72 @@
1
+ import AVFoundation
2
+
3
+ final class AudioTrack {
4
+ let id: String
5
+ let uri: String
6
+ let url: URL
7
+ let file: AVAudioFile
8
+ let playerNode: AVAudioPlayerNode
9
+ let timePitch: AVAudioUnitTimePitch
10
+ let durationMs: Double
11
+ var startTimeMs: Double
12
+ var volume: Double
13
+ var pan: Double
14
+ var muted: Bool
15
+ var solo: Bool
16
+ var pitch: Double
17
+ var speed: Double
18
+ var isAttached: Bool
19
+
20
+ init?(input: [String: Any]) {
21
+ guard let id = input["id"] as? String,
22
+ let uri = input["uri"] as? String,
23
+ let url = AudioTrack.resolveURL(uri: uri) else {
24
+ return nil
25
+ }
26
+
27
+ do {
28
+ let file = try AVAudioFile(forReading: url)
29
+ let sampleRate = file.processingFormat.sampleRate
30
+ let durationMs = sampleRate > 0 ? (Double(file.length) / sampleRate) * 1000.0 : 0.0
31
+
32
+ self.id = id
33
+ self.uri = uri
34
+ self.url = url
35
+ self.file = file
36
+ self.durationMs = durationMs
37
+ self.startTimeMs = input["startTimeMs"] as? Double ?? 0.0
38
+ self.volume = input["volume"] as? Double ?? 1.0
39
+ self.pan = input["pan"] as? Double ?? 0.0
40
+ self.muted = input["muted"] as? Bool ?? false
41
+ self.solo = input["solo"] as? Bool ?? false
42
+ self.pitch = 0.0
43
+ self.speed = 1.0
44
+ self.playerNode = AVAudioPlayerNode()
45
+ self.timePitch = AVAudioUnitTimePitch()
46
+ self.isAttached = false
47
+ } catch {
48
+ return nil
49
+ }
50
+ }
51
+
52
+ func asDictionary() -> [String: Any] {
53
+ return [
54
+ "id": id,
55
+ "uri": uri,
56
+ "volume": volume,
57
+ "pan": pan,
58
+ "muted": muted,
59
+ "startTimeMs": startTimeMs
60
+ ]
61
+ }
62
+
63
+ private static func resolveURL(uri: String) -> URL? {
64
+ if uri.hasPrefix("file://") {
65
+ return URL(string: uri)
66
+ }
67
+ if uri.hasPrefix("/") {
68
+ return URL(fileURLWithPath: uri)
69
+ }
70
+ return URL(string: uri)
71
+ }
72
+ }