@symbiote-native/audio 0.1.2 → 0.1.4

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.
Files changed (280) hide show
  1. package/README.md +96 -52
  2. package/build/angular/index.d.ts +8 -0
  3. package/build/angular/index.js +8 -3
  4. package/build/angular/inject-audio-player-status.d.ts +4 -0
  5. package/build/angular/inject-audio-player-status.js +4 -0
  6. package/build/angular/inject-audio-player.d.ts +4 -0
  7. package/build/angular/inject-audio-player.js +8 -0
  8. package/build/angular/inject-audio-playlist-status.d.ts +3 -0
  9. package/build/angular/inject-audio-playlist-status.js +4 -0
  10. package/build/angular/inject-audio-playlist.d.ts +5 -0
  11. package/build/angular/inject-audio-playlist.js +7 -0
  12. package/build/angular/inject-audio-recorder-state.d.ts +5 -0
  13. package/build/angular/inject-audio-recorder-state.js +10 -0
  14. package/build/angular/inject-audio-recorder.d.ts +5 -0
  15. package/build/angular/inject-audio-recorder.js +13 -0
  16. package/build/angular/inject-audio-sample-listener.d.ts +4 -0
  17. package/build/angular/inject-audio-sample-listener.js +7 -0
  18. package/build/angular/inject-audio-stream.d.ts +4 -0
  19. package/build/angular/inject-audio-stream.js +17 -0
  20. package/build/core/audio-module.js +1 -1
  21. package/build/core/audio-player-controller.d.ts +7 -0
  22. package/build/core/audio-player-controller.js +10 -0
  23. package/build/core/audio-player.js +16 -4
  24. package/build/core/audio-playlist-controller.d.ts +4 -0
  25. package/build/core/audio-playlist-controller.js +5 -0
  26. package/build/core/audio-recorder-controller.d.ts +4 -0
  27. package/build/core/audio-recorder-controller.js +5 -0
  28. package/build/core/audio-recorder-polling.d.ts +5 -0
  29. package/build/core/audio-recorder-polling.js +10 -0
  30. package/build/core/audio-recorder-state.d.ts +2 -0
  31. package/build/core/audio-recorder-state.js +18 -0
  32. package/build/core/audio-recorder-status.d.ts +8 -0
  33. package/build/core/audio-recorder-status.js +6 -0
  34. package/build/core/audio-recorder.d.ts +2 -0
  35. package/build/core/audio-recorder.js +4 -0
  36. package/build/core/audio-sample-listener.d.ts +11 -0
  37. package/build/core/audio-sample-listener.js +9 -0
  38. package/build/core/audio-stream-controller.d.ts +4 -0
  39. package/build/core/audio-stream-controller.js +5 -0
  40. package/build/core/audio-stream-hooks.d.ts +8 -0
  41. package/build/core/audio-stream-hooks.js +13 -0
  42. package/build/core/audio-stream-lifecycle.d.ts +3 -0
  43. package/build/core/audio-stream-lifecycle.js +9 -0
  44. package/build/core/audio-stream-subscription.d.ts +10 -0
  45. package/build/core/audio-stream-subscription.js +12 -0
  46. package/build/core/audio-stream.d.ts +9 -10
  47. package/build/core/audio-stream.js +1 -9
  48. package/build/core/index.d.ts +14 -2
  49. package/build/core/index.js +9 -1
  50. package/build/core/json-keyed-resource-controller.d.ts +1 -0
  51. package/build/core/json-keyed-resource-controller.js +1 -0
  52. package/build/core/resolve-source.d.ts +2 -7
  53. package/build/core/resolve-source.js +69 -15
  54. package/build/core/subscribe-single-event.d.ts +5 -0
  55. package/build/core/subscribe-single-event.js +8 -0
  56. package/build/core/types.d.ts +11 -5
  57. package/build/react/index.d.ts +9 -0
  58. package/build/react/index.js +9 -0
  59. package/build/react/use-audio-player-status.d.ts +4 -0
  60. package/build/react/use-audio-player-status.js +4 -0
  61. package/build/react/use-audio-player.d.ts +3 -0
  62. package/build/react/use-audio-player.js +7 -0
  63. package/build/react/use-audio-playlist-status.d.ts +3 -0
  64. package/build/react/use-audio-playlist-status.js +4 -0
  65. package/build/react/use-audio-playlist.d.ts +4 -0
  66. package/build/react/use-audio-playlist.js +7 -0
  67. package/build/react/use-audio-recorder-state.d.ts +4 -0
  68. package/build/react/use-audio-recorder-state.js +9 -0
  69. package/build/react/use-audio-recorder.d.ts +4 -0
  70. package/build/react/use-audio-recorder.js +11 -0
  71. package/build/react/use-audio-sample-listener.d.ts +4 -0
  72. package/build/react/use-audio-sample-listener.js +6 -0
  73. package/build/react/use-audio-stream.d.ts +3 -0
  74. package/build/react/use-audio-stream.js +14 -0
  75. package/build/solid/index.d.ts +9 -0
  76. package/build/solid/index.js +9 -0
  77. package/build/solid/use-audio-player-status.d.ts +4 -0
  78. package/build/solid/use-audio-player-status.js +4 -0
  79. package/build/solid/use-audio-player.d.ts +5 -0
  80. package/build/solid/use-audio-player.js +7 -0
  81. package/build/solid/use-audio-playlist-status.d.ts +3 -0
  82. package/build/solid/use-audio-playlist-status.js +4 -0
  83. package/build/solid/use-audio-playlist.d.ts +5 -0
  84. package/build/solid/use-audio-playlist.js +7 -0
  85. package/build/solid/use-audio-recorder-state.d.ts +5 -0
  86. package/build/solid/use-audio-recorder-state.js +9 -0
  87. package/build/solid/use-audio-recorder.d.ts +5 -0
  88. package/build/solid/use-audio-recorder.js +13 -0
  89. package/build/solid/use-audio-sample-listener.d.ts +4 -0
  90. package/build/solid/use-audio-sample-listener.js +8 -0
  91. package/build/solid/use-audio-stream.d.ts +4 -0
  92. package/build/solid/use-audio-stream.js +16 -0
  93. package/build/svelte/index.d.ts +9 -0
  94. package/build/svelte/index.js +9 -0
  95. package/build/svelte/use-audio-player-status.svelte.d.ts +6 -0
  96. package/build/svelte/use-audio-player-status.svelte.js +4 -0
  97. package/build/svelte/use-audio-player.svelte.d.ts +6 -0
  98. package/build/svelte/use-audio-player.svelte.js +8 -0
  99. package/build/svelte/use-audio-playlist-status.svelte.d.ts +5 -0
  100. package/build/svelte/use-audio-playlist-status.svelte.js +4 -0
  101. package/build/svelte/use-audio-playlist.svelte.d.ts +6 -0
  102. package/build/svelte/use-audio-playlist.svelte.js +8 -0
  103. package/build/svelte/use-audio-recorder-state.svelte.d.ts +7 -0
  104. package/build/svelte/use-audio-recorder-state.svelte.js +14 -0
  105. package/build/svelte/use-audio-recorder.svelte.d.ts +6 -0
  106. package/build/svelte/use-audio-recorder.svelte.js +11 -0
  107. package/build/svelte/use-audio-sample-listener.svelte.d.ts +4 -0
  108. package/build/svelte/use-audio-sample-listener.svelte.js +5 -0
  109. package/build/svelte/use-audio-stream.svelte.d.ts +4 -0
  110. package/build/svelte/use-audio-stream.svelte.js +29 -0
  111. package/build/vue/index.d.ts +9 -0
  112. package/build/vue/index.js +9 -0
  113. package/build/vue/use-audio-player-status.d.ts +4 -0
  114. package/build/vue/use-audio-player-status.js +4 -0
  115. package/build/vue/use-audio-player.d.ts +5 -0
  116. package/build/vue/use-audio-player.js +8 -0
  117. package/build/vue/use-audio-playlist-status.d.ts +3 -0
  118. package/build/vue/use-audio-playlist-status.js +4 -0
  119. package/build/vue/use-audio-playlist.d.ts +5 -0
  120. package/build/vue/use-audio-playlist.js +8 -0
  121. package/build/vue/use-audio-recorder-state.d.ts +5 -0
  122. package/build/vue/use-audio-recorder-state.js +12 -0
  123. package/build/vue/use-audio-recorder.d.ts +5 -0
  124. package/build/vue/use-audio-recorder.js +11 -0
  125. package/build/vue/use-audio-sample-listener.d.ts +4 -0
  126. package/build/vue/use-audio-sample-listener.js +11 -0
  127. package/build/vue/use-audio-stream.d.ts +4 -0
  128. package/build/vue/use-audio-stream.js +18 -0
  129. package/build-ngc/angular/index.d.ts +8 -0
  130. package/build-ngc/angular/index.js +8 -3
  131. package/build-ngc/angular/index.js.map +1 -1
  132. package/build-ngc/angular/inject-audio-player-status.d.ts +4 -0
  133. package/build-ngc/angular/inject-audio-player-status.js +5 -0
  134. package/build-ngc/angular/inject-audio-player-status.js.map +1 -0
  135. package/build-ngc/angular/inject-audio-player.d.ts +4 -0
  136. package/build-ngc/angular/inject-audio-player.js +9 -0
  137. package/build-ngc/angular/inject-audio-player.js.map +1 -0
  138. package/build-ngc/angular/inject-audio-playlist-status.d.ts +3 -0
  139. package/build-ngc/angular/inject-audio-playlist-status.js +5 -0
  140. package/build-ngc/angular/inject-audio-playlist-status.js.map +1 -0
  141. package/build-ngc/angular/inject-audio-playlist.d.ts +5 -0
  142. package/build-ngc/angular/inject-audio-playlist.js +8 -0
  143. package/build-ngc/angular/inject-audio-playlist.js.map +1 -0
  144. package/build-ngc/angular/inject-audio-recorder-state.d.ts +5 -0
  145. package/build-ngc/angular/inject-audio-recorder-state.js +12 -0
  146. package/build-ngc/angular/inject-audio-recorder-state.js.map +1 -0
  147. package/build-ngc/angular/inject-audio-recorder.d.ts +5 -0
  148. package/build-ngc/angular/inject-audio-recorder.js +14 -0
  149. package/build-ngc/angular/inject-audio-recorder.js.map +1 -0
  150. package/build-ngc/angular/inject-audio-sample-listener.d.ts +4 -0
  151. package/build-ngc/angular/inject-audio-sample-listener.js +8 -0
  152. package/build-ngc/angular/inject-audio-sample-listener.js.map +1 -0
  153. package/build-ngc/angular/inject-audio-stream.d.ts +4 -0
  154. package/build-ngc/angular/inject-audio-stream.js +18 -0
  155. package/build-ngc/angular/inject-audio-stream.js.map +1 -0
  156. package/build-ngc/core/audio-module.js +1 -1
  157. package/build-ngc/core/audio-module.js.map +1 -1
  158. package/build-ngc/core/audio-player-controller.d.ts +7 -0
  159. package/build-ngc/core/audio-player-controller.js +11 -0
  160. package/build-ngc/core/audio-player-controller.js.map +1 -0
  161. package/build-ngc/core/audio-player.js +16 -4
  162. package/build-ngc/core/audio-player.js.map +1 -1
  163. package/build-ngc/core/audio-playlist-controller.d.ts +4 -0
  164. package/build-ngc/core/audio-playlist-controller.js +6 -0
  165. package/build-ngc/core/audio-playlist-controller.js.map +1 -0
  166. package/build-ngc/core/audio-recorder-controller.d.ts +4 -0
  167. package/build-ngc/core/audio-recorder-controller.js +6 -0
  168. package/build-ngc/core/audio-recorder-controller.js.map +1 -0
  169. package/build-ngc/core/audio-recorder-polling.d.ts +5 -0
  170. package/build-ngc/core/audio-recorder-polling.js +11 -0
  171. package/build-ngc/core/audio-recorder-polling.js.map +1 -0
  172. package/build-ngc/core/audio-recorder-state.d.ts +2 -0
  173. package/build-ngc/core/audio-recorder-state.js +19 -0
  174. package/build-ngc/core/audio-recorder-state.js.map +1 -0
  175. package/build-ngc/core/audio-recorder-status.d.ts +8 -0
  176. package/build-ngc/core/audio-recorder-status.js +7 -0
  177. package/build-ngc/core/audio-recorder-status.js.map +1 -0
  178. package/build-ngc/core/audio-recorder.d.ts +2 -0
  179. package/build-ngc/core/audio-recorder.js +4 -0
  180. package/build-ngc/core/audio-recorder.js.map +1 -1
  181. package/build-ngc/core/audio-sample-listener.d.ts +11 -0
  182. package/build-ngc/core/audio-sample-listener.js +10 -0
  183. package/build-ngc/core/audio-sample-listener.js.map +1 -0
  184. package/build-ngc/core/audio-stream-controller.d.ts +4 -0
  185. package/build-ngc/core/audio-stream-controller.js +6 -0
  186. package/build-ngc/core/audio-stream-controller.js.map +1 -0
  187. package/build-ngc/core/audio-stream-hooks.d.ts +8 -0
  188. package/build-ngc/core/audio-stream-hooks.js +14 -0
  189. package/build-ngc/core/audio-stream-hooks.js.map +1 -0
  190. package/build-ngc/core/audio-stream-lifecycle.d.ts +3 -0
  191. package/build-ngc/core/audio-stream-lifecycle.js +10 -0
  192. package/build-ngc/core/audio-stream-lifecycle.js.map +1 -0
  193. package/build-ngc/core/audio-stream-subscription.d.ts +10 -0
  194. package/build-ngc/core/audio-stream-subscription.js +13 -0
  195. package/build-ngc/core/audio-stream-subscription.js.map +1 -0
  196. package/build-ngc/core/audio-stream.d.ts +9 -10
  197. package/build-ngc/core/audio-stream.js +1 -9
  198. package/build-ngc/core/audio-stream.js.map +1 -1
  199. package/build-ngc/core/index.d.ts +14 -2
  200. package/build-ngc/core/index.js +9 -1
  201. package/build-ngc/core/index.js.map +1 -1
  202. package/build-ngc/core/json-keyed-resource-controller.d.ts +1 -0
  203. package/build-ngc/core/json-keyed-resource-controller.js +2 -0
  204. package/build-ngc/core/json-keyed-resource-controller.js.map +1 -0
  205. package/build-ngc/core/resolve-source.d.ts +2 -7
  206. package/build-ngc/core/resolve-source.js +69 -15
  207. package/build-ngc/core/resolve-source.js.map +1 -1
  208. package/build-ngc/core/subscribe-single-event.d.ts +5 -0
  209. package/build-ngc/core/subscribe-single-event.js +9 -0
  210. package/build-ngc/core/subscribe-single-event.js.map +1 -0
  211. package/build-ngc/core/types.d.ts +11 -5
  212. package/build-ngc/core/types.js.map +1 -1
  213. package/package.json +24 -23
  214. package/src/angular/index.ts +8 -3
  215. package/src/angular/inject-audio-player-status.ts +11 -0
  216. package/src/angular/inject-audio-player.ts +17 -0
  217. package/src/angular/inject-audio-playlist-status.ts +11 -0
  218. package/src/angular/inject-audio-playlist.ts +16 -0
  219. package/src/angular/inject-audio-recorder-state.ts +21 -0
  220. package/src/angular/inject-audio-recorder.ts +24 -0
  221. package/src/angular/inject-audio-sample-listener.ts +13 -0
  222. package/src/angular/inject-audio-stream.ts +41 -0
  223. package/src/core/audio-module.ts +2 -1
  224. package/src/core/audio-player-controller.ts +25 -0
  225. package/src/core/audio-player.ts +17 -4
  226. package/src/core/audio-playlist-controller.ts +14 -0
  227. package/src/core/audio-recorder-controller.ts +14 -0
  228. package/src/core/audio-recorder-polling.ts +20 -0
  229. package/src/core/audio-recorder-state.ts +26 -0
  230. package/src/core/audio-recorder-status.ts +22 -0
  231. package/src/core/audio-recorder.ts +5 -0
  232. package/src/core/audio-sample-listener.ts +23 -0
  233. package/src/core/audio-stream-controller.ts +14 -0
  234. package/src/core/audio-stream-hooks.ts +31 -0
  235. package/src/core/audio-stream-lifecycle.ts +19 -0
  236. package/src/core/audio-stream-subscription.ts +28 -0
  237. package/src/core/audio-stream.ts +11 -10
  238. package/src/core/index.ts +20 -1
  239. package/src/core/json-keyed-resource-controller.ts +1 -0
  240. package/src/core/resolve-source.ts +80 -18
  241. package/src/core/subscribe-single-event.ts +17 -0
  242. package/src/core/types.ts +13 -14
  243. package/src/react/index.ts +9 -0
  244. package/src/react/use-audio-player-status.ts +11 -0
  245. package/src/react/use-audio-player.ts +14 -0
  246. package/src/react/use-audio-playlist-status.ts +11 -0
  247. package/src/react/use-audio-playlist.ts +13 -0
  248. package/src/react/use-audio-recorder-state.ts +23 -0
  249. package/src/react/use-audio-recorder.ts +23 -0
  250. package/src/react/use-audio-sample-listener.ts +15 -0
  251. package/src/react/use-audio-stream.ts +39 -0
  252. package/src/solid/index.ts +9 -0
  253. package/src/solid/use-audio-player-status.ts +11 -0
  254. package/src/solid/use-audio-player.ts +15 -0
  255. package/src/solid/use-audio-playlist-status.ts +11 -0
  256. package/src/solid/use-audio-playlist.ts +14 -0
  257. package/src/solid/use-audio-recorder-state.ts +18 -0
  258. package/src/solid/use-audio-recorder.ts +22 -0
  259. package/src/solid/use-audio-sample-listener.ts +14 -0
  260. package/src/solid/use-audio-stream.ts +34 -0
  261. package/src/svelte/index.ts +9 -0
  262. package/src/svelte/svelte-ambient.d.ts +3 -0
  263. package/src/svelte/svelte-compile.test-helper.ts +7 -0
  264. package/src/svelte/use-audio-player-status.svelte.ts +11 -0
  265. package/src/svelte/use-audio-player.svelte.ts +20 -0
  266. package/src/svelte/use-audio-playlist-status.svelte.ts +11 -0
  267. package/src/svelte/use-audio-playlist.svelte.ts +19 -0
  268. package/src/svelte/use-audio-recorder-state.svelte.ts +29 -0
  269. package/src/svelte/use-audio-recorder.svelte.ts +25 -0
  270. package/src/svelte/use-audio-sample-listener.svelte.ts +11 -0
  271. package/src/svelte/use-audio-stream.svelte.ts +44 -0
  272. package/src/vue/index.ts +9 -0
  273. package/src/vue/use-audio-player-status.ts +11 -0
  274. package/src/vue/use-audio-player.ts +19 -0
  275. package/src/vue/use-audio-playlist-status.ts +11 -0
  276. package/src/vue/use-audio-playlist.ts +15 -0
  277. package/src/vue/use-audio-recorder-state.ts +21 -0
  278. package/src/vue/use-audio-recorder.ts +30 -0
  279. package/src/vue/use-audio-sample-listener.ts +22 -0
  280. package/src/vue/use-audio-stream.ts +40 -0
