@lls/lls-audio 0.0.14 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lls/lls-audio",
3
- "version": "0.0.14",
3
+ "version": "0.0.17",
4
4
  "main": "index.js",
5
5
  "devDependencies": {
6
6
  "@kadira/storybook": "^2.21.0",
@@ -34,11 +34,11 @@
34
34
  "node": ">=0.12.0"
35
35
  },
36
36
  "repository": {
37
- "url": "github.com/lelivrescolaire/lls-ui"
37
+ "url": "github.com/lelivrescolaire/sound-poke"
38
38
  },
39
39
  "scripts": {
40
40
  "start": "npm run storybook",
41
- "storybook": "start-storybook -p 9009 -s public",
41
+ "storybook": "start-storybook -p 9009",
42
42
  "deploy-storybook": "storybook-to-ghpages",
43
43
  "build": "rm -rf dist && mkdir dist && babel -d dist src",
44
44
  "copy:package": "cp -R package.json dist/",
package/player/Audio.js CHANGED
@@ -28,6 +28,8 @@ var Player = function (_Component) {
28
28
 
29
29
  var _this = _possibleConstructorReturn(this, (Player.__proto__ || Object.getPrototypeOf(Player)).call(this, props));
30
30
 
31
+ _this.requestAnimationId = null;
32
+
31
33
  _this.play = function () {
32
34
  return _this.refs.player.play();
33
35
  };
@@ -65,12 +67,27 @@ var Player = function (_Component) {
65
67
  return _this.setStateWithCallback({ currentTime: _this.getCurrentTime() });
66
68
  };
67
69
 
70
+ _this.watchAudioUpdate = function () {
71
+ var step = function step(timestamp) {
72
+ if (_this.getCurrentTime() !== _this.state.currentTime) {
73
+ _this.setStateWithCallback({ currentTime: _this.getCurrentTime() });
74
+ }
75
+ _this.requestAnimationId = requestAnimationFrame(step);
76
+ };
77
+
78
+ _this.requestAnimationId = requestAnimationFrame(step);
79
+ };
80
+
68
81
  _this.onPlay = function () {
69
- return _this.setStateWithCallback({ play: true });
82
+ _this.setStateWithCallback({ play: true }, function () {
83
+ return _this.watchAudioUpdate();
84
+ });
70
85
  };
71
86
 
72
87
  _this.onPause = function () {
73
- return _this.setStateWithCallback({ play: false });
88
+ _this.setStateWithCallback({ play: false }, function () {
89
+ return cancelAnimationFrame(_this.requestAnimationId);
90
+ });
74
91
  };
75
92
 
76
93
  _this.onEnded = function () {
@@ -92,7 +109,7 @@ var Player = function (_Component) {
92
109
  duration: 0
93
110
  };
94
111
 
95
- (0, _utils.autoBind)(['onEnded', 'onDurationChange', 'onAudioUpdate', 'onPlay', 'onPause'], _this);
112
+ (0, _utils.autoBind)(['onEnded', 'onDurationChange', 'onPlay', 'onPause', 'onAudioUpdate'], _this);
96
113
 
97
114
  // Execute callback onChange props
98
115
  _this.setStateWithCallback = function (state) {
@@ -139,14 +156,13 @@ var Player = function (_Component) {
139
156
  key: 'componentDidMount',
140
157
  value: function componentDidMount() {
141
158
  this.props.onChange(this.state);
142
-
143
159
  var player = this.refs.player;
144
160
 
145
161
  player.addEventListener('ended', this.onEnded);
146
162
  player.addEventListener('durationchange', this.onDurationChange);
147
- player.addEventListener('timeupdate', this.onAudioUpdate);
148
163
  player.addEventListener('play', this.onPlay);
149
164
  player.addEventListener('pause', this.onPause);
165
+ player.addEventListener('timeupdate', this.onAudioUpdate);
150
166
  }
151
167
  }, {
152
168
  key: 'componentWillUnmount',
@@ -155,9 +171,9 @@ var Player = function (_Component) {
155
171
 
156
172
  player.removeEventListener('ended', this.onEnded);
157
173
  player.removeEventListener('durationchange', this.onDurationChange);
158
- player.removeEventListener('timeupdate', this.onAudioUpdate);
159
174
  player.removeEventListener('play', this.onPlay);
160
175
  player.removeEventListener('pause', this.onPause);
176
+ player.removeEventListener('timeupdate', this.onAudioUpdate);
161
177
  }
162
178
  }, {
163
179
  key: 'getCurrentTime',
@@ -168,6 +184,7 @@ var Player = function (_Component) {
168
184
  startTime = _state$currentRange.startTime,
169
185
  endTime = _state$currentRange.endTime,
170
186
  loop = _state.loop;
187
+
171
188
  // check if current time is in the middle of current range
172
189
 
173
190
  if (currentTime > endTime || currentTime < startTime) {
@@ -183,6 +200,7 @@ var Player = function (_Component) {
183
200
  var duration = this.refs.player.duration;
184
201
 
185
202
  this.setStateWithCallback({
203
+ play: false,
186
204
  currentTime: 0,
187
205
  duration: duration,
188
206
  currentRange: { startTime: 0, endTime: duration }
package/player/index.js CHANGED
@@ -84,7 +84,20 @@ var Player = function (_Component) {
84
84
  return _this.refs.audio.setRange(range);
85
85
  };
86
86
 
87
- _this.state = { waveform: false, loading: true, containerWidth: 0 };
87
+ _this.toggleVisible = function () {
88
+ var visible = _this.state.visible;
89
+
90
+ _this.setState({
91
+ visible: !visible
92
+ });
93
+ };
94
+
95
+ _this.state = {
96
+ waveform: false,
97
+ loading: true,
98
+ containerWidth: 0,
99
+ visible: true
100
+ };
88
101
  _this.setContainerWidth = (0, _utils.debounce)(_this.setContainerWidth.bind(_this), 500);
89
102
  _this.onKeyPress = _this.onKeyPress.bind(_this);
90
103
  return _this;
@@ -109,7 +122,7 @@ var Player = function (_Component) {
109
122
  }, {
110
123
  key: 'componentWillReceiveProps',
111
124
  value: function componentWillReceiveProps(newProps) {
112
- if (newProps.url !== this.props.url) {
125
+ if (newProps.src !== this.props.src || newProps.visible !== newProps.visible) {
113
126
  this.initialize(newProps);
114
127
  }
115
128
  }
@@ -138,10 +151,16 @@ var Player = function (_Component) {
138
151
  value: function initialize(props) {
139
152
  var _this3 = this;
140
153
 
141
- var url = props.url;
154
+ var src = props.src,
155
+ _props$visible = props.visible,
156
+ visible = _props$visible === undefined ? true : _props$visible;
142
157
 
143
- (0, _audio.getWaveform)(url).then(function (waveform) {
144
- _this3.setState({ waveform: waveform, loading: false });
158
+ (0, _audio.getWaveform)(src).then(function (waveform) {
159
+ _this3.setState({
160
+ waveform: waveform,
161
+ loading: false,
162
+ visible: visible
163
+ });
145
164
  });
146
165
  }
147
166
  }, {
@@ -149,7 +168,9 @@ var Player = function (_Component) {
149
168
  value: function render() {
150
169
  var _this4 = this;
151
170
 
152
- var url = this.props.url;
171
+ var _props = this.props,
172
+ src = _props.src,
173
+ className = _props.className;
153
174
  var _state = this.state,
154
175
  loading = _state.loading,
155
176
  duration = _state.duration,
@@ -159,7 +180,8 @@ var Player = function (_Component) {
159
180
  containerWidth = _state.containerWidth,
160
181
  speed = _state.speed,
161
182
  _state$currentRange = _state.currentRange,
162
- currentRange = _state$currentRange === undefined ? {} : _state$currentRange;
183
+ currentRange = _state$currentRange === undefined ? {} : _state$currentRange,
184
+ visible = _state.visible;
163
185
  var _currentRange$startTi = currentRange.startTime,
164
186
  startTime = _currentRange$startTi === undefined ? 0 : _currentRange$startTi,
165
187
  _currentRange$endTime = currentRange.endTime,
@@ -167,7 +189,7 @@ var Player = function (_Component) {
167
189
 
168
190
  return _react2.default.createElement(
169
191
  'div',
170
- { className: 'Audio_player ' + (0, _noImportant.css)(styles.player) },
192
+ { className: 'Audio_player ' + (0, _noImportant.css)(styles.player) + ' ' + className + ' ' + (visible ? '' : (0, _noImportant.css)(styles.hidden)) },
171
193
  _react2.default.createElement(
172
194
  'div',
173
195
  { className: (0, _noImportant.css)(styles.play_and_stop), onClick: function onClick() {
@@ -177,7 +199,7 @@ var Player = function (_Component) {
177
199
  ),
178
200
  _react2.default.createElement(_Audio2.default, { onChange: function onChange(state) {
179
201
  return _this4.setState(_extends({}, state));
180
- }, ref: 'audio', src: url }),
202
+ }, ref: 'audio', src: src }),
181
203
  _react2.default.createElement(
182
204
  'div',
183
205
  { className: (0, _noImportant.css)(styles.timeline), ref: 'container' },
@@ -211,7 +233,10 @@ var Player = function (_Component) {
211
233
  _react2.default.createElement(_reactInputRange2.default, { classNames: _volumeClassNames2.default, maxValue: 100, minValue: 0, value: this.state.volume, onChange: function onChange(c, v) {
212
234
  return _this4.refs.audio.setVolume(v);
213
235
  } })
214
- )
236
+ ),
237
+ _react2.default.createElement('div', { className: 'Close ' + (0, _noImportant.css)(styles.close), onClick: function onClick() {
238
+ return _this4.toggleVisible();
239
+ } })
215
240
  );
216
241
  }
217
242
  }]);
@@ -60,5 +60,18 @@ exports.default = {
60
60
  ':hover': {
61
61
  overflow: 'visible'
62
62
  }
63
- })
63
+ }),
64
+ close: {
65
+ cursor: 'pointer',
66
+ position: 'absolute',
67
+ top: 1,
68
+ right: 3,
69
+ ':after': {
70
+ content: "'✖'",
71
+ color: '#e5e5e5'
72
+ }
73
+ },
74
+ hidden: {
75
+ display: 'none'
76
+ }
64
77
  };
@@ -55,8 +55,7 @@ var styles = exports.styles = _noImportant.StyleSheet.create({
55
55
  },
56
56
  trackActive: {
57
57
  backgroundColor: 'transparent'
58
- },
59
- sliderContainer: {}
58
+ }
60
59
  });
61
60
 
62
61
  exports.default = (0, _utils.concatStyle)(_sliderDefaultClassNames2.default, styles);
@@ -109,6 +109,7 @@ var DesktopAudioRecorder = function (_Component) {
109
109
  key: 'record',
110
110
  value: function record() {
111
111
  this.setState({
112
+ recordedData: null,
112
113
  isRecording: true
113
114
  });
114
115
  }
@@ -150,12 +151,37 @@ var DesktopAudioRecorder = function (_Component) {
150
151
  value: function onMediaError(e) {
151
152
  console.error('media error', e);
152
153
  }
154
+
155
+ // This is just for demo purposes: in real life we'll upload the file somewhere
156
+
157
+ }, {
158
+ key: 'updateAudioDom',
159
+ value: function updateAudioDom() {
160
+ var recordedData = this.state.recordedData;
161
+
162
+ var audioEl = document.querySelector('audio');
163
+
164
+ if (audioEl) {
165
+ var wav = (0, _audio.audioBufferToWav)(recordedData);
166
+ var audioBlob = new Blob([wav], { type: 'audio/wav' });
167
+ var reader = new FileReader();
168
+ reader.onload = function (e) {
169
+ // e.target.result will hold the base64 data.
170
+ // Note: It includes the pre-text "data:audio/<format>;base64,<base64 data>"
171
+ audioEl.src = e.target.result;
172
+ };
173
+ reader.readAsDataURL(audioBlob);
174
+ }
175
+ }
153
176
  }, {
154
177
  key: 'stopRecording',
155
178
  value: function stopRecording() {
156
179
  this.setState({
157
180
  isRecording: false
158
181
  });
182
+
183
+ // Update audio element with new file
184
+ this.updateAudioDom();
159
185
  }
160
186
  }]);
161
187
 
@@ -118,7 +118,6 @@ var DesktopRecorder = function (_Component) {
118
118
  var recordedData = this.state.recordedData;
119
119
 
120
120
  var mergedBuffer = (0, _audio.appendBuffer)(recordedData, audioBuffer);
121
- console.log("mergedBuffer", mergedBuffer);
122
121
  this.setState({
123
122
  recordedData: mergedBuffer
124
123
  });
package/stories/player.js CHANGED
@@ -62,7 +62,7 @@ var PlayerExample = function (_Component) {
62
62
  'http://lls-media-public.s3.amazonaws.com/upload/LeLivreScolaire/p15vcjbl4lvpl9l7g8ov8ammh1.mp3'
63
63
  )
64
64
  ),
65
- _react2.default.createElement(_player2.default, { url: this.state.url })
65
+ _react2.default.createElement(_player2.default, { src: this.state.url })
66
66
  );
67
67
  }
68
68
  }]);
@@ -22,8 +22,8 @@ var RecorderExample = function RecorderExample() {
22
22
  return _react2.default.createElement(
23
23
  'div',
24
24
  null,
25
- _react2.default.createElement(_player2.default, { url: 'english.mp3' }),
26
- _react2.default.createElement(_recorder2.default, null)
25
+ _react2.default.createElement(_recorder2.default, null),
26
+ _react2.default.createElement('audio', { controls: true })
27
27
  );
28
28
  };
29
29
 
package/utils/audio.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.convertBlobToaudioBuffer = exports.appendBuffer = exports.convertAudioBufferToWaveform = exports.getWaveform = undefined;
6
+ exports.audioBufferToWav = exports.convertBlobToaudioBuffer = exports.appendBuffer = exports.convertAudioBufferToWaveform = exports.getWaveform = undefined;
7
7
 
8
8
  var _audioContext = require('./audioContext');
9
9
 
@@ -120,4 +120,93 @@ var convertBlobToaudioBuffer = exports.convertBlobToaudioBuffer = function conve
120
120
  });
121
121
  };
