Browse Source

Add support for HTTP streaming audio

- Install CocoaPods (leaving deps out of source control)
- Add StreamingKit pod
- Create AudioManager React Native module
- Adapt Audio Stream adapter JS from 5e9fd90 of stetro/domradio-ios
- Integrate everything with our App component
- Start playing radio after component mounts
master
Josh Habdas 6 years ago
parent
commit
bcea55584c
11 changed files with 575 additions and 26 deletions
  1. +8
    -0
      .gitignore
  2. +7
    -0
      Podfile
  3. +10
    -0
      Podfile.lock
  4. +266
    -0
      Pods/StreamingKit/StreamingKit/StreamingKit/STKAudioPlayer.h
  5. +93
    -14
      ReactNativeEs6Reflux.xcodeproj/project.pbxproj
  6. +10
    -0
      ReactNativeEs6Reflux.xcworkspace/contents.xcworkspacedata
  7. +1
    -2
      iOS/AppDelegate.m
  8. +17
    -0
      iOS/AudioManager.h
  9. +78
    -0
      iOS/AudioManager.m
  10. +41
    -10
      src/components/app.jsx
  11. +44
    -0
      src/lib/bridges/audio-stream.js

+ 8
- 0
.gitignore View File

@ -22,6 +22,14 @@ DerivedData
*.xcuserstate
project.xcworkspace
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
# node.js
#
node_modules/

+ 7
- 0
Podfile View File

@ -0,0 +1,7 @@
source 'git@github.com:/CocoaPods/Specs.git'
platform :ios, '7.0'
pod 'StreamingKit', '~> 0.1'
link_with 'ReactNativeEs6Reflux'

+ 10
- 0
Podfile.lock View File

@ -0,0 +1,10 @@
PODS:
- StreamingKit (0.1.25)
DEPENDENCIES:
- StreamingKit (~> 0.1)
SPEC CHECKSUMS:
StreamingKit: f00c054cfd0f59d88c8be7a2270e208302ce48c4
COCOAPODS: 0.37.1

+ 266
- 0
Pods/StreamingKit/StreamingKit/StreamingKit/STKAudioPlayer.h View File