@@ -1,19 +1,20 @@
1
1
  import { expoAudio } from './native-module';
2
- import type { IAudioStreamOptions } from './types';
2
+ import type { IAudioStreamBuffer, IAudioStreamOptions } from './types';
3
3
 
4
4
  // Plain re-export — upstream has no prototype shim for `AudioStream` either.
5
5
  export const AudioStream = expoAudio.AudioStream;
6
6
  export type AudioStream = InstanceType<typeof AudioStream>;
7
7
 
8
- /**
9
- * Creates a native `AudioStream` for real-time PCM microphone capture. Call `.start()` to
10
- * begin and `.stop()` to end; requires microphone permission — see
11
- * `requestRecordingPermissionsAsync`. Upstream only exposes this via the `useAudioStream` hook
12
- * (React-only, see the package README); this factory is the framework-agnostic equivalent — the
13
- * hook itself just applies these same defaults and wires `addListener` for
14
- * `AUDIO_STREAM_STATUS`/`AUDIO_STREAM_BUFFER` on top, which each adapter's own lifecycle wrapper
15
- * is responsible for.
16
- */
8
+ export type IUseAudioStreamOptions = IAudioStreamOptions & {
9
+ onBuffer?: (buffer: IAudioStreamBuffer) => void;
10
+ };
11
+
12
+ export type IAudioStreamResult = {
13
+ stream: AudioStream;
14
+ isStreaming: boolean;
15
+ };
16
+
17
+ /** Requires microphone permission - see `requestRecordingPermissionsAsync`. */
17
18
  export function createAudioStream(
18
19
  options: IAudioStreamOptions = {},
19
20
  ): AudioStream {
package/src/core/index.ts CHANGED
@@ -1,7 +1,19 @@
1
1
  export { AudioPlayer, createAudioPlayer } from './audio-player';
2
- export { AudioRecorder } from './audio-recorder';
2
+ export { AudioRecorder, createAudioRecorder } from './audio-recorder';
3
3
  export { AudioPlaylist, createAudioPlaylist } from './audio-playlist';
4
4
  export { AudioStream, createAudioStream } from './audio-stream';
5
+ export type {
6
+ IUseAudioStreamOptions,
7
+ IAudioStreamResult,
8
+ } from './audio-stream';
9
+ export { createAudioStreamController } from './audio-stream-controller';
10
+ export {
11
+ subscribeAudioStreamBuffer,
12
+ toAudioStreamKey,
13
+ } from './audio-stream-subscription';
14
+ export type { IAudioStreamBufferSource } from './audio-stream-subscription';
15
+ export { runAudioStreamBufferEffect } from './audio-stream-lifecycle';
16
+ export { createAudioStreamHooks } from './audio-stream-hooks';
5
17
  export {
6
18
  setIsAudioActiveAsync,
7
19
  setAudioModeAsync,
@@ -14,6 +26,12 @@ export {
14
26
  getPreloadedSources,
15
27
  } from './audio-module';
16
28
  export { resolveSource, resolveSources } from './resolve-source';
29
+ export { subscribeAudioSampleListener } from './audio-sample-listener';
30
+ export type { IAudioSamplePlayer } from './audio-sample-listener';
31
+ export { subscribeRecordingStatus } from './audio-recorder-status';
32
+ export type { IStatusRecorder } from './audio-recorder-status';
33
+ export { shouldUpdateRecorderState } from './audio-recorder-state';
34
+ export { pollRecorderState } from './audio-recorder-polling';
17
35
  export {
18
36
  RecordingPresets,
19
37
  IOSOutputFormat,
@@ -32,6 +50,7 @@ export type {
32
50
  IAudioSource,
33
51
  IAudioSourceInfo,
34
52
  IAudioPlayerOptions,
53
+ IAudioLoadOptions,
35
54
  IPreloadOptions,
36
55
  IRecordingInput,
37
56
  IPitchCorrectionQuality,
@@ -0,0 +1 @@
1
+ export { createJsonKeyedResourceController } from '@symbiote-native/engine';
@@ -1,34 +1,74 @@
1
- import { resolveAssetSource } from '@symbiote-native/engine';
1
+ // Ported from expo-audio/src/utils/resolveSource.ts (sdk-57), against @symbiote-native/asset's
2
+ // Asset class instead of importing expo-asset directly (same pattern as packages/font).
3
+ import { Asset } from '@symbiote-native/asset';
4
+
2
5
  import type { IAudioSource } from './types';
3
6
 
4
- type IResolvedAudioSourceObject = Exclude<IAudioSource, string | number | null>;
7
+ type IResolvedAudioSourceObject = Exclude<
8
+ IAudioSource,
9
+ string | number | Asset | null
10
+ >;
11
+
12
+ function getAssetFromSource(source?: IAudioSource): Asset | null {
13
+ if (!source) {
14
+ return null;
15
+ }
16
+ if (source instanceof Asset) {
17
+ return source;
18
+ }
19
+ if (typeof source === 'number') {
20
+ return Asset.fromModule(source);
21
+ }
22
+ if (typeof source === 'object') {
23
+ if ('assetId' in source && typeof source.assetId === 'number') {
24
+ return Asset.fromModule(source.assetId);
25
+ }
26
+ if ('uri' in source && typeof source.uri === 'string') {
27
+ return Asset.fromURI(source.uri);
28
+ }
29
+ }
30
+ if (typeof source === 'string') {
31
+ return Asset.fromURI(source);
32
+ }
33
+ return null;
34
+ }
5
35
 
6
- // resolveAssetSource returns `unknown` by design (the seam serves every require()'d-asset
7
- // consumer, not just this package) — narrow it instead of casting.
8
- function isAudioSourceObject(
9
- value: unknown,
10
- ): value is IResolvedAudioSourceObject {
11
- return typeof value === 'object' && value !== null;
36
+ function createSourceFromAsset(
37
+ asset: Asset,
38
+ extras: { assetId?: number; headers?: Record<string, string> } = {},
39
+ ): IResolvedAudioSourceObject {
40
+ const result: IResolvedAudioSourceObject = {
41
+ uri: asset.localUri ?? asset.uri,
42
+ };
43
+ if (asset.name) {
44
+ result.name = asset.name;
45
+ }
46
+ if (extras.assetId != null) {
47
+ result.assetId = extras.assetId;
48
+ }
49
+ if (extras.headers) {
50
+ result.headers = extras.headers;
51
+ }
52
+ return result;
12
53
  }
13
54
 
14
- /**
15
- * Normalizes a source into the shape the native player accepts. A `number` (a `require()`'d
16
- * local asset) resolves via `@symbiote-native/engine`'s `resolveAssetSource` seam — RN's own
17
- * generic asset-registry lookup, wired by `bootstrapHost`. An actual `expo-asset` `Asset`
18
- * instance is still not ported, see `types.ts`'s header comment on `IAudioSource`. A plain
19
- * string is treated as a URI, matching upstream.
20
- */
21
55
  export function resolveSource(source?: IAudioSource): IAudioSource | null {
22
56
  if (source == null) {
23
57
  return null;
24
58
  }
25
- if (typeof source === 'number') {
26
- const resolved = resolveAssetSource(source);
27
- return isAudioSourceObject(resolved) ? resolved : null;
59
+ if (source instanceof Asset) {
60
+ return createSourceFromAsset(source);
28
61
  }
29
62
  if (typeof source === 'string') {
30
63
  return { uri: source };
31
64
  }
65
+ if (typeof source === 'number') {
66
+ return createSourceFromAsset(Asset.fromModule(source), { assetId: source });
67
+ }
68
+ if ('assetId' in source && typeof source.assetId === 'number') {
69
+ const asset = Asset.fromModule(source.assetId);
70
+ return { ...source, uri: asset.localUri ?? asset.uri };
71
+ }
32
72
  return source;
33
73
  }
34
74
 
@@ -39,3 +79,25 @@ export function resolveSources(
39
79
  .map(source => resolveSource(source))
40
80
  .filter((source): source is NonNullable<IAudioSource> => source != null);
41
81
  }
82
+
83
+ /** Resolves `source` and, unlike `resolveSource`, downloads it to a local cache file first. */
84
+ export async function resolveSourceWithDownload(
85
+ source?: IAudioSource,
86
+ ): Promise<IAudioSource | null> {
87
+ const asset = getAssetFromSource(source);
88
+ const fallback = resolveSource(source);
89
+ if (!asset) {
90
+ return fallback;
91
+ }
92
+ try {
93
+ await asset.downloadAsync();
94
+ if (asset.localUri) {
95
+ return fallback && typeof fallback === 'object'
96
+ ? { ...fallback, uri: asset.localUri }
97
+ : { uri: asset.localUri };
98
+ }
99
+ } catch {
100
+ // Falls through to the un-downloaded source below, matching upstream's own recovery path.
101
+ }
102
+ return fallback;
103
+ }
@@ -0,0 +1,17 @@
1
+ // Shared shape behind `subscribeRecordingStatus`/`subscribeAudioStreamBuffer`: one optional
2
+ // listener over one named event, unsubscribed via the returned cleanup
3
+ export function subscribeSingleEvent<TEvent extends string, TPayload>(
4
+ source: {
5
+ addListener: (
6
+ event: TEvent,
7
+ listener: (payload: TPayload) => void,
8
+ ) => { remove: () => void };
9
+ },
10
+ event: TEvent,
11
+ listener: ((payload: TPayload) => void) | undefined,
12
+ ): () => void {
13
+ const subscription = source.addListener(event, payload => {
14
+ listener?.(payload);
15
+ });
16
+ return () => subscription.remove();
17
+ }
package/src/core/types.ts CHANGED
@@ -2,16 +2,8 @@
2
2
  // RecordingConstants.ts / AudioStream.types.ts / AudioEventKeys.ts, renamed with this repo's
3
3
  // `I`-prefix convention for object/union-shaped exported types (enums keep their upstream name,
4
4
  // matching packages/local-auth's AuthenticationType/SecurityLevel).
5
- //
6
- // `IAudioSource`'s `number` variant (a `require()`'d local asset) IS supported: it resolves via
7
- // `@symbiote-native/engine`'s `resolveAssetSource` seam, which wraps RN's own generic
8
- // `Image.resolveAssetSource` — an ordinary Metro asset-registry lookup, nothing image-specific
9
- // despite the name, and `mp3` is already in Metro's default `assetExts`. Only an actual `Asset`
10
- // CLASS instance (as opposed to the plain numeric id `require()` returns) is deliberately NOT
11
- // ported, along with the `downloadFirst` player option — both need `expo-asset`'s own
12
- // remote-caching layer, which pulls in `expo-constants` and peers on the `expo` meta-package this
13
- // project never depends on (root CLAUDE.md's dependency-scope invariant, and the
14
- // `symbiote-expo-native-module` skill §1). See the package README.
5
+ import type { Asset } from '@symbiote-native/asset';
6
+
15
7
  import type { AudioQuality, IOSOutputFormat } from './recording-presets';
16
8
 
17
9
  /** Audio source information returned from native when reading sources from a queue. */
@@ -20,16 +12,16 @@ export type IAudioSourceInfo = {
20
12
  name?: string;
21
13
  };
22
14
 
23
- /**
24
- * A URI string, an object describing a remote/local (by URI) audio source, or the numeric asset
25
- * id `require('./song.mp3')` produces — resolved via `resolveSource`/`resolveSources`.
26
- */
15
+ /** A URI, a `require()` module id, an `Asset`, or a source object — see resolve-source.ts. */
27
16
  export type IAudioSource =
28
17
  | string
29
18
  | number
19
+ | Asset
30
20
  | null
31
21
  | {
32
22
  uri?: string;
23
+ /** id of a local asset from `require()`; ignored if `uri` is set. */
24
+ assetId?: number;
33
25
  headers?: Record<string, string>;
34
26
  name?: string;
35
27
  };
@@ -52,8 +44,13 @@ export type IAudioPlayerOptions = {
52
44
  * @default 0
53
45
  */
54
46
  preferredForwardBufferDuration?: number;
47
+ /** Downloads the source via `Asset` before the native player loads it. @default false */
48
+ downloadFirst?: boolean;
55
49
  };
56
50
 
51
+ /** @deprecated Use `IAudioPlayerOptions` instead */
52
+ export type IAudioLoadOptions = IAudioPlayerOptions;
53
+
57
54
  export type IPreloadOptions = {
58
55
  /** @default 10 */
59
56
  preferredForwardBufferDuration?: number;
@@ -190,6 +187,8 @@ export type IAudioMode = {
190
187
  playsInSilentMode: boolean;
191
188
  /** @default 'mixWithOthers' */
192
189
  interruptionMode: IInterruptionMode;
190
+ /** @platform android @deprecated Use `interruptionMode`, which now works on both platforms */
191
+ interruptionModeAndroid?: IInterruptionMode;
193
192
  /** @default false @platform ios */
194
193
  allowsRecording: boolean;
195
194
  /** @default false */
@@ -0,0 +1,9 @@
1
+ export * from '../core';
2
+ export { useAudioPlayer } from './use-audio-player';
3
+ export { useAudioPlayerStatus } from './use-audio-player-status';
4
+ export { useAudioSampleListener } from './use-audio-sample-listener';
5
+ export { useAudioPlaylist } from './use-audio-playlist';
6
+ export { useAudioPlaylistStatus } from './use-audio-playlist-status';
7
+ export { useAudioRecorder } from './use-audio-recorder';
8
+ export { useAudioRecorderState } from './use-audio-recorder-state';
9
+ export { useAudioStream } from './use-audio-stream';
@@ -0,0 +1,11 @@
1
+ import { createEventValueHook } from '@symbiote-native/react';
2
+ import { PLAYBACK_STATUS_UPDATE } from '../core/types';
3
+ import type { AudioPlayer } from '../core';
4
+ import type { IAudioStatus } from '../core';
5
+
6
+ /** React twin of `expo-audio`'s `useAudioPlayerStatus` */
7
+ export const useAudioPlayerStatus = createEventValueHook<
8
+ AudioPlayer,
9
+ IAudioStatus,
10
+ typeof PLAYBACK_STATUS_UPDATE
11
+ >(PLAYBACK_STATUS_UPDATE, player => player.currentStatus);
@@ -0,0 +1,14 @@
1
+ import { createResourceHook } from '@symbiote-native/react';
2
+ import { createAudioPlayerController } from '../core/audio-player-controller';
3
+ import type { AudioPlayer } from '../core';
4
+ import type { IAudioPlayerOptions, IAudioSource } from '../core';
5
+
6
+ const usePlayerResource = createResourceHook(createAudioPlayerController);
7
+
8
+ // React twin of `expo-audio`'s `useAudioPlayer`
9
+ export function useAudioPlayer(
10
+ source: IAudioSource = null,
11
+ options: IAudioPlayerOptions = {},
12
+ ): AudioPlayer {
13
+ return usePlayerResource(source, options);
14
+ }
@@ -0,0 +1,11 @@
1
+ import { createEventValueHook } from '@symbiote-native/react';
2
+ import { PLAYLIST_STATUS_UPDATE } from '../core/types';
3
+ import type { AudioPlaylist } from '../core';
4
+ import type { IAudioPlaylistStatus } from '../core';
5
+
6
+ /** React twin of `expo-audio`'s `useAudioPlaylistStatus` */
7
+ export const useAudioPlaylistStatus = createEventValueHook<
8
+ AudioPlaylist,
9
+ IAudioPlaylistStatus,
10
+ typeof PLAYLIST_STATUS_UPDATE
11
+ >(PLAYLIST_STATUS_UPDATE, playlist => playlist.currentStatus);
@@ -0,0 +1,13 @@
1
+ import { createResourceHook } from '@symbiote-native/react';
2
+ import { createAudioPlaylistController } from '../core/audio-playlist-controller';
3
+ import type { AudioPlaylist } from '../core';
4
+ import type { IAudioPlaylistOptions } from '../core';
5
+
6
+ const usePlaylistResource = createResourceHook(createAudioPlaylistController);
7
+
8
+ /** React twin of `expo-audio`'s `useAudioPlaylist` */
9
+ export function useAudioPlaylist(
10
+ options: IAudioPlaylistOptions = {},
11
+ ): AudioPlaylist {
12
+ return usePlaylistResource(options);
13
+ }
@@ -0,0 +1,23 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { pollRecorderState } from '../core/audio-recorder-polling';
3
+ import type { AudioRecorder } from '../core';
4
+ import type { IRecorderState } from '../core';
5
+
6
+ const DEFAULT_POLL_INTERVAL_MS = 500;
7
+
8
+ /** React twin of `expo-audio`'s `useAudioRecorderState` */
9
+ export function useAudioRecorderState(
10
+ recorder: AudioRecorder,
11
+ interval: number = DEFAULT_POLL_INTERVAL_MS,
12
+ ): IRecorderState {
13
+ const [state, setState] = useState<IRecorderState>(() =>
14
+ recorder.getStatus(),
15
+ );
16
+
17
+ useEffect(
18
+ () => pollRecorderState(recorder, interval, setState),
19
+ [recorder, interval],
20
+ );
21
+
22
+ return state;
23
+ }
@@ -0,0 +1,23 @@
1
+ import { useEffect } from 'react';
2
+ import { createResourceHook } from '@symbiote-native/react';
3
+ import { createAudioRecorderController } from '../core/audio-recorder-controller';
4
+ import { subscribeRecordingStatus } from '../core/audio-recorder-status';
5
+ import type { AudioRecorder } from '../core';
6
+ import type { IRecordingOptions, IRecordingStatus } from '../core';
7
+
8
+ const useRecorderResource = createResourceHook(createAudioRecorderController);
9
+
10
+ /** React twin of `expo-audio`'s `useAudioRecorder` */
11
+ export function useAudioRecorder(
12
+ options: IRecordingOptions,
13
+ statusListener?: (status: IRecordingStatus) => void,
14
+ ): AudioRecorder {
15
+ const recorder = useRecorderResource(options);
16
+
17
+ useEffect(
18
+ () => subscribeRecordingStatus(recorder, statusListener),
19
+ [recorder, statusListener],
20
+ );
21
+
22
+ return recorder;
23
+ }
@@ -0,0 +1,15 @@
1
+ import { useEffect } from 'react';
2
+ import { subscribeAudioSampleListener } from '../core/audio-sample-listener';
3
+ import type { IAudioSamplePlayer } from '../core/audio-sample-listener';
4
+ import type { IAudioSample } from '../core/types';
5
+
6
+ /** React twin of `expo-audio`'s `useAudioSampleListener` */
7
+ export function useAudioSampleListener(
8
+ player: IAudioSamplePlayer,
9
+ listener: (data: IAudioSample) => void,
10
+ ): void {
11
+ useEffect(
12
+ () => subscribeAudioSampleListener(player, listener),
13
+ [player, listener],
14
+ );
15
+ }
@@ -0,0 +1,39 @@
1
+ import { useEffect } from 'react';
2
+ import {
3
+ createResourceHook,
4
+ createEventValueHook,
5
+ } from '@symbiote-native/react';
6
+ import { createAudioStreamController } from '../core/audio-stream-controller';
7
+ import {
8
+ subscribeAudioStreamBuffer,
9
+ toAudioStreamKey,
10
+ } from '../core/audio-stream-subscription';
11
+ import { AUDIO_STREAM_STATUS } from '../core/types';
12
+ import type {
13
+ AudioStream,
14
+ IAudioStreamResult,
15
+ IAudioStreamStatus,
16
+ IUseAudioStreamOptions,
17
+ } from '../core';
18
+
19
+ const useStreamResource = createResourceHook(createAudioStreamController);
20
+ const useStreamStatus = createEventValueHook<
21
+ AudioStream,
22
+ IAudioStreamStatus,
23
+ typeof AUDIO_STREAM_STATUS
24
+ >(AUDIO_STREAM_STATUS, stream => ({ isStreaming: stream.isStreaming }));
25
+
26
+ /** React twin of `expo-audio`'s `useAudioStream` */
27
+ export function useAudioStream(
28
+ options: IUseAudioStreamOptions = {},
29
+ ): IAudioStreamResult {
30
+ const stream = useStreamResource(toAudioStreamKey(options));
31
+ const status = useStreamStatus(stream);
32
+
33
+ useEffect(
34
+ () => subscribeAudioStreamBuffer(stream, options.onBuffer),
35
+ [stream, options.onBuffer],
36
+ );
37
+
38
+ return { stream, isStreaming: status.isStreaming };
39
+ }
@@ -0,0 +1,9 @@
1
+ export * from '../core';
2
+ export { useAudioPlayer } from './use-audio-player';
3
+ export { useAudioPlayerStatus } from './use-audio-player-status';
4
+ export { useAudioSampleListener } from './use-audio-sample-listener';
5
+ export { useAudioPlaylist } from './use-audio-playlist';
6
+ export { useAudioPlaylistStatus } from './use-audio-playlist-status';
7
+ export { useAudioRecorder } from './use-audio-recorder';
8
+ export { useAudioRecorderState } from './use-audio-recorder-state';
9
+ export { useAudioStream } from './use-audio-stream';
@@ -0,0 +1,11 @@
1
+ import { createEventValueHook } from '@symbiote-native/solid';
2
+ import { PLAYBACK_STATUS_UPDATE } from '../core/types';
3
+ import type { AudioPlayer } from '../core';
4
+ import type { IAudioStatus } from '../core';
5
+
6
+ /** Solid twin of `expo-audio`'s `useAudioPlayerStatus` */
7
+ export const useAudioPlayerStatus = createEventValueHook<
8
+ AudioPlayer,
9
+ IAudioStatus,
10
+ typeof PLAYBACK_STATUS_UPDATE
11
+ >(PLAYBACK_STATUS_UPDATE, player => player.currentStatus);
@@ -0,0 +1,15 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { createResourceHook } from '@symbiote-native/solid';
3
+ import { createAudioPlayerController } from '../core/audio-player-controller';
4
+ import type { AudioPlayer } from '../core';
5
+ import type { IAudioPlayerOptions, IAudioSource } from '../core';
6
+
7
+ const usePlayerResource = createResourceHook(createAudioPlayerController);
8
+
9
+ /** Solid twin of `expo-audio`'s `useAudioPlayer` */
10
+ export function useAudioPlayer(
11
+ source: Accessor<IAudioSource>,
12
+ options: Accessor<IAudioPlayerOptions> = () => ({}),
13
+ ): Accessor<AudioPlayer> {
14
+ return usePlayerResource(() => [source(), options()]);
15
+ }
@@ -0,0 +1,11 @@
1
+ import { createEventValueHook } from '@symbiote-native/solid';
2
+ import { PLAYLIST_STATUS_UPDATE } from '../core/types';
3
+ import type { AudioPlaylist } from '../core';
4
+ import type { IAudioPlaylistStatus } from '../core';
5
+
6
+ /** Solid twin of `expo-audio`'s `useAudioPlaylistStatus` */
7
+ export const useAudioPlaylistStatus = createEventValueHook<
8
+ AudioPlaylist,
9
+ IAudioPlaylistStatus,
10
+ typeof PLAYLIST_STATUS_UPDATE
11
+ >(PLAYLIST_STATUS_UPDATE, playlist => playlist.currentStatus);
@@ -0,0 +1,14 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { createResourceHook } from '@symbiote-native/solid';
3
+ import { createAudioPlaylistController } from '../core/audio-playlist-controller';
4
+ import type { AudioPlaylist } from '../core';
5
+ import type { IAudioPlaylistOptions } from '../core';
6
+
7
+ const usePlaylistResource = createResourceHook(createAudioPlaylistController);
8
+
9
+ /** Solid twin of `expo-audio`'s `useAudioPlaylist` */
10
+ export function useAudioPlaylist(
11
+ getOptions: Accessor<IAudioPlaylistOptions>,
12
+ ): Accessor<AudioPlaylist> {
13
+ return usePlaylistResource(() => [getOptions()]);
14
+ }
@@ -0,0 +1,18 @@
1
+ import { createSignal, onCleanup, type Accessor } from 'solid-js';
2
+ import { pollRecorderState } from '../core/audio-recorder-polling';
3
+ import type { AudioRecorder } from '../core';
4
+ import type { IRecorderState } from '../core';
5
+
6
+ const DEFAULT_POLL_INTERVAL_MS = 500;
7
+
8
+ /** Solid twin of `expo-audio`'s `useAudioRecorderState` */
9
+ export function useAudioRecorderState(
10
+ recorder: AudioRecorder,
11
+ interval: number = DEFAULT_POLL_INTERVAL_MS,
12
+ ): Accessor<IRecorderState> {
13
+ const [state, setState] = createSignal(recorder.getStatus());
14
+
15
+ onCleanup(pollRecorderState(recorder, interval, setState));
16
+
17
+ return state;
18
+ }
@@ -0,0 +1,22 @@
1
+ import { createEffect, onCleanup, type Accessor } from 'solid-js';
2
+ import { createResourceHook } from '@symbiote-native/solid';
3
+ import { createAudioRecorderController } from '../core/audio-recorder-controller';
4
+ import { subscribeRecordingStatus } from '../core/audio-recorder-status';
5
+ import type { AudioRecorder } from '../core';
6
+ import type { IRecordingOptions, IRecordingStatus } from '../core';
7
+
8
+ const useRecorderResource = createResourceHook(createAudioRecorderController);
9
+
10
+ /** Solid twin of `expo-audio`'s `useAudioRecorder` */
11
+ export function useAudioRecorder(
12
+ getOptions: Accessor<IRecordingOptions>,
13
+ statusListener?: (status: IRecordingStatus) => void,
14
+ ): Accessor<AudioRecorder> {
15
+ const recorder = useRecorderResource(() => [getOptions()]);
16
+
17
+ createEffect(() => {
18
+ onCleanup(subscribeRecordingStatus(recorder(), statusListener));
19
+ });
20
+
21
+ return recorder;
22
+ }
@@ -0,0 +1,14 @@
1
+ import { createEffect, onCleanup } from 'solid-js';
2
+ import { subscribeAudioSampleListener } from '../core/audio-sample-listener';
3
+ import type { IAudioSamplePlayer } from '../core/audio-sample-listener';
4
+ import type { IAudioSample } from '../core/types';
5
+
6
+ /** Solid twin of `expo-audio`'s `useAudioSampleListener` */
7
+ export function useAudioSampleListener(
8
+ player: IAudioSamplePlayer,
9
+ listener: (data: IAudioSample) => void,
10
+ ): void {
11
+ createEffect(() => {
12
+ onCleanup(subscribeAudioSampleListener(player, listener));
13
+ });
14
+ }
@@ -0,0 +1,34 @@
1
+ import { createMemo, onCleanup, type Accessor } from 'solid-js';
2
+ import {
3
+ createResourceHook,
4
+ createEventValueHook,
5
+ } from '@symbiote-native/solid';
6
+ import { createAudioStreamHooks } from '../core/audio-stream-hooks';
7
+ import { toAudioStreamKey } from '../core/audio-stream-subscription';
8
+ import { runAudioStreamBufferEffect } from '../core/audio-stream-lifecycle';
9
+ import type { IAudioStreamResult, IUseAudioStreamOptions } from '../core';
10
+
11
+ const { useStreamResource, useStreamStatus } = createAudioStreamHooks(
12
+ createResourceHook,
13
+ createEventValueHook,
14
+ );
15
+
16
+ /** Solid twin of `expo-audio`'s `useAudioStream` */
17
+ export function useAudioStream(
18
+ getOptions: Accessor<IUseAudioStreamOptions>,
19
+ ): Accessor<IAudioStreamResult> {
20
+ const stream = useStreamResource(() => [toAudioStreamKey(getOptions())]);
21
+ const status = useStreamStatus(stream);
22
+
23
+ runAudioStreamBufferEffect(
24
+ stream,
25
+ () => getOptions().onBuffer,
26
+ fn => fn(),
27
+ onCleanup,
28
+ );
29
+
30
+ return createMemo(() => ({
31
+ stream: stream(),
32
+ isStreaming: status().isStreaming,
33
+ }));
34
+ }
@@ -0,0 +1,9 @@
1
+ export * from '../core';
2
+ export { useAudioPlayer } from './use-audio-player.svelte';
3
+ export { useAudioPlayerStatus } from './use-audio-player-status.svelte';
4
+ export { useAudioSampleListener } from './use-audio-sample-listener.svelte';
5
+ export { useAudioPlaylist } from './use-audio-playlist.svelte';
6
+ export { useAudioPlaylistStatus } from './use-audio-playlist-status.svelte';
7
+ export { useAudioRecorder } from './use-audio-recorder.svelte';
8
+ export { useAudioRecorderState } from './use-audio-recorder-state.svelte';
9
+ export { useAudioStream } from './use-audio-stream.svelte';
@@ -0,0 +1,3 @@
1
+ // Pulls svelte's ambient globals ($state/$effect/...) into this package's tsc program,
2
+ // same fix as `packages/navigation/src/svelte/svelte-ambient.d.ts`
3
+ /// <reference types="svelte" />
@@ -0,0 +1,7 @@
1
+ // Re-exports the existing harness instead of a third copy (packages/sqlite and
2
+ // packages/navigation already carry one, not part of either package's public exports)
3
+ export {
4
+ createSvelteHarness,
5
+ loadComponent,
6
+ type ISvelteHarness,
7
+ } from '../../../sqlite/src/svelte/svelte-compile.test-helper';
@@ -0,0 +1,11 @@
1
+ import { createEventValueHook } from '@symbiote-native/svelte/runes/create-event-value-hook';
2
+ import { PLAYBACK_STATUS_UPDATE } from '../core/types';
3
+ import type { AudioPlayer } from '../core';
4
+ import type { IAudioStatus } from '../core';
5
+
6
+ /** Svelte twin of `expo-audio`'s `useAudioPlayerStatus` */
7
+ export const useAudioPlayerStatus = createEventValueHook<
8
+ AudioPlayer,
9
+ IAudioStatus,
10
+ typeof PLAYBACK_STATUS_UPDATE
11
+ >(PLAYBACK_STATUS_UPDATE, player => player.currentStatus);