122
122
  fileReader.readAsArrayBuffer(blob);
123
+ };
124
+
125
+ var audioBufferToWav = exports.audioBufferToWav = function audioBufferToWav(buffer, opt) {
126
+ opt = opt || {};
127
+
128
+ var numChannels = buffer.numberOfChannels;
129
+ var sampleRate = buffer.sampleRate;
130
+ var format = opt.float32 ? 3 : 1;
131
+ var bitDepth = format === 3 ? 32 : 16;
132
+
133
+ var result = numChannels === 2 ? interleave(buffer.getChannelData(0), buffer.getChannelData(1)) : buffer.getChannelData(0);
134
+
135
+ return encodeWAV(result, format, sampleRate, numChannels, bitDepth);
136
+ };
137
+
138
+ var encodeWAV = function encodeWAV(samples, format, sampleRate, numChannels, bitDepth) {
139
+ var bytesPerSample = bitDepth / 8;
140
+ var blockAlign = numChannels * bytesPerSample;
141
+
142
+ var buffer = new ArrayBuffer(44 + samples.length * bytesPerSample);
143
+ var view = new DataView(buffer);
144
+
145
+ /* RIFF identifier */
146
+ writeString(view, 0, 'RIFF');
147
+ /* RIFF chunk length */
148
+ view.setUint32(4, 36 + samples.length * bytesPerSample, true);
149
+ /* RIFF type */
150
+ writeString(view, 8, 'WAVE');
151
+ /* format chunk identifier */
152
+ writeString(view, 12, 'fmt ');
153
+ /* format chunk length */
154
+ view.setUint32(16, 16, true);
155
+ /* sample format (raw) */
156
+ view.setUint16(20, format, true);
157
+ /* channel count */
158
+ view.setUint16(22, numChannels, true);
159
+ /* sample rate */
160
+ view.setUint32(24, sampleRate, true);
161
+ /* byte rate (sample rate * block align) */
162
+ view.setUint32(28, sampleRate * blockAlign, true);
163
+ /* block align (channel count * bytes per sample) */
164
+ view.setUint16(32, blockAlign, true);
165
+ /* bits per sample */
166
+ view.setUint16(34, bitDepth, true);
167
+ /* data chunk identifier */
168
+ writeString(view, 36, 'data');
169
+ /* data chunk length */
170
+ view.setUint32(40, samples.length * bytesPerSample, true);
171
+ if (format === 1) {
172
+ // Raw PCM
173
+ floatTo16BitPCM(view, 44, samples);
174
+ } else {
175
+ writeFloat32(view, 44, samples);
176
+ }
177
+ return buffer;
178
+ };
179
+
180
+ var interleave = function interleave(inputL, inputR) {
181
+ var length = inputL.length + inputR.length;
182
+ var result = new Float32Array(length);
183
+
184
+ var index = 0;
185
+ var inputIndex = 0;
186
+
187
+ while (index < length) {
188
+ result[index++] = inputL[inputIndex];
189
+ result[index++] = inputR[inputIndex];
190
+ inputIndex++;
191
+ }
192
+ return result;
193
+ };
194
+
195
+ var writeFloat32 = function writeFloat32(output, offset, input) {
196
+ for (var i = 0; i < input.length; i++, offset += 4) {
197
+ output.setFloat32(offset, input[i], true);
198
+ }
199
+ };
200
+
201
+ var floatTo16BitPCM = function floatTo16BitPCM(output, offset, input) {
202
+ for (var i = 0; i < input.length; i++, offset += 2) {
203
+ var s = Math.max(-1, Math.min(1, input[i]));
204
+ output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
205
+ }
206
+ };
207
+
208
+ var writeString = function writeString(view, offset, string) {
209
+ for (var i = 0; i < string.length; i++) {
210
+ view.setUint8(offset + i, string.charCodeAt(i));
211
+ }
123
212
  };