@ -0,0 +1,266 @@
/**********************************************************************************
AudioPlayer.m
Created by Thong Nguyen on 14/05/2012.
https://github.com/tumtumtum/StreamingKit
Inspired by Matt Gallagher's AudioStreamer:
https://github.com/mattgallagher/AudioStreamer
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
4. Neither the name of Thong Nguyen nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Thong Nguyen''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************************/
#import <Foundation/Foundation.h>
#import <pthread.h>
#import "STKDataSource.h"
#import <AudioToolbox/AudioToolbox.h>
#if TARGET_OS_IPHONE
#include "UIKit/UIApplication.h"
#endif
typedef enum
{
STKAudioPlayerStateReady,
STKAudioPlayerStateRunning = 1,
STKAudioPlayerStatePlaying = (1 << 1) | STKAudioPlayerStateRunning,
STKAudioPlayerStateBuffering = (1 << 2) | STKAudioPlayerStateRunning,
STKAudioPlayerStatePaused = (1 << 3) | STKAudioPlayerStateRunning,
STKAudioPlayerStateStopped = (1 << 4),
STKAudioPlayerStateError = (1 << 5),
STKAudioPlayerStateDisposed = (1 << 6)
}
STKAudioPlayerState;
typedef enum
{
STKAudioPlayerStopReasonNone = 0,
STKAudioPlayerStopReasonEof,
STKAudioPlayerStopReasonUserAction,
STKAudioPlayerStopReasonPendingNext,
STKAudioPlayerStopReasonDisposed,
STKAudioPlayerStopReasonError = 0xffff
}
STKAudioPlayerStopReason;
typedef enum
{
STKAudioPlayerErrorNone = 0,
STKAudioPlayerErrorDataSource,
STKAudioPlayerErrorStreamParseBytesFailed,
STKAudioPlayerErrorAudioSystemError,
STKAudioPlayerErrorCodecError,
STKAudioPlayerErrorDataNotFound,
STKAudioPlayerErrorOther = 0xffff
}
STKAudioPlayerErrorCode;
typedef struct
{
/// If YES then seeking a track will cause all pending items to be flushed from the queue
BOOL flushQueueOnSeek;
/// If YES then volume control will be enabled on iOS
BOOL enableVolumeMixer;
/// A pointer to a 0 terminated array of band frequencies (iOS 5.0 and later, OSX 10.9 and later)
Float32 equalizerBandFrequencies[24];
/// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger.
UInt32 readBufferSize;
/// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM)
UInt32 bufferSizeInSeconds;
/// Number of seconds of decompressed audio is required before playback first starts for each item (Default is 0.5 seconds. Must be larger than bufferSizeInSeconds)
Float32 secondsRequiredToStartPlaying;
/// Seconds after a seek is performed before data needs to come in (after which the state will change to playing/buffering)
Float32 gracePeriodAfterSeekInSeconds;
/// Number of seconds of decompressed audio required before playback resumes after a buffer underrun (Default is 5 seconds. Must be larger than bufferSizeinSeconds)
Float32 secondsRequiredToStartPlayingAfterBufferUnderun;
}
STKAudioPlayerOptions;
typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames);
@interface STKFrameFilterEntry : NSObject
@property (readonly) NSString* name;
@property (readonly) STKFrameFilter filter;
@end
@class STKAudioPlayer;
@protocol STKAudioPlayerDelegate <NSObject>
/// Raised when an item has started playing
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId;
/// Raised when an item has finished buffering (may or may not be the currently playing item)
/// This event may be raised multiple times for the same item if seek is invoked on the player
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId;
/// Raised when the state of the player has changed
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState;
/// Raised when an item has finished playing
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration;
/// Raised when an unexpected and possibly unrecoverable error has occured (usually best to recreate the STKAudioPlauyer)
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode;
@optional
/// Optionally implemented to get logging information from the STKAudioPlayer (used internally for debugging)
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer logInfo:(NSString*)line;
/// Raised when items queued items are cleared (usually because of a call to play, setDataSource or stop)
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
@end
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
/// Gets or sets the volume (ranges 0 - 1.0).
/// On iOS the STKAudioPlayerOptionEnableMultichannelMixer option must be enabled for volume to work.
@property (readwrite) Float32 volume;
/// Gets or sets the player muted state
@property (readwrite) BOOL muted;
/// Gets the current item duration in seconds
@property (readonly) double duration;
/// Gets the current item progress in seconds
@property (readonly) double progress;
/// Enables or disables peak and average decibel meteting
@property (readwrite) BOOL meteringEnabled;
/// Enables or disables the EQ
@property (readwrite) BOOL equalizerEnabled;
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
@property (readonly) NSArray* frameFilters;
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
@property (readonly) NSArray* pendingQueue;
/// The number of items pending to be played (includes buffering and upcoming items but does not include the current item)
@property (readonly) NSUInteger pendingQueueCount;
/// Gets the most recently queued item that is still pending to play
@property (readonly) NSObject* mostRecentlyQueuedStillPendingItem;
/// Gets the current state of the player
@property (readwrite) STKAudioPlayerState state;
/// Gets the options provided to the player on startup
@property (readonly) STKAudioPlayerOptions options;
/// Gets the reason why the player is stopped (if any)
@property (readonly) STKAudioPlayerStopReason stopReason;
/// Gets and sets the delegate used for receiving events from the STKAudioPlayer
@property (readwrite, unsafe_unretained) id<STKAudioPlayerDelegate> delegate;
/// Creates a datasource from a given URL.
/// URLs with FILE schemes will return an STKLocalFileDataSource.
/// URLs with HTTP schemes will return an STKHTTPDataSource wrapped within an STKAutoRecoveringHTTPDataSource.
/// URLs with unrecognised schemes will return nil.
+(STKDataSource*) dataSourceFromURL:(NSURL*)url;
/// Initializes a new STKAudioPlayer with the default options
-(id) init;
/// Initializes a new STKAudioPlayer with the given options
-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn;
/// Plays an item from the given URL string (all pending queued items are removed).
/// The NSString is used as the queue item ID
-(void) play:(NSString*)urlString;
/// Plays an item from the given URL (all pending queued items are removed)
-(void) play:(NSString*)urlString withQueueItemID:(NSObject*)queueItemId;
/// Plays an item from the given URL (all pending queued items are removed)
/// The NSURL is used as the queue item ID
-(void) playURL:(NSURL*)url;
/// Plays an item from the given URL (all pending queued items are removed)
-(void) playURL:(NSURL*)url withQueueItemID:(NSObject*)queueItemId;
/// Plays the given item (all pending queued items are removed)
/// The STKDataSource is used as the queue item ID
-(void) playDataSource:(STKDataSource*)dataSource;
/// Plays the given item (all pending queued items are removed)
-(void) playDataSource:(STKDataSource*)dataSource withQueueItemID:(NSObject*)queueItemId;
/// Queues the URL string for playback and uses the NSString as the queueItemID
-(void) queue:(NSString*)urlString;
/// Queues the URL string for playback with the given queueItemID
-(void) queue:(NSString*)urlString withQueueItemId:(NSObject*)queueItemId;
/// Queues the URL for playback and uses the NSURL as the queueItemID
-(void) queueURL:(NSURL*)url;
/// Queues the URL for playback with the given queueItemID
-(void) queueURL:(NSURL*)url withQueueItemId:(NSObject*)queueItemId;
/// Queues a DataSource with the given queueItemId
-(void) queueDataSource:(STKDataSource*)dataSource withQueueItemId:(NSObject*)queueItemId;
/// Plays the given item (all pending queued items are removed)
-(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId;
/// Seeks to a specific time (in seconds)
-(void) seekToTime:(double)value;
/// Clears any upcoming items already queued for playback (does not stop the current item).
/// The didCancelItems event will be raised for the items removed from the queue.
-(void) clearQueue;
/// Pauses playback
-(void) pause;
/// Resumes playback from pause
-(void) resume;
/// Stops playback of the current file, flushes all the buffers and removes any pending queued items
-(void) stop;
/// Mutes playback
-(void) mute;
/// Unmutes playback
-(void) unmute;
/// Disposes the STKAudioPlayer and frees up all resources before returning
-(void) dispose;
/// The QueueItemId of the currently playing item
-(NSObject*) currentlyPlayingQueueItemId;
/// Removes a frame filter with the given name
-(void) removeFrameFilterWithName:(NSString*)name;
/// Appends a frame filter with the given name and filter block to the end of the filter chain
-(void) appendFrameFilterWithName:(NSString*)name block:(STKFrameFilter)block;
/// Appends a frame filter with the given name and filter block just after the filter with the given name.
/// If the given name is nil, the filter will be inserted at the beginning of the filter change
-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block;
/// Reads the peak power in decibals for the given channel (0 or 1).
/// Return values are between -60 (low) and 0 (high).
-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
/// Reads the average power in decibals for the given channel (0 or 1)
/// Return values are between -60 (low) and 0 (high).
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
@end

+ 93
- 14
ReactNativeEs6Reflux.xcodeproj/project.pbxproj View File

@ -16,13 +16,15 @@
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* ReactNativeEs6RefluxTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeEs6RefluxTests.m */; };
0BC1CA72532579F9E2CCB620 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 16163A4DE2078135E9435029 /* libPods.a */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
DD3B4C1B1AFDC1DA00BCD40D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
DD3B4C4D1AFDCD3400BCD40D /* AudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3B4C4C1AFDCD3400BCD40D /* AudioManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -106,14 +108,14 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = node_modules/react-native/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = "<group>"; };
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = "node_modules/react-native/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj"; sourceTree = "<group>"; };
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = "<group>"; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = node_modules/react-native/Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = node_modules/react-native/Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = "<group>"; };
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = "node_modules/react-native/Libraries/AdSupport/RCTAdSupport.xcodeproj"; sourceTree = "<group>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
00E356EE1AD99517003FC87E /* ReactNativeEs6RefluxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeEs6RefluxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* ReactNativeEs6RefluxTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeEs6RefluxTests.m; sourceTree = "<group>"; };
@ -124,9 +126,15 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = node_modules/react-native/React/React.xcodeproj; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = node_modules/react-native/Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
16163A4DE2078135E9435029 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
3BF3E2CDD07E93A19DFFB2CB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
40F8BDECA5F81ADA12E89DFE /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
DD3B4C341AFDC76C00BCD40D /* libPods-StreamingKit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-StreamingKit.a"; path = "Pods/../build/Debug-iphoneos/libPods-StreamingKit.a"; sourceTree = "<group>"; };
DD3B4C4B1AFDCD3400BCD40D /* AudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioManager.h; path = iOS/AudioManager.h; sourceTree = "<group>"; };
DD3B4C4C1AFDCD3400BCD40D /* AudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AudioManager.m; path = iOS/AudioManager.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -151,6 +159,7 @@
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
0BC1CA72532579F9E2CCB620 /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -240,6 +249,8 @@
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
DD3B4C4B1AFDCD3400BCD40D /* AudioManager.h */,
DD3B4C4C1AFDCD3400BCD40D /* AudioManager.m */,
);
name = ReactNativeEs6Reflux;
sourceTree = "<group>";
@ -252,6 +263,15 @@
name = Products;
sourceTree = "<group>";
};
6F0606FA8C62EE74A598B2DE /* Pods */ = {
isa = PBXGroup;
children = (
3BF3E2CDD07E93A19DFFB2CB /* Pods.debug.xcconfig */,
40F8BDECA5F81ADA12E89DFE /* Pods.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
78C398B11ACF4ADC00677621 /* Products */ = {
isa = PBXGroup;
children = (
@ -260,6 +280,15 @@
name = Products;
sourceTree = "<group>";
};
7BC1E3E98E014CD4DA009A1D /* Frameworks */ = {
isa = PBXGroup;
children = (
DD3B4C341AFDC76C00BCD40D /* libPods-StreamingKit.a */,
16163A4DE2078135E9435029 /* libPods.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
@ -292,6 +321,8 @@
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* ReactNativeEs6RefluxTests */,
83CBBA001A601CBA00E9B192 /* Products */,
6F0606FA8C62EE74A598B2DE /* Pods */,
7BC1E3E98E014CD4DA009A1D /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
@ -331,9 +362,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeEs6Reflux" */;
buildPhases = (
C047BA584CF9BC94F21DEB82 /* Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
8F1AFCEEA6D5A3000C85C300 /* Copy Pods Resources */,
);
buildRules = (
);
@ -513,6 +546,39 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
8F1AFCEEA6D5A3000C85C300 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C047BA584CF9BC94F21DEB82 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
00E356EA1AD99517003FC87E /* Sources */ = {
isa = PBXSourcesBuildPhase;
@ -526,8 +592,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
DD3B4C1B1AFDC1DA00BCD40D /* AppDelegate.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
DD3B4C4D1AFDCD3400BCD40D /* AudioManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -593,6 +660,7 @@
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3BF3E2CDD07E93A19DFFB2CB /* Pods.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
HEADER_SEARCH_PATHS = (
@ -602,13 +670,19 @@
);
INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/build/Debug-iphoneos",
);
OTHER_LDFLAGS = "$(inherited)";
OTHER_LIBTOOLFLAGS = "$(OTHER_LDFLAGS)";
PRODUCT_NAME = ReactNativeEs6Reflux;
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 40F8BDECA5F81ADA12E89DFE /* Pods.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
HEADER_SEARCH_PATHS = (
@ -618,7 +692,12 @@
);
INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/build/Debug-iphoneos",
);
OTHER_LDFLAGS = "$(inherited)";
OTHER_LIBTOOLFLAGS = "$(OTHER_LDFLAGS)";
PRODUCT_NAME = ReactNativeEs6Reflux;
};
name = Release;

+ 10
- 0
ReactNativeEs6Reflux.xcworkspace/contents.xcworkspacedata View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:ReactNativeEs6Reflux.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

+ 1
- 2
iOS/AppDelegate.m View File

@ -8,12 +8,11 @@
*/
#import "AppDelegate.h"
#import "RCTRootView.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;

+ 17
- 0
iOS/AudioManager.h View File

@ -0,0 +1,17 @@
//
// AudioManager.h
// ReactNativeEs6Reflux
//
// Created by Josh Habdas on 5/9/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
#import "STKAudioPlayer.h"
static NSString *const audioStream = @"http://50.7.99.155:7416/;stream/1";
@interface AudioManager : NSObject <RCTBridgeModule, STKAudioPlayerDelegate>
@end

+ 78
- 0
iOS/AudioManager.m View File

@ -0,0 +1,78 @@
//
// AudioManager.m
// ReactNativeEs6Reflux
//
// Created by Josh Habdas on 5/9/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "AudioManager.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
@implementation AudioManager
@synthesize bridge = _bridge;
static STKAudioPlayer *audioPlayer;
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(play) {
if (audioPlayer != nil) {
[audioPlayer stop];
}
audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer setDelegate:self];
[audioPlayer play:audioStream];
}
RCT_EXPORT_METHOD(stop) {
if (audioPlayer != nil) {
[audioPlayer stop];
[audioPlayer setDelegate:nil];
}
[self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status" : @"STOPPED"}];
}
RCT_EXPORT_METHOD(getStatus:
(RCTResponseSenderBlock) callback) {
if (audioPlayer == nil) {
callback(@[[NSNull null], @{@"status" : @"STOPPED"}]);
} else if ([audioPlayer state] == STKAudioPlayerStatePlaying) {
callback(@[[NSNull null], @{@"status" : @"PLAYING"}]);
} else {
callback(@[[NSNull null], @{@"status" : @"STOPPED"}]);
}
}
- (void)audioPlayer:(STKAudioPlayer *)audioPlayer didStartPlayingQueueItemId:(NSObject *)queueItemId {
NSLog(@"AudioPlayer is playing");
}
- (void)audioPlayer:(STKAudioPlayer *)audioPlayer didFinishPlayingQueueItemId:(NSObject *)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration {
NSLog(@"AudioPlayer has stopped");
[self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status" : @"STOPPED"}];
}
- (void)audioPlayer:(STKAudioPlayer *)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject *)queueItemId {
NSLog(@"AudioPlayer finished buffering");
}
- (void)audioPlayer:(STKAudioPlayer *)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode {
NSLog(@"AudioPlayer unecpected Error with code %d", errorCode);
[self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status" : @"STOPPED"}];
}
- (void)audioPlayer:(STKAudioPlayer *)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState {
NSLog(@"AudioPlayer state has changed");
if (state == STKAudioPlayerStatePlaying) {
[self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status" : @"PLAYING"}];
} else if (state == STKAudioPlayerStateStopped) {
[self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status" : @"STOPPED"}];
} else if (state == STKAudioPlayerStateBuffering) {
[self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status" : @"LOADING"}];
}
}
@end

+ 41
- 10
src/components/app.jsx View File

@ -1,29 +1,56 @@
import React from "react-native";
import Reflux from "reflux";
import Styles from "../styles";
import Actions from "../actions";
import AppMessageStore from "../stores/messages";
import Messages from "../stores/messages";
import styles from "../styles";
import { AudioManager } from "NativeModules";
let {
View,
Text,
Image,
StatusBarIOS,
TouchableOpacity
TouchableOpacity,
DeviceEventEmitter
} = React;
let App = React.createClass({
mixins: [Reflux.connect(AppMessageStore, "message")],
mixins: [Reflux.connect(Messages, "message")],
getInitialState() {
this.subscription = DeviceEventEmitter.addListener(
'AudioBridgeEvent', (event) => this.setState(event)
);
AudioManager.getStatus((error, status) => {
console.log(error);
this.setState(status);
});
return { status: 'STOPPED' };
},
componentDidMount() {
// Do stuff when the App top-level component is ready,
// such as change the color of the iOS status bar:
StatusBarIOS.setStyle(StatusBarIOS.Style.lightContent);
// Get the initial message from the store
Actions.updateMessage();
// Play Lumpen.fm using StreamingKit Native Module
if (this.state.status == 'STOPPED') {
this.setState({
status: 'LOADING'
});
AudioManager.play();
} else {
this.setState({
status: 'STOPPED'
});
AudioManager.stop();
}
},
render() {
@ -33,24 +60,28 @@ let App = React.createClass({
// child components.
return (
<View style={Styles.appContainer}>
<Text style={[Styles.appMessage, Styles.appSubMessage]}>
<View style={styles.appContainer}>
<Text style={[styles.appMessage, styles.appSubMessage]}>
Tap the React logo to change the message!
</Text>
<TouchableOpacity
onPress={Actions.updateMessage}>
<Image
style={Styles.appLogo}
style={styles.appLogo}
source={{uri: "http://facebook.github.io/react/img/logo_og.png"}}/>
</TouchableOpacity>
<Text style={Styles.appMessage}>{this.state.message}</Text>
<Text style={[Styles.appMessage, Styles.appSubMessage]}>
<Text style={styles.appMessage}>{this.state.message}</Text>
<Text style={[styles.appMessage, styles.appSubMessage]}>
Edit me in: src/components/app.jsx
</Text>
</View>
);
},
componentWillUnmount() {
this.subscription.remove();
}
});

+ 44
- 0
src/lib/bridges/audio-stream.js View File

@ -0,0 +1,44 @@
Ximport React from "react-native";
import { AudioBridge } from "NativeModules";
let {
NetInfo, AlertIOS
} = React;
var start = function() {
console.log('starting audio ...');
AudioBridge.play();
};
var stop = function() {
console.log('stopping audio ...');
AudioBridge.stop();
};
exports.play = function() {
NetInfo.reachabilityIOS.fetch().done(function(reach) {
if (reach == 'wifi') {
AlertIOS.alert('Mobile Internet', 'You are not connected to Wi-Fi. Would you like to continue using your wireless provider?', [{
text: 'Start radio',
onPress: function() {
start();
}
}, {
text: 'Quit',
onPress: function() {
stop();
}
}]);
} else {
start();
}
});
};
exports.stop = stop;
exports.getStatus = function(callback) {
console.log('getting status');
return AudioBridge.getStatus(callback);
};

Loading…
Cancel
Save