{"id":31581359,"url":"https://github.com/truemagic-coder/expo-streamer","last_synced_at":"2025-10-05T21:56:38.689Z","repository":{"id":315375084,"uuid":"1059223865","full_name":"truemagic-coder/expo-streamer","owner":"truemagic-coder","description":"Realtime Audio Streaming for Expo","archived":false,"fork":false,"pushed_at":"2025-09-30T04:41:11.000Z","size":7100,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-30T04:48:35.143Z","etag":null,"topics":["audio","expo"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/truemagic-coder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-18T06:55:14.000Z","updated_at":"2025-09-30T04:40:07.000Z","dependencies_parsed_at":"2025-09-18T09:22:31.407Z","dependency_job_id":"823c36ce-5325-44d8-872f-9d411093bf68","html_url":"https://github.com/truemagic-coder/expo-streamer","commit_stats":null,"previous_names":["truemagic-coder/expo-streamer"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/truemagic-coder/expo-streamer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truemagic-coder%2Fexpo-streamer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truemagic-coder%2Fexpo-streamer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truemagic-coder%2Fexpo-streamer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truemagic-coder%2Fexpo-streamer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/truemagic-coder","download_url":"https://codeload.github.com/truemagic-coder/expo-streamer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/truemagic-coder%2Fexpo-streamer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278526241,"owners_count":26001326,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","expo"],"created_at":"2025-10-05T21:56:36.667Z","updated_at":"2025-10-05T21:56:38.683Z","avatar_url":"https://github.com/truemagic-coder.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🎙️ expo-streamer\n\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n[![npm version](https://img.shields.io/npm/v/expo-streamer)](https://www.npmjs.com/package/expo-streamer)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/truemagic-coder/expo-streamer/ci.yml?branch=main)](https://github.com/truemagic-coder/expo-streamer/actions/workflows/ci.yml)\n[![codecov](https://img.shields.io/codecov/c/github/truemagic-coder/expo-streamer/main.svg)](https://codecov.io/gh/truemagic-coder/solana-agent)\n\n**Enterprise-grade audio streaming and recording for Expo applications**\n\n\u003e **Zero-crash reliability** • **Full TypeScript support** • **SOLID architecture** • **Production ready**\n\n## ✨ Why Choose expo-streamer?\n\n| Feature | Description |\n|---------|-------------|\n| 📘 **TypeScript First** | Full TypeScript support with comprehensive type definitions |\n| ⚡ **Thread Safe** | Proper synchronization for multi-threaded audio operations |\n| 🏗️ **SOLID Architecture** | Clean, maintainable code following industry best practices |\n| 🧪 **Fully Tested** | 100% test coverage with comprehensive test suites |\n| 📱 **Cross Platform** | Works seamlessly on iOS and Android |\n| 🎛️ **Real-time** | Low-latency audio streaming perfect for voice apps |\n\n## 🚀 Installation\n\n```bash\nnpm install expo-streamer\n# or\nyarn add expo-streamer\n```\n\n## 📘 TypeScript Usage\n\n### Basic Recording and Playback\n\n```typescript\nimport { \n  ExpoStreamer, \n  RecordingConfig, \n  AudioDataEvent,\n  RecordingEncodingTypes,\n  SampleRates \n} from 'expo-streamer';\n\n// Define recording configuration with full TypeScript support\nconst recordingConfig: RecordingConfig = {\n  sampleRate: SampleRates.SR_44100,\n  channels: 1,\n  encoding: RecordingEncodingTypes.PCM_16BIT,\n  interval: 250,\n  onAudioStream: (event: AudioDataEvent) =\u003e {\n    console.log('Audio data received:', {\n      data: event.data,\n      position: event.position,\n      soundLevel: event.soundLevel\n    });\n  }\n};\n\n// Start recording with type safety\nconst { recordingResult, subscription } = await ExpoStreamer.startRecording(recordingConfig);\n\n// Play audio with proper typing\nawait ExpoStreamer.playAudio(base64AudioData, 'turn-1', EncodingTypes.PCM_S16LE);\n\n// Stop recording\nconst recording = await ExpoStreamer.stopRecording();\n```\n\n### Advanced Configuration with Types\n\n```typescript\nimport { \n  ExpoStreamer, \n  SoundConfig, \n  PlaybackModes, \n  SampleRates,\n  EncodingTypes \n} from 'expo-streamer';\n\n// Configure audio playback with type safety\nconst soundConfig: SoundConfig = {\n  sampleRate: SampleRates.SR_44100,\n  playbackMode: PlaybackModes.VOICE_PROCESSING,\n  enableBuffering: true,\n  bufferConfig: {\n    targetBufferMs: 100,\n    maxBufferMs: 500,\n    minBufferMs: 50\n  }\n};\n\nawait ExpoStreamer.setSoundConfig(soundConfig);\n\n// Use typed encoding constants\nawait ExpoStreamer.playAudio(\n  audioData, \n  'turn-1', \n  EncodingTypes.PCM_S16LE\n);\n```\n\n### Voice-Optimized Configuration\n\n```typescript\n// Voice processing with 24000 Hz sample rate (recommended for voice applications)\nconst voiceConfig: RecordingConfig = {\n  sampleRate: SampleRates.SR_24000,  // Voice-optimized sample rate\n  channels: 1,                       // Mono for voice\n  encoding: RecordingEncodingTypes.PCM_16BIT,\n  interval: 50,                      // Fast response for real-time voice\n  voiceProcessing: true,             // Enable platform AEC/NS/AGC when needed\n  preGainDb: 6,                      // Optional gain boost for softer microphones\n  onAudioStream: async (event: AudioDataEvent) =\u003e {\n    // Process voice data with optimal settings\n    console.log('Voice data:', {\n      soundLevel: event.soundLevel,\n      dataLength: event.data.length\n    });\n  }\n};\n\nconst soundConfig: SoundConfig = {\n  sampleRate: SampleRates.SR_24000,\n  playbackMode: PlaybackModes.VOICE_PROCESSING,\n  enableBuffering: true,\n  bufferConfig: {\n    targetBufferMs: 50,   // Lower latency for voice\n    maxBufferMs: 200,\n    minBufferMs: 25\n  }\n};\n\nawait ExpoStreamer.startRecording(voiceConfig);\nawait ExpoStreamer.setSoundConfig(soundConfig);\n\n\u003e **Tip:** `voiceProcessing` now defaults to `false` so recordings capture the hotter, unprocessed microphone signal. Toggle it on when you need the built-in echo cancellation/noise suppression pipeline. Use `preGainDb` (range −24 dB to +24 dB) to fine-tune input loudness without clipping.\n```\n\n### Event Handling with TypeScript\n\n```typescript\nimport { \n  ExpoStreamer, \n  AudioDataEvent, \n  SoundChunkPlayedEventPayload \n} from 'expo-streamer';\n\n// Subscribe to audio events with proper typing\nconst subscription = ExpoStreamer.subscribeToAudioEvents(\n  async (event: AudioDataEvent) =\u003e {\n    console.log('Audio event:', {\n      data: event.data,\n      soundLevel: event.soundLevel,\n      position: event.position\n    });\n  }\n);\n\n// Subscribe to playback events\nconst playbackSubscription = ExpoStreamer.subscribeToSoundChunkPlayed(\n  async (event: SoundChunkPlayedEventPayload) =\u003e {\n    console.log('Chunk played:', {\n      isFinalChunk: event.isFinalChunk,\n      turnId: event.turnId\n    });\n  }\n);\n\n// Clean up subscriptions\nsubscription?.remove();\nplaybackSubscription?.remove();\n```\n\n## 📋 API Reference\n\n### Core Types\n\n```typescript\ninterface RecordingConfig {\n  sampleRate?: SampleRate;           // SampleRates.SR_16000 | SR_24000 | SR_44100 | SR_48000\n  channels?: number;                 // 1 (mono) or 2 (stereo)\n  encoding?: RecordingEncodingType;  // RecordingEncodingTypes.PCM_8BIT | PCM_16BIT | PCM_32BIT\n  interval?: number;                 // Callback interval in milliseconds\n  onAudioStream?: (event: AudioDataEvent) =\u003e void;\n}\n\ninterface AudioDataEvent {\n  data: string;        // Base64 encoded audio data\n  position: number;    // Position in the audio stream\n  soundLevel?: number; // Audio level for visualization\n  eventDataSize: number;\n  totalSize: number;\n}\n\ninterface SoundConfig {\n  sampleRate?: SampleRate;           // SampleRates.SR_16000 | SR_24000 | SR_44100 | SR_48000\n  playbackMode?: PlaybackMode;       // PlaybackModes.REGULAR | VOICE_PROCESSING | CONVERSATION\n  useDefault?: boolean;\n  enableBuffering?: boolean;\n  bufferConfig?: Partial\u003cIAudioBufferConfig\u003e;\n}\n\n// Available Enum Constants\nconst RecordingEncodingTypes = {\n  PCM_32BIT: 'pcm_32bit',\n  PCM_16BIT: 'pcm_16bit',\n  PCM_8BIT: 'pcm_8bit',\n} as const;\n\nconst SampleRates = {\n  SR_16000: 16000,\n  SR_24000: 24000,\n  SR_44100: 44100,\n  SR_48000: 48000,\n} as const;\n\nconst PlaybackModes = {\n  REGULAR: 'regular',\n  VOICE_PROCESSING: 'voiceProcessing',\n  CONVERSATION: 'conversation',\n} as const;\n\nconst EncodingTypes = {\n  PCM_F32LE: 'pcm_f32le',\n  PCM_S16LE: 'pcm_s16le',\n} as const;\n```\n\n### Recording Methods\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `startRecording(config: RecordingConfig)` | `Promise\u003cStartRecordingResult\u003e` | Start microphone recording |\n| `stopRecording()` | `Promise\u003cAudioRecording\u003e` | Stop recording and return data |\n| `pauseRecording()` | `Promise\u003cvoid\u003e` | Pause current recording |\n| `resumeRecording()` | `Promise\u003cvoid\u003e` | Resume paused recording |\n\n### Playback Methods\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `playAudio(data: string, turnId: string, encoding?: Encoding)` | `Promise\u003cvoid\u003e` | Play base64 audio data |\n| `pauseAudio()` | `Promise\u003cvoid\u003e` | Pause current playback |\n| `stopAudio()` | `Promise\u003cvoid\u003e` | Stop all audio playback |\n| `clearPlaybackQueueByTurnId(turnId: string)` | `Promise\u003cvoid\u003e` | Clear queue for specific turn |\n\n### Configuration Methods\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `setSoundConfig(config: SoundConfig)` | `Promise\u003cvoid\u003e` | Configure audio playback |\n| `getPermissionsAsync()` | `Promise\u003cPermissionResponse\u003e` | Check microphone permissions |\n| `requestPermissionsAsync()` | `Promise\u003cPermissionResponse\u003e` | Request microphone permissions |\n\n## 🧪 Testing\n\n```bash\n# Run all tests with TypeScript checking\nnpm run test:all\n\n# Individual test suites\nnpm test                   # Jest (TypeScript)\nnpm run test:android       # Android test analysis\nnpm run test:ios           # iOS test guide\nnpm run test:coverage      # Coverage report\n```\n\n**Note**: Android and iOS native tests require running within an Expo app context due to module dependencies. The `test:android` command provides static analysis and validation of the Android test code structure.\n\n## 🏗️ Architecture\n\nBuilt with enterprise-grade patterns and full TypeScript support:\n\n- **🔒 Type Safety**: Comprehensive TypeScript definitions for all APIs\n- **🏛️ SOLID Principles**: Single responsibility, dependency injection, interface segregation  \n- **🧵 Thread Safety**: Proper synchronization with DispatchQueue (iOS) and Mutex (Android)\n- **🛡️ Error Handling**: Result types and graceful degradation\n- **💾 Memory Management**: Efficient buffer pooling and automatic cleanup\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n### Development Setup\n\n```bash\n# Clone and setup\ngit clone https://github.com/truemagic-coder/expo-streamer.git\ncd expo-streamer\nnpm install\n\n# Run example app\ncd example\nnpm run ios     # or npm run android\n```\n\n### Version Management\n\nThe package uses synchronized versioning across all platforms:\n\n- **npm package**: Version defined in `package.json`\n- **iOS podspec**: Automatically reads from `package.json` via `package['version']`\n- **Android gradle**: Automatically reads from `package.json` via `getPackageJsonVersion()`\n\nTo update the version, only change `package.json` - all other platforms will sync automatically.\n\n### Code Standards\n\n- Full TypeScript support with strict mode\n- Follow SOLID principles\n- Include comprehensive tests\n- Ensure thread safety\n- Document all public APIs\n\n## 📜 License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n**Acknowledgments**: This project is a hard fork based on original work by Alexander Demchuk, also under MIT License.\n\n## 📞 Support\n\n- 🐛 **Bug Reports**: [GitHub Issues](https://github.com/truemagic-coder/expo-streamer/issues)\n- 💡 **Feature Requests**: [GitHub Discussions](https://github.com/truemagic-coder/expo-streamer/discussions)\n- 📖 **Documentation**: [GitHub Wiki](https://github.com/truemagic-coder/expo-streamer/wiki)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n  \u003cstrong\u003eBuilt with ❤️ for the Expo community\u003c/strong\u003e\n\u003c/div\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftruemagic-coder%2Fexpo-streamer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftruemagic-coder%2Fexpo-streamer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftruemagic-coder%2Fexpo-streamer/lists"}