├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── demo
├── app
│ ├── App_Resources
│ │ ├── Android
│ │ │ ├── drawable-hdpi
│ │ │ │ └── icon.png
│ │ │ ├── drawable-ldpi
│ │ │ │ └── icon.png
│ │ │ ├── drawable-mdpi
│ │ │ │ └── icon.png
│ │ │ └── drawable-nodpi
│ │ │ │ └── splashscreen.9.png
│ │ └── iOS
│ │ │ ├── Default-568h@2x.png
│ │ │ ├── Default-667h@2x.png
│ │ │ ├── Default-736h@3x.png
│ │ │ ├── Default-Landscape-568h@2x.png
│ │ │ ├── Default-Landscape-667h@2x.png
│ │ │ ├── Default-Landscape.png
│ │ │ ├── Default-Landscape@2x.png
│ │ │ ├── Default-Landscape@3x.png
│ │ │ ├── Default-Portrait.png
│ │ │ ├── Default-Portrait@2x.png
│ │ │ ├── Default.png
│ │ │ ├── Default@2x.png
│ │ │ ├── Icon-Small-50.png
│ │ │ ├── Icon-Small-50@2x.png
│ │ │ ├── Icon-Small.png
│ │ │ ├── Icon-Small@2x.png
│ │ │ ├── Info.plist
│ │ │ ├── icon-40.png
│ │ │ ├── icon-40@2x.png
│ │ │ ├── icon-60.png
│ │ │ ├── icon-60@2x.png
│ │ │ ├── icon-72.png
│ │ │ ├── icon-72@2x.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-76@2x.png
│ │ │ ├── icon.png
│ │ │ ├── icon@2x.png
│ │ │ └── sounds
│ │ │ ├── daddy.mp3
│ │ │ ├── harder-better-faster.mp3
│ │ │ └── hotline-bling.mp3
│ ├── app.css
│ ├── app.ts
│ ├── package.json
│ ├── player
│ │ ├── player-page.ts
│ │ ├── player-page.xml
│ │ └── player-view-model.ts
│ └── recorder
│ │ ├── recorder-page.ts
│ │ ├── recorder-page.xml
│ │ └── recorder-view-model.ts
└── package.json
├── docs
└── CONTRIBUTING.md
├── ezaudio.ios.ts
├── package.json
├── platforms
└── ios
│ └── Podfile
├── screenshots
├── 1.png
├── 2.png
├── 3.png
└── 4.png
├── src
├── audioplot.ts
├── core.ts
├── player.ts
└── recorder.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | *.d.ts
3 | *.js
4 | *.js.map
5 | demo/**/*.js
6 | demo/hooks
7 | demo/lib
8 | demo/platforms/
9 | demo/node_modules/
10 | demo/*d.ts
11 | demo/tsconfig.json
12 | node_modules
13 | *.log
14 | !ezaudio.d.ts
15 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | node_modules
3 | demo
4 | docs
5 | screenshots
6 | *.png
7 | *.log
8 | *.ts
9 | !*.d.ts
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | nativescript-ezaudio
4 | Copyright (c) 2016, Nathan Walker
5 |
6 | The iOS (https://github.com/syedhali/EZAudio) library is also licensed by their respective authors under the MIT License.
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | this software and associated documentation files (the "Software"), to deal in
10 | the Software without restriction, including without limitation the rights to
11 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | the Software, and to permit persons to whom the Software is furnished to do so,
13 | subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in all
16 | copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | A NativeScript plugin for the simple, intuitive audio framework for iOS.
4 | [EZAudio](https://github.com/syedhali/EZAudio)
5 |
6 | * [Install](#install)
7 | * [Usage](#usage)
8 | * [Screenshots](#screenshots)
9 | * [TNSEZAudioPlayer](#tnsezaudioplayer)
10 | * [TNSEZRecorder](#tnsezrecorder)
11 | * [UI Components](#ui-components)
12 | * [Why the `TNS` prefixed name?](#why-the-tns-prefixed-name)
13 | * [Try it/Contributing](https://github.com/NathanWalker/nativescript-ezaudio/blob/master/docs/CONTRIBUTING.md)
14 |
15 | # Install
16 |
17 | ```
18 | npm install nativescript-ezaudio --save
19 | ```
20 |
21 | # Usage
22 |
23 | **IMPORTANT:** *Make sure you include `xmlns:ez="nativescript-ezaudio"` on the Page element*
24 |
25 | ```
26 | // main-page.xml
27 |
30 |
31 |
38 |
39 |
40 |
41 |
42 | // app.css
43 | .audioPlot {
44 | width:100%;
45 | height:100%;
46 | background-color: #000;
47 | top:0;
48 | left:0;
49 | }
50 | button {
51 | font-size: 22;
52 | horizontal-align: center;
53 | margin:20px 0;
54 | color:#FFF803;
55 | top:20;
56 | left:0;
57 | width:100%;
58 | }
59 |
60 | // main-page.ts
61 | import {AudioDemo} from "./main-view-model";
62 |
63 | function pageLoaded(args) {
64 | var page = args.object;
65 | page.bindingContext = new AudioDemo(page);
66 | }
67 | exports.pageLoaded = pageLoaded;
68 |
69 | // main-view-model.ts
70 | import {Observable} from 'data/observable';
71 | import {TNSEZAudioPlayer} from 'nativescript-ezaudio';
72 |
73 | export class AudioDemo extends Observable {
74 | public btnTxt: string = 'Play Track';
75 |
76 | // AudioPlot
77 | public audioPlotColor: string = '#FFF803';
78 | public audioPlotType: string = 'buffer';
79 | public audioPlotFill: boolean = true;
80 | public audioPlotMirror: boolean = true;
81 | public audioPlotBufferData: any;
82 |
83 | // internal
84 | private _player: any;
85 | private _currentTrackIndex: number = 0;
86 | private _tracks: Array = [
87 | `any-mp3-you-like.mp3`,
88 | ];
89 |
90 | constructor(page: any) {
91 | super();
92 | this._player = new TNSEZAudioPlayer(true);
93 | this._player.delegate().audioEvents.on('audioBuffer', (eventData) => {
94 | this.set('audioPlotBufferData', {
95 | buffer: eventData.data.buffer,
96 | bufferSize: eventData.data.bufferSize
97 | });
98 | });
99 | }
100 |
101 | public toggleCurrentTrack() {
102 | this._player.togglePlay(this._tracks[this._currentTrackIndex]);
103 | this.toggleBtn();
104 | }
105 |
106 | private toggleBtn() {
107 | this.set(`btnTxt`, `${this._player.isPlaying() ? 'Stop' : 'Play'} Track`);
108 | }
109 | }
110 | ```
111 |
112 | ## Screenshots
113 |
114 | Sample 1 | Sample 2
115 | -------- | ---------
116 |  | 
117 |
118 | Sample 3 | Sample 4
119 | -------- | -------
120 |  | 
121 |
122 | ## TNSEZAudioPlayer
123 |
124 | AudioPlayer based on [EZAudioPlayer](https://github.com/syedhali/EZAudio#EZAudioPlayer).
125 |
126 | Creating:
127 | ```
128 | // Option 1: simple
129 | this._player = new TNSEZAudioPlayer();
130 |
131 | // Option 2: advanced
132 | // passing true to constructor will let the player know it should emit events
133 | this._player = new TNSEZAudioPlayer(true);
134 |
135 | // it allows you to listen to events like so:
136 | this._player.delegate().audioEvents.on('audioBuffer', (eventData) => {
137 | this.set('audioPlotBufferData', {
138 | buffer: eventData.data.buffer,
139 | bufferSize: eventData.data.bufferSize
140 | });
141 | });
142 |
143 | ```
144 |
145 | #### Methods
146 |
147 | Event | Description
148 | -------- | ---------
149 | `togglePlay(fileName?: string, reloadTrack?: boolean)`: `void` | Allows toggle play/pause on a track as well as reloading the current track or reloading in a new track. First time will always load the track and play. `fileName` represents the path to the file in your resources. `reloadTrack` can be used to reload current track or load a new track.
150 | `pause()`: `void` | Pause track
151 | `isPlaying()`: `boolean` | Determine whether player is playing a track
152 | `duration()`: `number` | Length in seconds
153 | `formattedDuration()`: `string` | Formatted duration in '00:00'
154 | `totalFrames`: `number` | Total number of frames in the loaded track
155 | `formattedCurrentTime`: `string` | Formatted current time in '00:00'
156 | `setCurrentTime(time: number)`: `void` | Set the current time via a frame number
157 | `seekToFrame(frame: number)`: `void` | Seek playhead to a given frame number
158 | `volume()`: `number` | Get the current volume
159 | `setVolume(volume: number)`: `void` | Set the volume. Must be between 0 - 1.
160 | `pan()`: `number` | Get current pan settings
161 | `setPan(pan: number)`: `void` | Set pan left/right. Must be between -1 (left) and 1 (right). 0 is default (center).
162 | `device()`: `any` | Get current output device
163 |
164 | #### Events
165 |
166 | Event | Description
167 | -------- | ---------
168 | `audioBuffer` | When audio file is playing, get the `buffer` and `bufferSize` to set an `AudioPlot`'s `bufferData`
169 | `position` | Current frame number
170 | `reachedEnd` | When the end of the file is reached
171 | `changeAudioFile` | When the audio file is changed or set
172 | `changeOutput` | When the output device is changed
173 | `changePan` | When the pan is changed
174 | `changeVolume` | When the volume is changed
175 | `changePlayState` | When the player state changes, ie. play/pause
176 | `seeked` | When the audio file has been seeked to a certain frame number
177 |
178 | ## TNSEZRecorder
179 |
180 | Recorder based on [EZRecorder](https://github.com/syedhali/EZAudio#ezrecorder).
181 |
182 | Creating:
183 | ```
184 | this._recorder = new TNSEZRecorder();
185 |
186 | // it allows you to listen to events like so:
187 | this._recorder.delegate().audioEvents.on('audioBuffer', (eventData) => {
188 | this.set('audioPlotBufferData', {
189 | buffer: eventData.data.buffer,
190 | bufferSize: eventData.data.bufferSize
191 | });
192 | });
193 |
194 | ```
195 |
196 | #### Methods
197 |
198 | Event | Description
199 | -------- | ---------
200 | `record(filePath: string)`: `void` | Record a `.m4a` file. Pass in an absoulte filePath.
201 | `stop()`: `void` | Stop recording
202 | `isRecording()`: `boolean` | Determine whether recorder is recording
203 | `deviceInputs()`: `Array` | Collection of input devices
204 | `setDevice(device:any)`: `void` | Set the input device
205 |
206 | #### Events
207 |
208 | Event | Description
209 | -------- | ---------
210 | `audioBuffer` | While recording, get the `buffer` and `bufferSize` to set an `AudioPlot`'s `bufferData`
211 | `recordTime` | Current recording time
212 |
213 | ## UI Components
214 |
215 | ### AudioPlot
216 |
217 | Displays an audio waveform and provides attributes to modify it's display.
218 |
219 | Example:
220 | ```
221 |
222 | ```
223 |
224 | #### Attributes
225 |
226 | Property | Value
227 | -------- | ---------
228 | `plotColor`: `string` | Color of waveform. Any rgb hex value, ie. #fff
229 | `plotType`: `string` | `buffer` or `rolling`
230 | `fill`: `boolean` | Makes waveform solid with color. When `false`, it appears more like lines.
231 | `mirror`: `boolean` | Whether to mirror the waveform top/bottom.
232 | `bufferData`: `Object` | An Object representing the audio file's `buffer` and `bufferSize`. See [example implementation](https://github.com/NathanWalker/nativescript-ezaudio/blob/master/demo/app/player/player-view-model.ts#L54-L59)
233 |
234 | ### Contributors
235 |
236 | * [NathanaelA](https://github.com/NathanaelA)
237 |
238 | ## Why the TNS prefixed name?
239 |
240 | `TNS` stands for **T**elerik **N**ative**S**cript
241 |
242 | iOS uses classes prefixed with `NS` (stemming from the [NeXTSTEP](https://en.wikipedia.org/wiki/NeXTSTEP) days of old):
243 | https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/
244 |
245 | To avoid confusion with iOS native classes, `TNS` is used instead.
246 |
247 | ## License
248 |
249 | MIT
250 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/Android/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/Android/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/Android/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/drawable-nodpi/splashscreen.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/Android/drawable-nodpi/splashscreen.9.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-568h@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-667h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-667h@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-736h@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-736h@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Landscape-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Landscape-568h@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Landscape-667h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Landscape-667h@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Landscape.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Landscape@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Landscape@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Landscape@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Landscape@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Portrait.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default-Portrait@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default-Portrait@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Default@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Icon-Small-50.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Icon-Small.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/Icon-Small@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | NSAppTransportSecurity
45 |
46 | NSAllowsArbitraryLoads
47 |
48 |
49 | UIBackgroundModes
50 |
51 | audio
52 |
53 | NSMicrophoneUsageDescription
54 | The Audio Recorder needs to access your Microphone to record.
55 |
56 |
57 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-40.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-40@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-60.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-60@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-72.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-72@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-76.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon-76@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/icon@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/sounds/daddy.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/sounds/daddy.mp3
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/sounds/harder-better-faster.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/sounds/harder-better-faster.mp3
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/sounds/hotline-bling.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/demo/app/App_Resources/iOS/sounds/hotline-bling.mp3
--------------------------------------------------------------------------------
/demo/app/app.css:
--------------------------------------------------------------------------------
1 | .recordTime {
2 | horizontal-align: center;
3 | font-size:18;
4 | color:#fff;
5 | top:120;
6 | left:0;
7 | width:100%;
8 | }
9 |
10 | button {
11 | font-size: 22;
12 | horizontal-align: center;
13 | margin:20px 0;
14 | color:#FFF803;
15 | top:20;
16 | left:0;
17 | width:100%;
18 | }
19 |
20 | .playBtn {
21 | top:300;
22 | color: green;
23 | font-size: 22;
24 | horizontal-align: center;
25 | left:0;
26 | width:100%;
27 | }
28 |
29 | label {
30 | color: #999;
31 | font-size:14;
32 | }
33 |
34 | slider {
35 | width:85%;
36 | }
37 |
38 | .audioPlot {
39 | width:100%;
40 | height:100%;
41 | background-color: #000;
42 | top:0;
43 | left:0;
44 | }
45 |
46 | .footer {
47 | background-color: #000;
48 | horizontal-align: center;
49 | width: 100%;
50 | height:30;
51 | top:500;
52 | left: 0;
53 | }
--------------------------------------------------------------------------------
/demo/app/app.ts:
--------------------------------------------------------------------------------
1 | var application = require("application");
2 | application.mainModule = "./player/player-page";
3 | // application.mainModule = "./recorder/recorder-page";
4 | application.cssFile = "./app.css";
5 | application.start();
6 |
--------------------------------------------------------------------------------
/demo/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo-ezaudio",
3 | "main": "app.js",
4 | "version": "1.0.0",
5 | "author": {
6 | "name": "Nathan Walker",
7 | "email": "walkerrunpdx@gmail.com"
8 | },
9 | "description": "Nativescript ezaudio demo",
10 | "license": "MIT",
11 | "keywords": [
12 | "ezaudio",
13 | "audio",
14 | "nativescript"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/NathanWalker/nativescript-ezaudio.git"
19 | },
20 | "bugs": {
21 | "url": "https://github.com/NathanWalker/nativescript-ezaudio/issues"
22 | },
23 | "homepage": "https://github.com/NathanWalker/nativescript-ezaudio"
24 | }
--------------------------------------------------------------------------------
/demo/app/player/player-page.ts:
--------------------------------------------------------------------------------
1 | var frameModule = require("ui/frame");
2 | import {AudioPlayerDemo} from "./player-view-model";
3 |
4 | function pageLoaded(args) {
5 | var page = args.object;
6 | page.bindingContext = new AudioPlayerDemo(page);
7 |
8 | var controller = frameModule.topmost().ios.controller;
9 | var navigationBar = controller.navigationBar;
10 | navigationBar.barStyle = 1;
11 | }
12 | exports.pageLoaded = pageLoaded;
--------------------------------------------------------------------------------
/demo/app/player/player-page.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/demo/app/player/player-view-model.ts:
--------------------------------------------------------------------------------
1 | import {Observable} from 'data/observable';
2 | import placeholder = require("ui/placeholder");
3 | import {topmost} from 'ui/frame';
4 | import {Color} from "color";
5 | var _ = require('lodash');
6 | import {TNSEZAudioPlayer} from 'nativescript-ezaudio';
7 |
8 | export class AudioPlayerDemo extends Observable {
9 | public footerNote: string = "Demo by Nathan Walker";
10 |
11 | // AudioPlot
12 | public audioPlotColor: string = '#FFF803';
13 | public audioPlotType: string = 'buffer';
14 | public audioPlotFill: boolean = true;
15 | public audioPlotMirror: boolean = true;
16 | public audioPlotBufferData: any;
17 |
18 | // UI controls
19 | public framePosition: number = 0;
20 | public totalFrames: number = 100;
21 | public currentTime: string = '00:00';
22 | public totalDuration: string = '00:00';
23 | public outputs: any;
24 | public selectedOutput: number = 0;
25 | public tracks: Array = [
26 | { title: 'Drake', path: 'sounds/hotline-bling.mp3' },
27 | { title: 'Daft Punk', path: 'sounds/harder-better-faster.mp3' },
28 | { title: 'Psy', path: 'sounds/daddy.mp3' }
29 | ];
30 | public selectedTrackIndex: number = 0;
31 | public plotStyles: Array = [
32 | { title: 'Buffer' },
33 | { title: 'Rolling' }
34 | ];
35 | public plotFill: Array = [
36 | { title: 'On' },
37 | { title: 'Off' }
38 | ];
39 | public plotColors: Array = [
40 | { title: 'Yellow' },
41 | { title: 'Blue' },
42 | { title: 'Green' },
43 | { title: 'Purple' }
44 | ];
45 | public selectedPlotStyle: number = 0;
46 |
47 | // internal
48 | private _player: any;
49 | private _plotType: string = 'buffer';
50 |
51 | constructor(page: any) {
52 | super();
53 | this.set(`btnTxt`, `Play Track`);
54 | this._player = new TNSEZAudioPlayer(true);
55 | this._player.delegate().audioEvents.on('audioBuffer', (eventData) => {
56 | this.set('audioPlotBufferData', {
57 | buffer: eventData.data.buffer,
58 | bufferSize: eventData.data.bufferSize
59 | });
60 | });
61 | this._player.delegate().audioEvents.on('position', (eventData) => {
62 | // console.log(`event framePosition: ${this.framePosition}`);
63 | this.set('framePosition', eventData.data.position);
64 | this.set('currentTime', this._player.formattedCurrentTime());
65 | this.set('totalFrames', this._player.totalFrames());
66 | this.set('totalDuration', this._player.formattedDuration());
67 | });
68 | this._player.delegate().audioEvents.on('changeAudioFile', (eventData) => {
69 | console.log('changeAudioFile');
70 | });
71 | this.on(Observable.propertyChangeEvent, _.debounce(this.seekToFrame.bind(this), 500, {leading: true}));
72 | this.set('outputs', EZAudioDevice.outputDevices());
73 | console.log(this.outputs);
74 | }
75 |
76 | public viewRecorder() {
77 | topmost().navigate("recorder/recorder-page");
78 | }
79 |
80 | public toggleCurrentTrack(e: any, reset?: boolean) {
81 | this._player.togglePlay(this.tracks[this.selectedTrackIndex].path, reset);
82 | if (e) {
83 | // only toggle btn state when triggered via view (e will be a valid ui event)
84 | this.toggleBtn();
85 | }
86 | this.set('totalFrames', this._player.totalFrames());
87 | this.set('totalDuration', this._player.formattedDuration());
88 | console.log(`currentTrack: ${this.tracks[this.selectedTrackIndex].path}`);
89 | console.log(`---- totalFrames: ${this.totalFrames}`);
90 | console.log(`---- duration: ${this._player.duration()}`);
91 | console.log(`---- currentTime: ${this.currentTime}`);
92 | }
93 |
94 | public changeTrack(e) {
95 | if (this.selectedTrackIndex != e.newIndex) {
96 | this.selectedTrackIndex = e.newIndex;
97 | console.log(`changeTrack: ${this.tracks[this.selectedTrackIndex].path}`);
98 | this.toggleCurrentTrack(null, true);
99 | }
100 | }
101 |
102 | public changePlotColor(e) {
103 | let color = '#FFF803';
104 | switch (e.newIndex) {
105 | case 0:
106 | color = '#FFF803';
107 | break;
108 | case 1:
109 | color = '#34C0FF';
110 | break;
111 | case 2:
112 | color = '#00FF17';
113 | break;
114 | case 3:
115 | color = '#FF45F9';
116 | break;
117 | }
118 | this.set('audioPlotColor', color);
119 | }
120 |
121 | public changePlotType(e) {
122 | this.set('audioPlotType', e.newIndex == 1 ? 'rolling' : 'buffer');
123 | }
124 |
125 | public changePlotFill(e) {
126 | this.set('audioPlotFill', !this.audioPlotFill);
127 | }
128 |
129 | public changePlotMirror(e) {
130 | this.set('audioPlotMirror', !this.audioPlotMirror);
131 | }
132 |
133 | private seekToFrame(propertyChangeData) {
134 | if (propertyChangeData && propertyChangeData.propertyName == 'framePosition') {
135 | console.log(`propertyChangeEvent: ${Math.ceil(propertyChangeData.value)}`);
136 | this._player.seekToFrame(Math.ceil(propertyChangeData.value));
137 | }
138 | }
139 |
140 | private toggleBtn() {
141 | this.set(`btnTxt`, `${this._player.isPlaying() ? 'Stop' : 'Play'} Track`);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/demo/app/recorder/recorder-page.ts:
--------------------------------------------------------------------------------
1 | var frameModule = require("ui/frame");
2 | import {AudioRecorderDemo} from "./recorder-view-model";
3 | function pageLoaded(args) {
4 | var page = args.object;
5 | page.bindingContext = new AudioRecorderDemo();
6 |
7 | var controller = frameModule.topmost().ios.controller;
8 | var navigationBar = controller.navigationBar;
9 | navigationBar.barStyle = 1;
10 | }
11 | exports.pageLoaded = pageLoaded;
12 |
--------------------------------------------------------------------------------
/demo/app/recorder/recorder-page.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demo/app/recorder/recorder-view-model.ts:
--------------------------------------------------------------------------------
1 | import {Observable} from 'data/observable';
2 | import * as fs from 'file-system';
3 | import {TNSEZRecorder, TNSEZAudioPlayer} from 'nativescript-ezaudio';
4 |
5 | export class AudioRecorderDemo extends Observable {
6 | public footerNote: string = "Demo by Nathan Walker";
7 |
8 | // AudioPlot
9 | public audioPlotColor: string = '#FFF803';
10 | public audioPlotType: string = 'buffer';
11 | public audioPlotFill: boolean = true;
12 | public audioPlotMirror: boolean = true;
13 | public audioPlotBufferData: any;
14 | public recordTime: string;
15 | public showPlayBtn: boolean = false;
16 | public playBtnTxt: string;
17 |
18 | private _recorder: any;
19 | private _player: any;
20 | private _recordingPath: string;
21 | private _reloadPlayer: boolean = false;
22 |
23 | constructor() {
24 | super();
25 | this._recorder = new TNSEZRecorder();
26 | this._recorder.delegate().audioEvents.on('audioBuffer', (eventData) => {
27 | this.set('audioPlotBufferData', {
28 | buffer: eventData.data.buffer,
29 | bufferSize: eventData.data.bufferSize
30 | });
31 | });
32 | this._recorder.delegate().audioEvents.on('recordTime', (eventData) => {
33 | this.set('recordTime', eventData.data.time);
34 | });
35 |
36 | // player
37 | this._player = new TNSEZAudioPlayer(true);
38 | this._player.delegate().audioEvents.on('audioBuffer', (eventData) => {
39 | this.set('audioPlotBufferData', {
40 | buffer: eventData.data.buffer,
41 | bufferSize: eventData.data.bufferSize
42 | });
43 | });
44 |
45 | this.set(`btnTxt`, `Start Recording`);
46 | this.set(`playBtnTxt`, `Play`);
47 | }
48 |
49 | public toggleRecord() {
50 | if (this._recorder.isRecording()) {
51 | this._recorder.stop();
52 | this.set('showPlayBtn', true);
53 | this._reloadPlayer = true;
54 | } else {
55 | var audioFolder = fs.knownFolders.currentApp().getFolder("audio");
56 | console.log(JSON.stringify(audioFolder));
57 | this._recordingPath = `${audioFolder.path}/recording.m4a`;
58 | this._recorder.record(this._recordingPath);
59 | }
60 | this.toggleBtn();
61 | }
62 |
63 | public togglePlay() {
64 | this._player.togglePlay(this._recordingPath, this._reloadPlayer);
65 | this.togglePlayBtn();
66 | this._reloadPlayer = false;
67 | }
68 |
69 | private toggleBtn() {
70 | this.set(`btnTxt`, `${this._recorder.isRecording() ? 'Stop' : 'Start'} Recording`);
71 | }
72 |
73 | private togglePlayBtn() {
74 | this.set(`playBtnTxt`, `${this._player.isPlaying() ? 'Stop' : 'Play'}`);
75 | }
76 | }
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "nativescript": {
3 | "id": "org.nativescript.demo",
4 | "tns-ios": {
5 | "version": "2.1.1"
6 | }
7 | },
8 | "scripts": {
9 | "demo": "tns livesync ios --emulator --watch --log trace"
10 | },
11 | "dependencies": {
12 | "lodash": "^4.8.2",
13 | "nativescript-ezaudio": "file:..",
14 | "tns-core-modules": "^2.1.0"
15 | },
16 | "devDependencies": {
17 | "nativescript-dev-typescript": "^0.3.2",
18 | "shelljs": "^0.6.0",
19 | "tns-platform-declarations": "^2.0.0",
20 | "typescript": "^1.8.10"
21 | }
22 | }
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Try it
2 |
3 | ```
4 | git clone https://github.com/NathanWalker/nativescript-ezaudio.git
5 | cd nativescript-ezaudio
6 | npm install
7 |
8 | // build out plugin
9 | // you will see a lot of TypeScript warnings, this is normal, you can ignore :)
10 | npm run build
11 |
12 | // now try out the demo
13 | cd demo
14 | tns install // please note: 'tns', not npm :)
15 |
16 | // run demo in iOS simulator
17 | // you may see more TypeScript warnings, you can ignore :)
18 | npm run demo
19 | ```
20 |
21 | **Please note**: Demo works best in **iPhone 6 Simulator** at the moment since I'm using AbsoluteLayout.
22 | I will change this soon to work across various devices.
23 |
24 | # Contributing
25 |
26 | ## Submitting Pull Requests
27 |
28 | **Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.**
29 |
30 | * Please rebase your branch against the current master
31 | * Make reference to possible [issues](https://github.com/NathanWalker/nativescript-ezaudio/issues) on PR comment
32 |
33 | ## Submitting bug reports
34 |
35 | * Please detail the affected platform and version
36 | * Please be sure to state which version of node, npm, and NativeScript you're using
--------------------------------------------------------------------------------
/ezaudio.ios.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * AudioPlayer
3 | */
4 | export * from './src/player';
5 |
6 | /**
7 | * AudioRecorder
8 | */
9 | export * from './src/recorder';
10 |
11 | /**
12 | * AudioPlot
13 | * Component for displaying audio waveform.
14 | */
15 | export * from './src/audioplot';
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nativescript-ezaudio",
3 | "version": "1.1.7",
4 | "description": "NativeScript plugin for the iOS audio visualization framework built upon Core Audio useful for anyone doing real-time, low-latency audio processing and visualizations.",
5 | "main": "ezaudio",
6 | "typings": "index.d.ts",
7 | "nativescript": {
8 | "platforms": {
9 | "ios": "2.1.1"
10 | }
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/NathanWalker/nativescript-ezaudio.git"
15 | },
16 | "keywords": [
17 | "NativeScript",
18 | "EZAudio",
19 | "JavaScript",
20 | "Android",
21 | "iOS",
22 | "Audio"
23 | ],
24 | "author": {
25 | "name": "Nathan Walker",
26 | "email": "walkerrunpdx@gmail.com"
27 | },
28 | "bugs": {
29 | "url": "https://github.com/NathanWalker/nativescript-ezaudio/issues"
30 | },
31 | "license": {
32 | "type": "MIT",
33 | "url": "https://github.com/NathanWalker/nativescript-ezaudio/blob/master/LICENSE"
34 | },
35 | "homepage": "https://github.com/NathanWalker/nativescript-ezaudio",
36 | "readmeFilename": "README.md",
37 | "scripts": {
38 | "build": "tsc",
39 | "fix_xcode": "xcproj --project 'platforms/ios/demo.xcodeproj' touch && xcproj --project 'platforms/ios/Pods/Pods.xcodeproj' touch",
40 | "demo.ios": "npm run preparedemo; cd demo; tns emulate ios",
41 | "livesync.ios": "npm run preparedemo; cd demo; tns livesync ios --emulator --watch",
42 | "preparedemo": "npm run build; cd demo; tns plugin remove nativescript-ezaudio; tns plugin add ..; tns install",
43 | "setup": "npm i; cd demo; npm install; cd ..; npm run build; cd demo; tns plugin add ..; cd .."
44 | },
45 | "devDependencies": {
46 | "tns-core-modules": "^2.4.2",
47 | "tns-platform-declarations": "^2.4.2",
48 | "typescript": "^2.1.4"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/platforms/ios/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 | pod 'EZAudio', '~> 1.1.5'
--------------------------------------------------------------------------------
/screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/screenshots/1.png
--------------------------------------------------------------------------------
/screenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/screenshots/2.png
--------------------------------------------------------------------------------
/screenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/screenshots/3.png
--------------------------------------------------------------------------------
/screenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanWalker/nativescript-ezaudio/06a1b287f5adacd35704bfc70f543a98b4b8f32d/screenshots/4.png
--------------------------------------------------------------------------------
/src/audioplot.ts:
--------------------------------------------------------------------------------
1 | import {ContentView} from "ui/content-view";
2 | import {Color} from "color";
3 |
4 | declare var EZAudioPlot: any, CGRectMake: any, EZPlotTypeBuffer: any, EZPlotTypeRolling: any;
5 |
6 | export class AudioPlot extends ContentView {
7 | private _color: string;
8 | private _fill: boolean;
9 | private _mirror: boolean;
10 | private _plotType: any;
11 | private _ios: any;
12 |
13 | constructor() {
14 | super();
15 | console.log('--------- AudioPlot ---------');
16 | this._ios = EZAudioPlot.alloc().initWithFrame(CGRectMake(0, 0, 0, 0));
17 | }
18 |
19 | get _nativeView(): any {
20 | return this._ios;
21 | }
22 |
23 | set plotColor(value: string) {
24 | this._color = value;
25 | // console.log(`AudioPlot color: ${value}`);
26 | this._ios.color = new Color(value).ios;
27 | }
28 |
29 | set fill(value: boolean) {
30 | this._fill = value;
31 | // console.log(`AudioPlot fill: ${value}`);
32 | this._ios.shouldFill = value;
33 | }
34 |
35 | set mirror(value: boolean) {
36 | this._mirror = value;
37 | // console.log(`AudioPlot mirror: ${value}`);
38 | this._ios.shouldMirror = value;
39 | }
40 |
41 | set plotType(type: string) {
42 | this._plotType = type;
43 | // console.log(`AudioPlot plotType: ${type}`);
44 | switch (type) {
45 | case 'buffer':
46 | this._ios.plotType = EZPlotTypeBuffer;
47 | break;
48 | case 'rolling':
49 | this._ios.plotType = EZPlotTypeRolling;
50 | break;
51 | }
52 | }
53 |
54 | set bufferData(data: any) {
55 | this._ios.updateBufferWithBufferSize(data.buffer, data.bufferSize);
56 | }
57 | }
--------------------------------------------------------------------------------
/src/core.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * iOS Notifications
3 | */
4 | declare var interop: any;
5 |
6 | export class EZNotificationObserver extends NSObject {
7 | private _onReceiveCallback: (notification: NSNotification) => void;
8 |
9 | static new(): EZNotificationObserver {
10 | return super.new();
11 | }
12 |
13 | public initWithCallback(onReceiveCallback: (notification: NSNotification) => void): EZNotificationObserver {
14 | this._onReceiveCallback = onReceiveCallback;
15 | return this;
16 | }
17 |
18 | public onReceive(notification: NSNotification): void {
19 | this._onReceiveCallback(notification);
20 | }
21 |
22 | public static ObjCExposedMethods = {
23 | "onReceive": { returns: interop.types.void, params: [NSNotification] }
24 | };
25 | }
--------------------------------------------------------------------------------
/src/player.ts:
--------------------------------------------------------------------------------
1 | import {Observable, EventData} from 'data/observable';
2 | import {EZNotificationObserver} from './core';
3 |
4 | declare var EZAudioFile: any, EZAudioPlayerDelegate: any, EZAudioPlayer: any;
5 |
6 | class TNSEZAudioDelegate extends NSObject implements EZAudioPlayerDelegate {
7 | public static ObjCProtocols = [EZAudioPlayerDelegate];
8 | public player: any;
9 | public audioEvents: Observable;
10 | private _bufferEvent: EventData;
11 | private _positionEvent: EventData;
12 | private _reachedEndEvent: EventData;
13 | private _changeAudioFileEvent: EventData;
14 | private _changeOutputEvent: EventData;
15 | private _changePanEvent: EventData;
16 | private _changeVolumeEvent: EventData;
17 | private _changePlayStateEvent: EventData;
18 | private _seekedEvent: EventData;
19 | private _observers: Array;
20 |
21 | public initPlayer(emitEvents?: boolean) {
22 | // this.player = EZAudioPlayer.audioPlayerWithDelegate(this);
23 | this.player = EZAudioPlayer.sharedAudioPlayer();
24 | this.player.delegate = this;
25 |
26 | // IMPORTANT: notifications can only be setup after the player has been setup in a concrete class implementation
27 | this.setupNotifications();
28 |
29 | if (emitEvents) {
30 | this.setupEvents();
31 | }
32 | }
33 |
34 | // delegate notifications and events
35 | public audioPlayerPlayedAudioWithBufferSizeWithNumberOfChannelsInAudioFile(player: any, buffer: number, bufferSize: number, numberOfChannels: number, audioFile: any) {
36 | // console.log(`buffer: ${buffer.value[0]}`);
37 | // console.log(`bufferSize: ${bufferSize}`);
38 | if (this.audioEvents) {
39 | this._bufferEvent.data.buffer = buffer.value;
40 | this._bufferEvent.data.bufferSize = bufferSize;
41 | this.audioEvents.notify(this._bufferEvent);
42 | }
43 | }
44 |
45 | public audioPlayerUpdatedPositionInAudioFile(player: any, framePosition: number, audioFile: any) {
46 | // console.log(`updatedPositionInAudioFile: ${framePosition}`);
47 | if (this.audioEvents) {
48 | this._positionEvent.data.position = framePosition;
49 | this.audioEvents.notify(this._positionEvent);
50 | }
51 | }
52 |
53 | public didChangeAudioFile(notification: NSNotification) {
54 | // console.log(`NSNotification: didChangeAudioFile`);
55 | if (this.audioEvents) {
56 | this.audioEvents.notify(this._changeAudioFileEvent);
57 | }
58 | }
59 |
60 | public didChangeOutputDevice(notification: NSNotification) {
61 | // console.log(`NSNotification: didChangeOutputDevice`);
62 | if (this.audioEvents) {
63 | this.audioEvents.notify(this._changeOutputEvent);
64 | }
65 | }
66 |
67 | public didChangePan(notification: NSNotification) {
68 | // console.log(`NSNotification: didChangePan`);
69 | if (this.audioEvents) {
70 | this.audioEvents.notify(this._changePanEvent);
71 | }
72 | }
73 |
74 | public didChangePlayState(notification: NSNotification) {
75 | // console.log(`NSNotification: didChangePlayState`);
76 | if (this.audioEvents) {
77 | this.audioEvents.notify(this._changePlayStateEvent);
78 | }
79 | }
80 |
81 | public didChangeVolume(notification: NSNotification) {
82 | // console.log(`NSNotification: didChangeVolume`);
83 | if (this.audioEvents) {
84 | this.audioEvents.notify(this._changeVolumeEvent);
85 | }
86 | }
87 |
88 | public didReachEndOfFile(notification: NSNotification) {
89 | // console.log(`NSNotification: didReachEndOfFile`);
90 | if (this.audioEvents) {
91 | this.audioEvents.notify(this._reachedEndEvent);
92 | }
93 | }
94 |
95 | public didSeek(notification: NSNotification) {
96 | // console.log(`NSNotification: didSeek`);
97 | if (this.audioEvents) {
98 | this.audioEvents.notify(this._seekedEvent);
99 | }
100 | }
101 |
102 | private addNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void): EZNotificationObserver {
103 | var observer = EZNotificationObserver.new().initWithCallback(onReceiveCallback);
104 | NSNotificationCenter.defaultCenter().addObserverSelectorNameObject(observer, "onReceive", notificationName, this.player);
105 | this._observers.push(observer);
106 | return observer;
107 | }
108 |
109 | private removeNotificationObserver(observer: any, notificationName: string) {
110 | var index = this._observers.indexOf(observer);
111 | if (index >= 0) {
112 | this._observers.splice(index, 1);
113 | NSNotificationCenter.defaultCenter().removeObserverNameObject(observer, notificationName, this.player);
114 | }
115 | }
116 |
117 | // Register player notifications
118 | // https://github.com/syedhali/EZAudio#notifications
119 | private setupNotifications() {
120 | this._observers = new Array();
121 | this.addNotificationObserver("EZAudioPlayerDidChangeAudioFileNotification", this.didChangeAudioFile.bind(this));
122 | this.addNotificationObserver("EZAudioPlayerDidChangeOutputDeviceNotification", this.didChangeOutputDevice.bind(this));
123 | this.addNotificationObserver("EZAudioPlayerDidChangePanNotification", this.didChangePan.bind(this));
124 | this.addNotificationObserver("EZAudioPlayerDidChangePlayStateNotification", this.didChangePlayState.bind(this));
125 | this.addNotificationObserver("EZAudioPlayerDidChangeVolumeNotification", this.didChangeVolume.bind(this));
126 | this.addNotificationObserver("EZAudioPlayerDidReachEndOfFileNotification", this.didReachEndOfFile.bind(this));
127 | this.addNotificationObserver("EZAudioPlayerDidSeekNotification", this.didSeek.bind(this));
128 | }
129 |
130 | private setupEvents() {
131 | this.audioEvents = new Observable();
132 | this._bufferEvent = {
133 | eventName: 'audioBuffer',
134 | data: {
135 | buffer: 0,
136 | bufferSize: 0
137 | }
138 | };
139 | this._positionEvent = {
140 | eventName: 'position',
141 | data: {
142 | position: 0
143 | }
144 | };
145 | this._reachedEndEvent = {
146 | eventName: 'reachedEnd'
147 | };
148 | this._changeAudioFileEvent = {
149 | eventName: 'changeAudioFile'
150 | };
151 | this._changeOutputEvent = {
152 | eventName: 'changeOutput'
153 | };
154 | this._changePanEvent = {
155 | eventName: 'changePan'
156 | };
157 | this._changeVolumeEvent = {
158 | eventName: 'changeVolume'
159 | };
160 | this._changePlayStateEvent = {
161 | eventName: 'changePlayState'
162 | };
163 | this._seekedEvent = {
164 | eventName: 'seeked'
165 | };
166 | }
167 | }
168 |
169 | // https://github.com/syedhali/EZAudio#EZAudioPlayer
170 | export class TNSEZAudioPlayer {
171 | private _audioFileLoaded: boolean = false;
172 | private _currentAudioFile: EZAudioFile;
173 | private _currentAudioFilePath: string;
174 | private _playing: boolean = false;
175 | private _delegate: any;
176 | private _playbackSession: any;
177 |
178 | constructor(emitEvents?: boolean) {
179 | this._delegate = new TNSEZAudioDelegate();
180 | this._delegate.initPlayer(emitEvents);
181 | }
182 |
183 | public delegate(): any {
184 | return this._delegate;
185 | }
186 |
187 | public togglePlay(fileName?: string, reloadTrack?: boolean) {
188 | if (!this._audioFileLoaded || reloadTrack) {
189 | this._playbackSession = AVAudioSession.sharedInstance();
190 | let errorRef = new interop.Reference();
191 | this._playbackSession.setCategoryError(AVAudioSessionCategoryPlayback, errorRef);
192 | if (errorRef) {
193 | console.log(`setCategoryError: ${errorRef.value}`);
194 | }
195 | this._playbackSession.setActiveError(true, null);
196 | this.loadAndPlayAudioFile(fileName);
197 | } else if (this._playing) {
198 | this.pause();
199 | } else {
200 | this._delegate.player.play();
201 | this._playing = true;
202 | }
203 | }
204 |
205 | public pause() {
206 | this._delegate.player.pause();
207 | this._playing = false;
208 | }
209 |
210 | public isPlaying(): boolean {
211 | return this._playing;
212 | }
213 |
214 | public duration(): number {
215 | if (this._audioFileLoaded) {
216 | return this._delegate.player.duration;
217 | } else {
218 | return 0;
219 | }
220 | }
221 |
222 | public formattedDuration(): string {
223 | if (this._audioFileLoaded) {
224 | return this._delegate.player.formattedDuration;
225 | } else {
226 | return '00:00';
227 | }
228 | }
229 |
230 | public totalFrames(): number {
231 | if (this._audioFileLoaded) {
232 | return this._delegate.player.totalFrames;
233 | } else {
234 | return 0;
235 | }
236 | }
237 |
238 | public formattedCurrentTime(): string {
239 | if (this._audioFileLoaded) {
240 | return this._delegate.player.formattedCurrentTime;
241 | } else {
242 | return '00:00';
243 | }
244 | }
245 |
246 | public setCurrentTime(time: number): void {
247 | if (this._audioFileLoaded) {
248 | this._delegate.player.setCurrentTime(time);
249 | } else {
250 | this.noFileError();
251 | }
252 | }
253 |
254 | public seekToFrame(frame: number): void {
255 | if (this._audioFileLoaded) {
256 | this._delegate.player.seekToFrame(frame);
257 | } else {
258 | this.noFileError();
259 | }
260 | }
261 |
262 | public volume(): number {
263 | if (this._audioFileLoaded) {
264 | return this._delegate.player.volume;
265 | } else {
266 | return 0;
267 | }
268 | }
269 |
270 | public setVolume(volume: number): void {
271 | if (this._audioFileLoaded) {
272 | if (volume >= 0 || volume <= 1) {
273 | this._delegate.player.setVolume(volume);
274 | } else {
275 | console.log('Volume must be >= 0 or <= 1.')
276 | }
277 | } else {
278 | this.noFileError();
279 | }
280 | }
281 |
282 | public pan(): number {
283 | if (this._audioFileLoaded) {
284 | return this._delegate.player.pan;
285 | } else {
286 | return 0;
287 | }
288 | }
289 |
290 | public setPan(pan: number): void {
291 | if (this._audioFileLoaded) {
292 | if (pan >= -1 || pan <= 1) {
293 | this._delegate.player.setPan(pan);
294 | } else {
295 | console.log('Pan must be >= -1 or <= 1.');
296 | }
297 | } else {
298 | this.noFileError();
299 | }
300 | }
301 |
302 | public device(): any {
303 | if (this._audioFileLoaded) {
304 | return this._delegate.player.device;
305 | } else {
306 | return 0;
307 | }
308 | }
309 |
310 | private loadAndPlayAudioFile(file: string) {
311 | let soundPath = file;
312 | if (file.indexOf('/') !== 0) {
313 | // using relative path, resolve to bundle
314 | let fileParts = file.split('.');
315 | let filePath = fileParts[0];
316 | let fileExt = fileParts[1];
317 | soundPath = NSBundle.mainBundle().pathForResourceOfType(filePath, fileExt);
318 | }
319 | let url = NSURL.fileURLWithPath(soundPath);
320 | this._currentAudioFile = EZAudioFile.audioFileWithURL(url);
321 | this._delegate.player.playAudioFile(this._currentAudioFile);
322 | this._audioFileLoaded = true;
323 | this._playing = true;
324 | }
325 |
326 | private noFileError(): void {
327 | console.log('No audio file loaded.');
328 | }
329 | }
--------------------------------------------------------------------------------
/src/recorder.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * iOS
3 | */
4 | import {Observable, EventData} from 'data/observable';
5 | import {EZNotificationObserver} from './core';
6 |
7 | class TNSEZRecorderDelegate extends NSObject {
8 | public static ObjCProtocols = [EZRecorderDelegate, EZMicrophoneDelegate];
9 | public recorder: any;
10 | public microphone: any;
11 | public audioEvents: Observable;
12 | public isRecording: boolean = false;
13 | private _recordingSession: any;
14 | private _observers: Array;
15 | private _bufferEvent: EventData;
16 | private _recordTimeEvent: EventData;
17 |
18 | public initRecorder() {
19 | this.initSession();
20 | this._recordingSession.requestRecordPermission((allowed: boolean) => {
21 | if (allowed) {
22 | this.microphone = EZMicrophone.microphoneWithDelegate(this);
23 | // errorRef = new interop.Reference();
24 | // this._recordingSession.overrideOutputAudioPortError(AVAudioSessionPortOverrideSpeaker, errorRef);
25 | // if (errorRef) {
26 | // console.log(`Error overriding output to the speaker: ${errorRef.localizedDescription}`);
27 | // }
28 | this.setupEvents();
29 | }
30 | });
31 | }
32 |
33 | private initSession() {
34 | this._recordingSession = AVAudioSession.sharedInstance();
35 | let errorRef = new interop.Reference();
36 | //AVAudioSessionCategoryRecord
37 | //AVAudioSessionCategoryPlayAndRecord
38 | this._recordingSession.setCategoryError(AVAudioSessionCategoryPlayAndRecord, errorRef);
39 | if (errorRef) {
40 | console.log(`setCategoryError: ${errorRef.value}`);
41 | }
42 | this._recordingSession.setActiveError(true, null);
43 | }
44 |
45 | public toggleRecord(filePath?: string) {
46 | if (this.isRecording) {
47 | this.microphone.stopFetchingAudio();
48 | this.finish();
49 | } else {
50 | this.initSession();
51 | this.microphone.startFetchingAudio();
52 |
53 | this.recorder = EZRecorder.recorderWithURLClientFormatFileTypeDelegate(NSURL.fileURLWithPath(filePath), this.microphone.audioStreamBasicDescription(), EZRecorderFileTypeM4A, this);
54 | }
55 | this.isRecording = !this.isRecording;
56 | }
57 |
58 | public finish() {
59 | this.recorder.closeAudioFile();
60 | }
61 |
62 | // delegate notifications and events
63 | public microphoneHasBufferListWithBufferSizeWithNumberOfChannels(microphone, bufferList, bufferSize, numberOfChannels) {
64 | if (this.isRecording) {
65 | this.recorder.appendDataFromBufferListWithBufferSize(bufferList, bufferSize);
66 | }
67 | }
68 |
69 | public recorderUpdatedCurrentTime(recorder:any) {
70 | let formattedCurrentTime = recorder.formattedCurrentTime;
71 | console.log(`recording time: ${formattedCurrentTime}`);
72 | if (this.audioEvents) {
73 | this._recordTimeEvent.data.time = formattedCurrentTime;
74 | this.audioEvents.notify(this._recordTimeEvent);
75 | }
76 | }
77 |
78 | public microphoneHasAudioReceivedWithBufferSizeWithNumberOfChannels(microphone, buffer, bufferSize, numberOfChannels) {
79 | // console.log(`record buffer: ${buffer.value[0]}`);
80 | // console.log(`record bufferSize: ${bufferSize}`);
81 | if (this.audioEvents) {
82 | this._bufferEvent.data.buffer = buffer.value;
83 | this._bufferEvent.data.bufferSize = bufferSize;
84 | this.audioEvents.notify(this._bufferEvent);
85 | }
86 | }
87 |
88 | public microphoneChangedDevice(device) {
89 | console.log(`Changed input device: ${device}`);
90 | }
91 |
92 | public microphoneChangedPlayingState(mic: any, isPlaying: boolean) {
93 | console.log(`microphone changed state: ${isPlaying}`);
94 | }
95 |
96 | public recorderDidClose(recorder) {
97 | this.recorder.delegate = undefined;
98 | }
99 |
100 | private setupEvents() {
101 | this.audioEvents = new Observable();
102 | this._bufferEvent = {
103 | eventName: 'audioBuffer',
104 | data: {
105 | buffer: 0,
106 | bufferSize: 0
107 | }
108 | };
109 | this._recordTimeEvent = {
110 | eventName: 'recordTime',
111 | data: {
112 | time: 0
113 | }
114 | };
115 | }
116 | }
117 |
118 | // https://github.com/syedhali/EZAudio#EZRecorder
119 | export class TNSEZRecorder {
120 | private _delegate: TNSEZRecorderDelegate;
121 |
122 | constructor() {
123 | this._delegate = new TNSEZRecorderDelegate();
124 | this._delegate.initRecorder();
125 | }
126 |
127 | public delegate(): any {
128 | return this._delegate;
129 | }
130 |
131 | public record(filePath: string) {
132 | this._delegate.toggleRecord(filePath);
133 | }
134 |
135 | public stop() {
136 | this._delegate.toggleRecord();
137 | }
138 |
139 | public finish() {
140 | this._delegate.finish();
141 | }
142 |
143 | public isRecording(): boolean {
144 | return this._delegate.isRecording;
145 | }
146 |
147 | public deviceInputs(): Array {
148 | return EZAudioDevice.inputDevices;
149 | }
150 |
151 | public setDevice(device): void {
152 | this._delegate.microphone.setDevice(device);
153 | }
154 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": true,
6 | "removeComments": true,
7 | "noLib": false,
8 | "lib": [
9 | "es2016"
10 | ],
11 | "sourceMap": true,
12 | "pretty": true,
13 | "allowUnreachableCode": false,
14 | "allowUnusedLabels": false,
15 | "noEmitHelpers": true,
16 | "noEmitOnError": false,
17 | "noImplicitAny": false,
18 | "noImplicitReturns": true,
19 | "noImplicitUseStrict": false,
20 | "noFallthroughCasesInSwitch": true,
21 | "typeRoots": [
22 | "./node_modules/@types",
23 | "./node_modules"
24 | ],
25 | "types": [
26 | ]
27 | },
28 | "exclude": [
29 | "demo",
30 | "node_modules"
31 | ],
32 | "compileOnSave": false
33 | }
--------------------------------------------------------------------------------