├── src
├── fx
│ ├── export.js
│ ├── volume
│ │ ├── export.js
│ │ └── volume-lib.js
│ └── equalizer
│ │ ├── default.bands.js
│ │ ├── export.js
│ │ ├── equalizer-static.js
│ │ ├── equalizer-band.js
│ │ ├── default.presets.js
│ │ └── equalizer.js
├── flash
│ ├── build
│ │ └── player-2_1.swf
│ ├── src
│ │ ├── AudioEvent.as
│ │ ├── AudioLoader.as
│ │ └── AudioManager.as
│ ├── loader.js
│ ├── flashembedder.js
│ └── flash-interface.js
├── logger
│ ├── export.js
│ └── logger.js
├── lib
│ ├── net
│ │ └── error
│ │ │ ├── export.js
│ │ │ └── loader-error.js
│ ├── noop.js
│ ├── class
│ │ ├── pure-instance.js
│ │ ├── error-class.js
│ │ └── proxy.js
│ ├── async
│ │ ├── reject.js
│ │ ├── deferred.js
│ │ ├── promise.js
│ │ └── events.js
│ ├── export.js
│ ├── data
│ │ └── merge.js
│ └── browser
│ │ ├── detect.js
│ │ └── audioContextMonkeyPatch.js
├── error
│ ├── export.js
│ ├── playback-error.js
│ └── audio-error.js
├── index.js
├── export.js
├── audio-static.js
├── config.js
└── IAudioImplementation.jsdoc
├── dist
└── player-2_1.swf
├── tutorial
├── images
│ ├── alias.png
│ ├── demo.png
│ ├── fon.png
│ ├── notch.png
│ ├── sin.png
│ ├── allpass.png
│ ├── lowpass.png
│ ├── peaking.png
│ ├── spectro.jpg
│ ├── bandpass.png
│ ├── clipping.png
│ ├── highpass.png
│ ├── highshelf.png
│ ├── lowshelf.png
│ ├── bandpass-filter.png
│ ├── bandstop-filter.png
│ ├── discrete-signal.png
│ ├── highpass-filter.png
│ ├── lowpass-filter.png
│ └── quantized-signal.png
├── contrib.md
├── quick-start.md
├── corner-case.md
└── fx.md
├── jsdoc
├── doc
│ ├── gfm-files
│ │ ├── exported.hbs
│ │ ├── event.hbs
│ │ ├── typedef.hbs
│ │ ├── parents-line.hbs
│ │ ├── type.hbs
│ │ ├── export-tree.hbs
│ │ ├── member.hbs
│ │ ├── namespace.hbs
│ │ ├── index.hbs
│ │ ├── property-table.hbs
│ │ ├── layout.hbs
│ │ ├── params-table.hbs
│ │ ├── class.hbs
│ │ ├── function.hbs
│ │ └── children.hbs
│ ├── gfm-single
│ │ ├── exported.hbs
│ │ ├── event.hbs
│ │ ├── parents-line.hbs
│ │ ├── type.hbs
│ │ ├── typedef.hbs
│ │ ├── export-tree.hbs
│ │ ├── member.hbs
│ │ ├── namespace.hbs
│ │ ├── property-table.hbs
│ │ ├── children.hbs
│ │ ├── params-table.hbs
│ │ ├── function.hbs
│ │ ├── layout.hbs
│ │ └── class.hbs
│ ├── jsdoc
│ │ ├── method.hbs
│ │ ├── fullname.hbs
│ │ ├── type.hbs
│ │ ├── event.hbs
│ │ ├── export-tree.hbs
│ │ ├── params-table.hbs
│ │ ├── property-table.hbs
│ │ ├── member.hbs
│ │ ├── namespace.hbs
│ │ ├── typedef.hbs
│ │ ├── function.hbs
│ │ ├── class.hbs
│ │ ├── children.hbs
│ │ └── layout.hbs
│ ├── jsdoc-tech
│ │ ├── event.hbs
│ │ ├── type.hbs
│ │ ├── typedef.hbs
│ │ ├── params-table.hbs
│ │ ├── property-table.hbs
│ │ ├── member.hbs
│ │ ├── function.hbs
│ │ ├── class.hbs
│ │ ├── children.hbs
│ │ ├── layout.hbs
│ │ └── namespace.hbs
│ ├── render.gfm.js
│ ├── render.jsdoc.js
│ ├── render.js
│ └── publish.js
└── jsdoc.public.json
├── AUTHORS
├── .gitignore
├── spec
├── LoaderError.md
├── readme.md
├── Deferred.md
├── ErrorClass.md
├── global.md
├── info.md
├── PlaybackError.md
├── Promise.md
├── AudioError.md
├── Logger.md
├── AbortablePromise.md
├── volumeLib.md
├── config.md
├── Events.md
└── Equalizer.md
├── package.json
├── examples
└── quick-start
│ ├── index.html
│ ├── index.css
│ └── index.js
├── test
└── web-audio-api
│ ├── index.html
│ └── index.js
├── Makefile
├── Gruntfile.js
└── readme.md
/src/fx/export.js:
--------------------------------------------------------------------------------
1 | require('../export');
2 |
3 | ya.music.Audio.fx = {};
4 |
--------------------------------------------------------------------------------
/dist/player-2_1.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/dist/player-2_1.swf
--------------------------------------------------------------------------------
/tutorial/images/alias.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/alias.png
--------------------------------------------------------------------------------
/tutorial/images/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/demo.png
--------------------------------------------------------------------------------
/tutorial/images/fon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/fon.png
--------------------------------------------------------------------------------
/tutorial/images/notch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/notch.png
--------------------------------------------------------------------------------
/tutorial/images/sin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/sin.png
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/exported.hbs:
--------------------------------------------------------------------------------
1 | {{#if exported}}
2 | **Доступен извне как:** `{{exported}}`
3 | {{/if}}
4 |
--------------------------------------------------------------------------------
/tutorial/images/allpass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/allpass.png
--------------------------------------------------------------------------------
/tutorial/images/lowpass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/lowpass.png
--------------------------------------------------------------------------------
/tutorial/images/peaking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/peaking.png
--------------------------------------------------------------------------------
/tutorial/images/spectro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/spectro.jpg
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/exported.hbs:
--------------------------------------------------------------------------------
1 | {{#if exported}}
2 | **Доступен извне как:** `{{exported}}`
3 | {{/if}}
4 |
--------------------------------------------------------------------------------
/src/flash/build/player-2_1.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/src/flash/build/player-2_1.swf
--------------------------------------------------------------------------------
/src/fx/volume/export.js:
--------------------------------------------------------------------------------
1 | require('../export');
2 |
3 | ya.music.Audio.fx.volumeLib = require('./volume-lib');
4 |
--------------------------------------------------------------------------------
/tutorial/images/bandpass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/bandpass.png
--------------------------------------------------------------------------------
/tutorial/images/clipping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/clipping.png
--------------------------------------------------------------------------------
/tutorial/images/highpass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/highpass.png
--------------------------------------------------------------------------------
/tutorial/images/highshelf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/highshelf.png
--------------------------------------------------------------------------------
/tutorial/images/lowshelf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/lowshelf.png
--------------------------------------------------------------------------------
/src/fx/equalizer/default.bands.js:
--------------------------------------------------------------------------------
1 | module.exports = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000];
2 |
--------------------------------------------------------------------------------
/src/fx/equalizer/export.js:
--------------------------------------------------------------------------------
1 | require('../export');
2 |
3 | ya.music.Audio.fx.Equalizer = require('./equalizer');
4 |
--------------------------------------------------------------------------------
/tutorial/images/bandpass-filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/bandpass-filter.png
--------------------------------------------------------------------------------
/tutorial/images/bandstop-filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/bandstop-filter.png
--------------------------------------------------------------------------------
/tutorial/images/discrete-signal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/discrete-signal.png
--------------------------------------------------------------------------------
/tutorial/images/highpass-filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/highpass-filter.png
--------------------------------------------------------------------------------
/tutorial/images/lowpass-filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/lowpass-filter.png
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/method.hbs:
--------------------------------------------------------------------------------
1 | "{{> fullname}}": function({{#each params}}{{#unless @first}}, {{/unless}}{{{name}}}{{/each}}){},
2 |
--------------------------------------------------------------------------------
/tutorial/images/quantized-signal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yandex/audio-js/master/tutorial/images/quantized-signal.png
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/fullname.hbs:
--------------------------------------------------------------------------------
1 | {{#if parent}}{{{parent.longname}}}{{#if static}}.{{else inner}}~{{else}}#{{/if}}{{/if}}{{{name}}}
2 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/type.hbs:
--------------------------------------------------------------------------------
1 | {{#if type.names}}\{ {{#each type.names}}{{#unless @first}} | {{/unless}}{{{this}}}{{/each}} \}{{/if}}
2 |
--------------------------------------------------------------------------------
/src/logger/export.js:
--------------------------------------------------------------------------------
1 | require("../export");
2 |
3 | var Logger = require('./logger');
4 |
5 | ya.music.Audio.Logger = Logger;
6 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/event.hbs:
--------------------------------------------------------------------------------
1 | #### *event* {{{longname}}}
2 |
3 | {{{description}}}
4 |
5 | {{> params-table}}
6 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/event.hbs:
--------------------------------------------------------------------------------
1 | #### *event* {{{longname}}}
2 |
3 | {{{description}}}
4 |
5 | {{> params-table}}
6 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/typedef.hbs:
--------------------------------------------------------------------------------
1 | #### *type* {{{name}}} {{> type line=1}}
2 |
3 | {{{description}}}
4 |
5 | {{> property-table}}
6 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/parents-line.hbs:
--------------------------------------------------------------------------------
1 | {{#if parent}}{{#if parent.parent}}{{> parents-line parent}}{{/if}}{{parent.name}}{{#if static}}.{{else}}#{{/if}}{{/if}}
2 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/type.hbs:
--------------------------------------------------------------------------------
1 | {{#if type.names}}{{#if line}}: {{/if}}{{#each type.names}}{{#unless @first}} \| {{/unless}}\{@link {{this}}\}{{/each}}{{/if}}
2 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/parents-line.hbs:
--------------------------------------------------------------------------------
1 | {{#if parent}}{{#if parent.parent}}{{> parents-line parent}}{{/if}}{{parent.name}}{{#if static}}.{{else}}#{{/if}}{{/if}}
2 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/type.hbs:
--------------------------------------------------------------------------------
1 | {{#if type.names}}{{#if line}}: {{/if}}{{#each type.names}}{{#unless @first}} \| {{/unless}}\{@link {{this}}\}{{/each}}{{/if}}
2 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/typedef.hbs:
--------------------------------------------------------------------------------
1 | #### *type* {{{name}}} {{> type line=1}}
2 |
3 | {{{description}}}
4 |
5 | {{> property-table}}
6 |
--------------------------------------------------------------------------------
/src/lib/net/error/export.js:
--------------------------------------------------------------------------------
1 | require('../../../export');
2 |
3 | var LoaderError = require('./loader-error');
4 |
5 | ya.music.Audio.LoaderError = LoaderError;
6 |
--------------------------------------------------------------------------------
/src/lib/noop.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Заглушка в виде пустой функции на все случаи жизни
3 | * @private
4 | */
5 | var noop = function() {};
6 |
7 | module.exports = noop;
8 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/export-tree.hbs:
--------------------------------------------------------------------------------
1 | {{#each sub}}
2 | {{../indent}}* {{#if link}}\{@link {{link}} {{@key}}\}{{else}}{{@key}}{{/if}}
3 | {{> export-tree}}
4 | {{/each}}
5 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/export-tree.hbs:
--------------------------------------------------------------------------------
1 | {{#each sub}}
2 | {{../indent}}* {{#if link}}\{@link {{link}} {{@key}}\}{{else}}{{@key}}{{/if}}
3 | {{> export-tree}}
4 | {{/each}}
5 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | The following authors have created the source code of "YandexAudio" published and distributed by YANDEX LLC as the owner:
2 |
3 | Silaev Mikhail
4 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/event.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * {{#if type.names}}@type {{> type}}{{/if}}
4 | * @event
5 | * @name {{{longname}}}
6 | {{> params-table}}
7 | */
8 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/type.hbs:
--------------------------------------------------------------------------------
1 | {{#if type.names}}{{#unless nowrap}}\{ {{/unless}}{{#each type.names}}{{#unless @first}} | {{/unless}}{{{this}}}{{/each}} {{#unless nowrap}}\}{{/unless}}{{/if}}
2 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/event.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * {{#if type.names}}@type {{> type}}{{/if}}
4 | * @name {{{longname}}}
5 | * @kind event
6 | {{> params-table}}
7 | */
8 | "{{{longname}}}": "",
9 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/typedef.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * @typedef {{> type}} {{#if parent}}{{parent.longname}}.{{/if}}{{{name}}}
4 | *
5 | {{> property-table}}
6 | */
7 |
8 | {{> children children}}
9 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/export-tree.hbs:
--------------------------------------------------------------------------------
1 | {{#each sub}}
2 | {{#unless link}}
3 | /**
4 | * @name {{full}}
5 | * @namespace
6 | */
7 | "{{full}}": {},
8 | {{/unless}}
9 |
10 | {{> export-tree}}
11 | {{/each}}
12 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/params-table.hbs:
--------------------------------------------------------------------------------
1 | {{#each params}}
2 | * @param {{> type}} {{#if optional}}[{{/if}}{{{name}}}{{#if defaultValue}}={{{defaultValue}}}{{/if}}{{#if optional}}]{{/if}} - {{{description}}}
3 | {{/each}}
4 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/params-table.hbs:
--------------------------------------------------------------------------------
1 | {{#each params}}
2 | * @param {{> type}} {{#if optional}}[{{/if}}{{{name}}}{{#if defaultValue}}={{{defaultValue}}}{{/if}}{{#if optional}}]{{/if}} {{{description}}}
3 | {{/each}}
4 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/member.hbs:
--------------------------------------------------------------------------------
1 | #### {{#if parent}}{{> parents-line}}{{else inherits}}{{else}} *inner* {{/if}}{{{name}}} {{> type line=1}}
2 |
3 | {{{description}}}
4 |
5 | {{> property-table}}
6 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/member.hbs:
--------------------------------------------------------------------------------
1 | #### {{#if parent}}{{> parents-line}}{{else inherits}}{{else}} *inner* {{/if}}{{{name}}} {{> type line=1}}
2 |
3 | {{{description}}}
4 |
5 | {{> property-table}}
6 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/property-table.hbs:
--------------------------------------------------------------------------------
1 | {{#each properties}}
2 | * @property {{> type}} {{#if optional}}[{{/if}}{{{name}}}{{#if defaultValue}}={{{defaultValue}}}{{/if}}{{#if optional}}]{{/if}} - {{{description}}}
3 | {{/each}}
4 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/property-table.hbs:
--------------------------------------------------------------------------------
1 | {{#each properties}}
2 | * @property {{> type}} {{#if optional}}[{{/if}}{{{name}}}{{#if defaultValue}}={{{defaultValue}}}{{/if}}{{#if optional}}]{{/if}} {{{description}}}
3 | {{/each}}
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | browserify
2 | uglify-js
3 | grunt
4 | grunt-browserify
5 | grunt-contrib-clean
6 | grunt-contrib-uglify
7 | grunt-mkdir
8 | grunt-contrib-copy
9 | node_modules/jsdoc
10 | node_modules/ym
11 | node_modules/.bin
12 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/namespace.hbs:
--------------------------------------------------------------------------------
1 | ## *ns* {{> parents-line}}{{{name}}}
2 |
3 | {{> exported}}
4 |
5 |
6 | {{{description}}}
7 |
8 |
9 | {{> property-table}}
10 |
11 |
12 | {{> children}}
13 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/namespace.hbs:
--------------------------------------------------------------------------------
1 | ## *ns* {{> parents-line}}{{{name}}}
2 |
3 | {{> exported}}
4 |
5 |
6 | {{{description}}}
7 |
8 |
9 | {{> property-table}}
10 |
11 |
12 | {{> children}}
13 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/member.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * @name {{> fullname}}
4 | * {{#if type.names}}@type {{> type}}{{/if}}
5 | * {{#if const}}@const{{/if}}
6 | {{> property-table}}
7 | */
8 |
9 | "{{> fullname}}": undefined,
10 |
--------------------------------------------------------------------------------
/src/error/export.js:
--------------------------------------------------------------------------------
1 | require('../export');
2 |
3 | var AudioError = require('./audio-error');
4 | var PlaybackError = require('./playback-error');
5 |
6 | ya.music.Audio.AudioError = AudioError;
7 | ya.music.Audio.PlaybackError = PlaybackError;
8 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/namespace.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * @name {{{longname}}}
4 | * {{#if exported}}@alias {{{exported}}}{{/if}}
5 | {{> property-table}}
6 | * @namespace
7 | */
8 | "{{{longname}}}": {},
9 |
10 | {{> children children}}
11 |
--------------------------------------------------------------------------------
/src/fx/equalizer/equalizer-static.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @namespace EqualizerStatic
3 | * @private
4 | */
5 | var EqualizerStatic = {};
6 |
7 | /** @type {String}
8 | * @const*/
9 | EqualizerStatic.EVENT_CHANGE = "change";
10 |
11 | module.exports = EqualizerStatic;
12 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/index.hbs:
--------------------------------------------------------------------------------
1 | # Внешняя структура модуля
2 |
3 | {{#with exportTree}}
4 | {{#each symbols}}
5 | {{../indent}}* \{@link {{name}} {{path}}\}
6 | {{/each}}
7 | {{#each sub}}
8 | {{../indent}}* {{@key}}
9 | {{> export-tree}}
10 | {{/each}}
11 | {{/with}}
12 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/property-table.hbs:
--------------------------------------------------------------------------------
1 | {{#if properties}}
2 | | Имя | Тип | * | Описание |
3 | | --- | --- | --- | --- |
4 | {{#each properties}}
5 | | {{#if optional}}*\[{{/if}}{{{name}}}{{#if optional}}\]*{{/if}} | {{> type}} | {{{defaultvalue}}} | {{{description}}} |
6 | {{/each}}
7 | {{/if}}
8 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/property-table.hbs:
--------------------------------------------------------------------------------
1 | {{#if properties}}
2 | | Имя | Тип | * | Описание |
3 | | --- | --- | --- | --- |
4 | {{#each properties}}
5 | | {{#if optional}}*\[{{/if}}{{{name}}}{{#if optional}}\]*{{/if}} | {{> type}} | {{{defaultvalue}}} | {{{description}}} |
6 | {{/each}}
7 | {{/if}}
8 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/typedef.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * @typedef {{> type}} {{{name}}}
4 | * {{#if parent}}@memberof {{parent.longname}}{{/if}}
5 | * {{#if static}}@static{{/if}}
6 | * {{#if inner}}@inner{{/if}}
7 | {{> property-table}}
8 | */
9 |
10 | {{> children children}}
11 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/layout.hbs:
--------------------------------------------------------------------------------
1 | {{#if class}}{{> class class}}{{/if}}
2 | {{#if typedef}}{{> typedef typedef}}{{/if}}
3 | {{#if namespace}}{{> namespace namespace}}{{/if}}
4 | {{#if event}}{{> event event}}{{/if}}
5 | {{#if function}}{{> function function}}{{/if}}
6 | {{#if member}}{{> member member}}{{/if}}
7 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/function.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * @function
4 | * @name {{> fullname}}
5 | * {{#if exported}}@alias {{exported}}{{/if}}
6 | {{> params-table}}
7 | * {{#if returns.type.names}}@returns {{>type returns}} - {{{returns.description}}}{{/if}}
8 | */
9 | {{> method}}
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | require('./lib/browser/audioContextMonkeyPatch.js');
2 |
3 | var YandexAudio = require('./export');
4 | require('./error/export');
5 | require('./lib/net/error/export');
6 | require('./logger/export');
7 | require('./fx/equalizer/export');
8 | require('./fx/volume/export');
9 |
10 | module.exports = YandexAudio;
11 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/children.hbs:
--------------------------------------------------------------------------------
1 | {{#each children.member}}
2 |
3 | {{> member}}
4 |
5 | {{/each}}
6 |
7 |
8 | {{#each children.function}}
9 |
10 | {{> function}}
11 |
12 | {{/each}}
13 |
14 |
15 | {{#each children.event}}
16 |
17 | {{> event}}
18 |
19 | {{/each}}
20 |
21 |
22 | {{#each children.namespace}}
23 |
24 | {{> namespace}}
25 |
26 | {{/each}}
27 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/member.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * {{{description}}}
3 | * @field
4 | * @name {{{name}}}
5 | * {{#if type.names}}@type {{> type nowrap=1}}{{/if}}
6 | * {{#if const}}@const{{/if}}
7 | * {{#if parent}}@memberof {{{parent.longname}}}{{/if}}
8 | * {{#if inner}}@inner {{/if}}
9 | * {{#if static}}@static {{/if}}
10 | {{> property-table}}
11 | *
12 | */
13 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/params-table.hbs:
--------------------------------------------------------------------------------
1 | {{#if params}}
2 | {{#if line}}
3 | {{#each params}}{{#unless @first}}, {{/unless}}{{{name}}}{{> type line=1}}{{/each}}
4 | {{else}}
5 | | Имя | Тип | * | Описание |
6 | | --- | --- | --- | --- |
7 | {{#each params}}
8 | | {{#if optional}}*\[{{/if}}{{{name}}}{{#if optional}}\]*{{/if}} | {{> type}} | {{{defaultvalue}}} | {{{description}}} |
9 | {{/each}}
10 | {{/if}}
11 | {{/if}}
12 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/params-table.hbs:
--------------------------------------------------------------------------------
1 | {{#if params}}
2 | {{#if line}}
3 | {{#each params}}{{#unless @first}}, {{/unless}}{{{name}}}{{> type line=1}}{{/each}}
4 | {{else}}
5 | | Имя | Тип | * | Описание |
6 | | --- | --- | --- | --- |
7 | {{#each params}}
8 | | {{#if optional}}*\[{{/if}}{{{name}}}{{#if optional}}\]*{{/if}} | {{> type}} | {{{defaultvalue}}} | {{{description}}} |
9 | {{/each}}
10 | {{/if}}
11 | {{/if}}
12 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/class.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * @name {{name}}
3 | * @kind class
4 | * @classdesc {{{classdesc}}}
5 | *
6 | {{#each augments}}
7 | * @extends {{this}}
8 | {{/each}}
9 | *
10 | {{#each fires}}
11 | * @fires {{this}}
12 | {{/each}}
13 | *{{#if exported}} @alias {{{exported}}}
14 | *
15 | * @constructor
16 | * {{{description}}}
17 | {{> params-table}}
18 | *
19 | {{/if}}
20 | */
21 | {{> method}}
22 |
23 | {{> children children}}
24 |
--------------------------------------------------------------------------------
/src/lib/class/pure-instance.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Создаёт экземпляр класса, но не запускает его конструктор
3 | * @param {function} OriginalClass - класс
4 | * @exported ya.music.lib.pureInstance
5 | * @returns {OriginalClass}
6 | */
7 | var pureInstance = function(OriginalClass) {
8 | var PureClass = function() {};
9 | PureClass.prototype = OriginalClass.prototype;
10 | return new PureClass();
11 | };
12 |
13 | module.exports = pureInstance;
14 |
--------------------------------------------------------------------------------
/src/lib/async/reject.js:
--------------------------------------------------------------------------------
1 | var noop = require('../noop');
2 | var Promise = require('./promise');
3 |
4 | /**
5 | * Содание отклонённого обещания, которое не плюётся в консоль ошибкой
6 | * @param {Error} data - причина отклонения обещания
7 | * @returns {Promise}
8 | * @private
9 | */
10 | var reject = function(data) {
11 | var promise = Promise.reject(data);
12 | promise["catch"](noop);
13 | return promise;
14 | };
15 |
16 | module.exports = reject;
17 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/function.hbs:
--------------------------------------------------------------------------------
1 | {{#unless inherited}}
2 | /**
3 | * {{{description}}}
4 | * @function
5 | * @name {{name}}
6 | * {{#if exported}}@alias {{exported}}{{/if}}
7 | * {{#if parent}}@memberof {{parent.longname}}{{/if}}
8 | * {{#if inner}}@inner{{/if}}
9 | * {{#unless parent.namespace}}{{#if static}}@static{{/if}}{{/unless}}
10 | {{> params-table}}
11 | * {{#if returns.type.names}}@returns {{>type returns}} {{{returns.description}}}{{/if}}
12 | */
13 | {{/unless}}
14 |
--------------------------------------------------------------------------------
/spec/LoaderError.md:
--------------------------------------------------------------------------------
1 | ## *class* LoaderError
2 |
3 | **Доступен извне как:** `ya.music.Audio.LoaderError`
4 |
5 | Класс ошибок загрузчика.
6 | Расширяет Error.
7 |
8 | #### new LoaderError(message: String)
9 |
10 | | Имя | Тип | * | Описание |
11 | | --- | --- | --- | --- |
12 | | message | String | | Текст ошибки. |
13 |
14 | #### LoaderError.TIMEOUT : String
15 |
16 | Таймаут загрузки.
17 |
18 | #### LoaderError.FAILED : String
19 |
20 | Ошибка запроса на загрузку.
21 |
22 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/class.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * @class {{{classdesc}}}
3 | * @name {{{name}}}
4 | * {{#if parent}}@memberof {{parent.longname}}{{/if}}
5 | * {{#if static}}@static{{/if}}
6 | * {{#if inner}}@inner{{/if}}
7 | *
8 | {{#each augments}}
9 | * @extends {{this}}
10 | {{/each}}
11 | *
12 | {{#each fires}}
13 | * @fires {{this}}
14 | {{/each}}
15 | *
16 | {{#if exported}}
17 | * @alias {{{exported}}}
18 | *
19 | * @constructor
20 | * {{{description}}}
21 | {{> params-table}}
22 | * {{else}}
23 | * @noconstructor
24 | {{/if}}
25 | */
26 |
27 | {{> children children}}
28 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/class.hbs:
--------------------------------------------------------------------------------
1 | ##{{#unless exported}} *inner*{{/unless}} *class* {{{name}}}
2 |
3 | {{> exported}}
4 |
5 |
6 | {{{classdesc}}}
7 |
8 | {{#if augments}}
9 | **Расширяет:**
10 |
11 | {{#each augments}}
12 | - \{@link {{this}}\}
13 | {{/each}}
14 | {{/if}}
15 |
16 | {{#if fires}}
17 | **Триггерит:**
18 |
19 | {{#each fires}}
20 | - \{@link {{this}}\}
21 | {{/each}}
22 | {{/if}}
23 |
24 | {{#if exported}}
25 | #### new {{{name}}}({{> params-table line=1}})
26 |
27 | {{{description}}}
28 |
29 | {{> params-table}}
30 | {{/if}}
31 |
32 | {{> children}}
33 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/function.hbs:
--------------------------------------------------------------------------------
1 | #### {{#if parent}}
2 | {{> parents-line}}
3 | {{else exported}}
4 |
5 | {{else}}
6 | *inner*
7 | {{/if}}{{{name}}} ({{> params-table line=1}}) {{>type returns line=1}} {{#if inherited}}*(inherits {@link {{inherits}}\})*{{/if}}
8 |
9 | {{> exported}}
10 |
11 |
12 | {{{description}}}
13 |
14 |
15 | {{> params-table}}
16 |
17 |
18 | {{#if returns.description}}
19 | > **Возвращает:** {{{returns.description}}}
20 | {{/if}}
21 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/function.hbs:
--------------------------------------------------------------------------------
1 | #### {{#if parent}}
2 | {{> parents-line}}
3 | {{else exported}}
4 |
5 | {{else}}
6 | *inner*
7 | {{/if}}{{{name}}} ({{> params-table line=1}}) {{>type returns line=1}} {{#if inherited}}*(inherits {@link {{inherits}}\})*{{/if}}
8 |
9 | {{> exported}}
10 |
11 |
12 | {{{description}}}
13 |
14 |
15 | {{> params-table}}
16 |
17 |
18 | {{#if returns.description}}
19 | > **Возвращает:** {{{returns.description}}}
20 | {{/if}}
21 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/children.hbs:
--------------------------------------------------------------------------------
1 | {{#if member}}
2 | /*************************************************************/
3 |
4 | // Members
5 | {{#each member}}
6 |
7 | {{> member}}
8 |
9 | {{/each}}
10 | {{/if}}
11 |
12 |
13 | {{#if function}}
14 | /*************************************************************/
15 |
16 | // Functions
17 | {{#each function}}
18 |
19 | {{> function}}
20 |
21 | {{/each}}
22 | {{/if}}
23 |
24 |
25 | {{#if event}}
26 | /*************************************************************/
27 |
28 | // Events
29 | {{#each event}}
30 |
31 | {{> event}}
32 |
33 | {{/each}}
34 | {{/if}}
35 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/layout.hbs:
--------------------------------------------------------------------------------
1 | ## Внешняя структура модуля
2 |
3 | {{> export-tree exportTree}}
4 |
5 | {{#each linear.class}}
6 | ----
7 |
8 | {{> class}}
9 |
10 | {{/each}}
11 |
12 |
13 | {{#if linear.function}}
14 | ----
15 |
16 | ## Функции
17 | {{#each linear.function}}
18 |
19 | {{> function}}
20 | {{/each}}
21 | {{/if}}
22 |
23 |
24 | {{#if linear.typedef}}
25 | ----
26 |
27 | ## Типы
28 | {{#each linear.typedef}}
29 |
30 | {{> typedef}}
31 | {{/each}}
32 | {{/if}}
33 |
34 |
35 | ## Пространства имён
36 |
37 | {{#each tree.namespace}}
38 | ----
39 |
40 | {{> namespace}}
41 |
42 | {{/each}}
43 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/children.hbs:
--------------------------------------------------------------------------------
1 | {{#if member}}
2 | /*************************************************************/
3 |
4 | // Members
5 | {{#each member}}
6 |
7 | {{> member}}
8 |
9 | {{/each}}
10 | {{/if}}
11 |
12 |
13 | {{#if function}}
14 | /*************************************************************/
15 |
16 | // Functions
17 | {{#each function}}
18 |
19 | {{> function}}
20 |
21 | {{/each}}
22 | {{/if}}
23 |
24 |
25 | {{#if event}}
26 | /*************************************************************/
27 |
28 | // Events
29 | {{#each event}}
30 |
31 | {{> event}}
32 |
33 | {{/each}}
34 | {{/if}}
35 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-single/class.hbs:
--------------------------------------------------------------------------------
1 | ##{{#unless exported}} *inner*{{/unless}} *class* {{{name}}}
2 |
3 | {{> exported}}
4 |
5 |
6 | {{{classdesc}}}
7 |
8 |
9 | {{#if augments}}
10 | **Расширяет:**
11 |
12 | {{#each augments}}
13 | - \{@link {{this}}\}
14 | {{/each}}
15 | {{/if}}
16 |
17 |
18 | {{#if fires}}
19 | **Триггерит:**
20 |
21 | {{#each fires}}
22 | - \{@link {{this}}\}
23 | {{/each}}
24 | {{/if}}
25 |
26 |
27 | {{#if exported}}
28 | #### new {{{name}}}({{> params-table line=1}})
29 |
30 |
31 | {{{description}}}
32 |
33 |
34 | {{> params-table}}
35 | {{/if}}
36 |
37 |
38 | {{> children}}
39 |
--------------------------------------------------------------------------------
/src/export.js:
--------------------------------------------------------------------------------
1 | if (typeof DEV === "undefined") {
2 | window.DEV = true;
3 | }
4 |
5 | if (typeof window.ya === "undefined") {
6 | window.ya = {};
7 | }
8 |
9 | var ya = window.ya;
10 |
11 | if (typeof ya.music === "undefined") {
12 | ya.music = {};
13 | }
14 |
15 | if (typeof ya.music.Audio === "undefined") {
16 | ya.music.Audio = {};
17 | }
18 |
19 | var config = require('./config');
20 | var AudioPlayer = require('./audio-player');
21 | var Proxy = require('./lib/class/proxy');
22 |
23 | ya.music.Audio = Proxy.createClass(AudioPlayer);
24 | ya.music.Audio.config = config;
25 |
26 | require('./lib/export');
27 |
28 | module.exports = ya.music.Audio;
29 |
--------------------------------------------------------------------------------
/spec/readme.md:
--------------------------------------------------------------------------------
1 | # Внешняя структура модуля
2 |
3 | * ya.music
4 | * [Audio](Audio.md#Audio)
5 | * [config](config.md#config)
6 | * [AudioError](AudioError.md#AudioError)
7 | * [PlaybackError](PlaybackError.md#PlaybackError)
8 | * fx
9 | * [Equalizer](Equalizer.md#Equalizer)
10 | * [volumeLib](volumeLib.md#volumeLib)
11 | * [LoaderError](LoaderError.md#LoaderError)
12 | * lib
13 | * [Deferred](Deferred.md#Deferred)
14 | * [Events](Events.md#Events)
15 | * [Promise](Promise.md#Promise)
16 | * [Error](ErrorClass.md#ErrorClass)
17 | * [pureInstance](global.md#pureInstance)
18 | * [merge](global.md#merge)
19 | * [info](info.md#info)
20 | * [Logger](Logger.md#Logger)
21 |
--------------------------------------------------------------------------------
/src/lib/net/error/loader-error.js:
--------------------------------------------------------------------------------
1 | var ErrorClass = require('../../class/error-class');
2 |
3 | /**
4 | * @exported ya.music.Audio.LoaderError
5 | * @classdesc Класс ошибок загрузчика.
6 | * Расширяет Error.
7 | * @param {String} message Текст ошибки.
8 | *
9 | * @constructor
10 | */
11 | var LoaderError = function(message) {
12 | ErrorClass.call(this, message);
13 | };
14 | LoaderError.prototype = ErrorClass.create("LoaderError");
15 |
16 | /**
17 | * Таймаут загрузки.
18 | * @type {String}
19 | * @const
20 | */
21 | LoaderError.TIMEOUT = "request timeout";
22 | /**
23 | * Ошибка запроса на загрузку.
24 | * @type {String}
25 | * @const
26 | */
27 | LoaderError.FAILED = "request failed";
28 |
29 | module.exports = LoaderError;
30 |
--------------------------------------------------------------------------------
/jsdoc/doc/gfm-files/children.hbs:
--------------------------------------------------------------------------------
1 | {{#each children.member}}
2 |
3 | {{> member}}
4 |
5 | {{/each}}
6 |
7 |
8 | {{#if children.event}}
9 | ----
10 |
11 | ### События
12 | {{#each children.event}}
13 |
14 | {{> event}}
15 |
16 | {{/each}}
17 | {{/if}}
18 |
19 | {{#if children.function}}
20 | ----
21 |
22 | ### Методы
23 | {{#each children.function}}
24 |
25 | {{> function}}
26 |
27 | {{/each}}
28 | {{/if}}
29 |
30 |
31 | {{#if children.namespace}}
32 | ----
33 |
34 | ### Пространства имён
35 | {{#each children.namespace}}
36 |
37 |
38 | {{> namespace}}
39 |
40 | {{/each}}
41 | {{/if}}
42 |
43 |
44 | {{#if children.typedef}}
45 | ----
46 |
47 | ### Типы
48 | {{#each children.typedef}}
49 |
50 | {{> typedef}}
51 |
52 | {{/each}}
53 | {{/if}}
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "YandexAudio",
3 | "version": "0.0.1",
4 | "description": "Audio-player library for browser",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/yandex/music-audio.git"
12 | },
13 | "keywords": [
14 | "audio",
15 | "html5",
16 | "flash"
17 | ],
18 | "author": "Silaev Mikhail ",
19 | "license": "GNU LGPL v3.0",
20 | "devDependencies": {
21 | "browserify": "^11.0.1",
22 | "handlebars": "^4.0.5",
23 | "jsdoc": "^3.3.2",
24 | "uglify-js": "^2.4.24"
25 | },
26 | "dependencies": {
27 | "vow": "^0.4.10"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/spec/Deferred.md:
--------------------------------------------------------------------------------
1 | ## *class* Deferred
2 |
3 | **Доступен извне как:** `ya.music.lib.Deferred`
4 |
5 | Класс для управления обещанием
6 |
7 | #### new Deferred()
8 |
9 | ----
10 |
11 | ### Методы
12 |
13 | #### Deferred#resolve (data)
14 |
15 | Разрешить обещание
16 |
17 | | Имя | Тип | * | Описание |
18 | | --- | --- | --- | --- |
19 | | data | | | передать данные в обещание |
20 |
21 | #### Deferred#reject (error)
22 |
23 | Отклонить обещание
24 |
25 | | Имя | Тип | * | Описание |
26 | | --- | --- | --- | --- |
27 | | error | | | передать ошибку |
28 |
29 | #### Deferred#promise () : [Promise](Promise.md#Promise)
30 |
31 | Получить обещание
32 |
33 |
--------------------------------------------------------------------------------
/examples/quick-start/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/spec/ErrorClass.md:
--------------------------------------------------------------------------------
1 | ## *class* ErrorClass
2 |
3 | **Доступен извне как:** `ya.music.lib.Error`
4 |
5 | Класс ошибки. Оригинальный Error ведёт себя как фабрика, а не как класс. Этот объект ведёт себя как класс и его можно наследовать.
6 |
7 | **Расширяет:**
8 |
9 | - Error
10 |
11 | #### new ErrorClass(message: String, id: Number)
12 |
13 | | Имя | Тип | * | Описание |
14 | | --- | --- | --- | --- |
15 | | *\[message\]* | String | | сообщение |
16 | | *\[id\]* | Number | | идентификатор ошибки |
17 |
18 | ----
19 |
20 | ### Методы
21 |
22 | #### ErrorClass.create (name: String) : [ErrorClass](ErrorClass.md#ErrorClass)
23 |
24 | Сахар для быстрого создания нового класса ошибок.
25 |
26 | | Имя | Тип | * | Описание |
27 | | --- | --- | --- | --- |
28 | | name | String | | имя создаваемого класса |
29 |
30 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/layout.hbs:
--------------------------------------------------------------------------------
1 | {{#if linear.class}}
2 | /*************************************************************/
3 |
4 | // Classes
5 |
6 | {{#each linear.class}}
7 | //-----------------------------------------------------------//
8 |
9 | {{> class}}
10 | {{/each}}
11 | {{/if}}
12 |
13 |
14 | {{#if linear.typedef}}
15 | /*************************************************************/
16 |
17 | // Typedefs
18 | {{#each linear.typedef}}
19 | //-----------------------------------------------------------//
20 |
21 | {{> typedef}}
22 | {{/each}}
23 | {{/if}}
24 |
25 |
26 | {{#if linear.namespace}}
27 | /*************************************************************/
28 |
29 | // Namespaces
30 | {{#each linear.namespace}}
31 | //-----------------------------------------------------------//
32 |
33 | {{> namespace}}
34 | {{/each}}
35 | {{/if}}
36 |
37 | {{> children linear}}
38 |
--------------------------------------------------------------------------------
/spec/global.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | #### pureInstance (OriginalClass: function) : OriginalClass
4 |
5 | **Доступен извне как:** `ya.music.lib.pureInstance`
6 |
7 | Создаёт экземпляр класса, но не запускает его конструктор
8 |
9 | | Имя | Тип | * | Описание |
10 | | --- | --- | --- | --- |
11 | | OriginalClass | function | | класс |
12 |
13 |
14 |
15 |
16 |
17 | #### merge (initial: Object, ...args: Object, extend: Boolean) : Object
18 |
19 | **Доступен извне как:** `ya.music.lib.merge`
20 |
21 | Объединение объектов
22 |
23 | | Имя | Тип | * | Описание |
24 | | --- | --- | --- | --- |
25 | | initial | Object | | начальный объект |
26 | | ...args | Object | | список объектов которые надо объединить с начальным |
27 | | *\[extend\]* | Boolean | | если последний аргумент true, то будет модифицирован начальный объект, в противном случае будет создана неглубокая копия. |
28 |
29 |
--------------------------------------------------------------------------------
/spec/info.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## *ns* info
4 |
5 | **Доступен извне как:** `ya.music.info`
6 |
7 | Информация об окружении
8 |
9 | #### info.onlyDeviceVolume : boolean
10 |
11 | Настройка громкости
12 |
13 | ----
14 |
15 | ### Пространства имён
16 |
17 | ## *ns* info.browser
18 |
19 | Информация о браузере
20 |
21 | | Имя | Тип | * | Описание |
22 | | --- | --- | --- | --- |
23 | | name | string | | название браузера |
24 | | version | string | | версия |
25 | | *\[documentMode\]* | number | | версия документа (для IE) |
26 |
27 | ## *ns* info.platform
28 |
29 | Информация о платформе
30 |
31 | | Имя | Тип | * | Описание |
32 | | --- | --- | --- | --- |
33 | | os | string | | тип операционной системы |
34 | | type | string | | тип платформы |
35 | | tablet | boolean | | планшет |
36 | | mobile | boolean | | мобильный |
37 |
38 |
--------------------------------------------------------------------------------
/test/web-audio-api/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc/layout.hbs:
--------------------------------------------------------------------------------
1 | ({
2 | {{> export-tree exportTree}}
3 |
4 | {{#if linear.class}}
5 | /*************************************************************/
6 |
7 | // Classes
8 |
9 | {{#each linear.class}}
10 | //-----------------------------------------------------------//
11 |
12 | {{> class}}
13 | {{/each}}
14 | {{/if}}
15 |
16 |
17 | {{#if linear.typedef}}
18 | /*************************************************************/
19 |
20 | // Typedefs
21 | {{#each linear.typedef}}
22 | //-----------------------------------------------------------//
23 |
24 | {{> typedef}}
25 | {{/each}}
26 | {{/if}}
27 |
28 |
29 | {{#if linear.namespace}}
30 | /*************************************************************/
31 |
32 | // Namespaces
33 | {{#each linear.namespace}}
34 | //-----------------------------------------------------------//
35 |
36 | {{> namespace}}
37 | {{/each}}
38 | {{/if}}
39 |
40 | {{> children linear}}
41 | })
42 |
--------------------------------------------------------------------------------
/spec/PlaybackError.md:
--------------------------------------------------------------------------------
1 | ## *class* PlaybackError
2 |
3 | **Доступен извне как:** `ya.music.Audio.PlaybackError`
4 |
5 | Класс ошибки воспроизведения.
6 |
7 | **Расширяет:**
8 |
9 | - Error
10 |
11 | #### new PlaybackError(message: String, src: String)
12 |
13 | | Имя | Тип | * | Описание |
14 | | --- | --- | --- | --- |
15 | | message | String | | Текст ошибки. |
16 | | src | String | | Ссылка на трек. |
17 |
18 | #### PlaybackError.CONNECTION_ABORTED : String
19 |
20 | Отмена соединенния.
21 |
22 | #### PlaybackError.NETWORK_ERROR : String
23 |
24 | Сетевая ошибка.
25 |
26 | #### PlaybackError.DECODE_ERROR : String
27 |
28 | Ошибка декодирования аудио.
29 |
30 | #### PlaybackError.BAD_DATA : String
31 |
32 | Недоступный источник.
33 |
34 | #### PlaybackError.DONT_START : String
35 |
36 | Не запускается воспроизведение.
37 |
38 | #### PlaybackError.html5 : Object
39 |
40 | Таблица соответствия кодов ошибок HTML5 плеера.
41 |
42 |
--------------------------------------------------------------------------------
/src/lib/export.js:
--------------------------------------------------------------------------------
1 | require('../export');
2 |
3 | if (!ya.music.lib) {
4 | ya.music.lib = {};
5 | }
6 |
7 | var Events = require('./async/events');
8 | var ErrorClass = require('./class/error-class');
9 | var pureInstance = require('./class/pure-instance');
10 |
11 | var EventsProxy = function() { Events.call(this); };
12 | EventsProxy.prototype = pureInstance(Events);
13 | EventsProxy.mixin = Events.mixin;
14 | EventsProxy.eventize = Events.eventize;
15 |
16 | var ErrorClassProxy = function() { ErrorClass.apply(this, arguments); };
17 | ErrorClassProxy.prototype = pureInstance(ErrorClass);
18 | ErrorClassProxy.create = ErrorClass.create;
19 |
20 | ya.music.lib.Events = EventsProxy;
21 | ya.music.lib.Error = ErrorClassProxy;
22 |
23 | ya.music.lib.Promise = require('./async/promise');
24 | ya.music.lib.Deferred = require('./async/deferred');
25 |
26 | ya.music.lib.pureInstance = pureInstance;
27 | ya.music.lib.merge = require('./data/merge');
28 |
29 | ya.music.info = require('./browser/detect');
30 |
--------------------------------------------------------------------------------
/src/flash/src/AudioEvent.as:
--------------------------------------------------------------------------------
1 | package {
2 | import flash.events.DataEvent;
3 |
4 | public final class AudioEvent extends DataEvent {
5 | public var offset:int;
6 |
7 | public static const EVENT_PLAY:String = "play";
8 | public static const EVENT_PAUSE:String = "pause";
9 | public static const EVENT_PROGRESS:String = "progress";
10 | public static const EVENT_ENDED:String = "ended";
11 | public static const EVENT_ERROR:String = "error";
12 | public static const EVENT_VOLUME:String = "volumechange";
13 | public static const EVENT_STOP:String = "stop";
14 | public static const EVENT_LOADED:String = "loaded";
15 | public static const EVENT_LOADING:String = "loading";
16 | public static const EVENT_SWAP:String = "swap";
17 |
18 | public static const EVENT_DEBUG:String = "debug";
19 |
20 | public function AudioEvent(type:String, offset:int = -1, data:String = "") {
21 | super(type, false, false, data);
22 | this.offset = offset;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/lib/async/deferred.js:
--------------------------------------------------------------------------------
1 | var Promise = require('./promise');
2 | var noop = require('../noop');
3 |
4 | /**
5 | * @classdesc Класс для управления обещанием
6 | * @exported ya.music.lib.Deferred
7 | * @constructor
8 | */
9 | var Deferred = function() {
10 | var self = this;
11 |
12 | var promise = new Promise(function(resolve, reject) {
13 | /**
14 | * Разрешить обещание
15 | * @method Deferred#resolve
16 | * @param data - передать данные в обещание
17 | */
18 | self.resolve = resolve;
19 |
20 | /**
21 | * Отклонить обещание
22 | * @method Deferred#reject
23 | * @param error - передать ошибку
24 | */
25 | self.reject = reject;
26 | });
27 |
28 | promise["catch"](noop); // Don't throw errors to console
29 |
30 | /**
31 | * Получить обещание
32 | * @method Deferred#promise
33 | * @returns {Promise}
34 | */
35 | this.promise = function() { return promise; };
36 | };
37 |
38 | module.exports = Deferred;
39 |
--------------------------------------------------------------------------------
/src/lib/class/error-class.js:
--------------------------------------------------------------------------------
1 | var pureInstance = require('./pure-instance');
2 |
3 | /**
4 | * @classdesc Класс ошибки. Оригинальный Error ведёт себя как фабрика, а не как класс. Этот объект ведёт себя как класс и его можно наследовать.
5 | * @param {String} [message] - сообщение
6 | * @param {Number} [id] - идентификатор ошибки
7 | * @extends Error
8 | * @exported ya.music.lib.Error
9 | * @constructor
10 | */
11 | var ErrorClass = function(message, id) {
12 | var err = new Error(message, id);
13 | err.name = this.name;
14 |
15 | this.message = err.message;
16 | this.stack = err.stack;
17 | };
18 |
19 | /**
20 | * Сахар для быстрого создания нового класса ошибок.
21 | * @param {String} name - имя создаваемого класса
22 | * @returns {ErrorClass}
23 | */
24 | ErrorClass.create = function(name) {
25 | var errClass = pureInstance(this);
26 | errClass.name = name;
27 | return errClass;
28 | };
29 |
30 | ErrorClass.prototype = pureInstance(Error);
31 | ErrorClass.prototype.name = "ErrorClass";
32 |
33 | module.exports = ErrorClass;
34 |
--------------------------------------------------------------------------------
/jsdoc/doc/jsdoc-tech/namespace.hbs:
--------------------------------------------------------------------------------
1 | {{#if parent.namespace}}
2 | /**
3 | * {{{description}}}
4 | *
5 | * ПараметрЗначение по умолчаниюОписание
6 | {{#each children.member}}
7 | * {{{name}}}{{>type nowrap=1}}{{{description}}}
8 | {{/each}}{{#each properties}}
9 | * {{{name}}}{{>type nowrap=1}}{{{description}}}
10 | {{/each}}
11 | *
12 | * @name {{{longname}}}
13 | * @type Object
14 | */
15 |
16 |
17 | {{else}}
18 | /**
19 | * @namespace {{{description}}}
20 | * @name {{{longname}}}
21 | * {{#if exported}}@alias {{{exported}}}{{/if}}
22 | * {{#if inner}}@inner{{/if}}
23 | * {{#if static}}@static{{/if}}
24 | */
25 |
26 | {{> children children}}
27 | {{/if}}
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/data/merge.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Объединение объектов
3 | * @param {Object} initial - начальный объект
4 | * @param {Object} ...args - список объектов которые надо объединить с начальным
5 | * @param {Boolean} [extend] - если последний аргумент true, то будет модифицирован начальный объект, в противном случае будет создана неглубокая копия.
6 | * @exported ya.music.lib.merge
7 | * @returns {Object}
8 | */
9 | var merge = function(initial) {
10 | var args = [].slice.call(arguments, 1);
11 | var object;
12 | var key;
13 |
14 | if (args[args.length - 1] === true) {
15 | object = initial;
16 | args.pop();
17 | } else {
18 | object = {};
19 | for (key in initial) {
20 | if (initial.hasOwnProperty(key)) {
21 | object[key] = initial[key];
22 | }
23 | }
24 | }
25 |
26 | for (var k = 0, l = args.length; k < l; k++) {
27 | for (key in args[k]) {
28 | if (args[k].hasOwnProperty(key)) {
29 | object[key] = args[k][key];
30 | }
31 | }
32 | }
33 |
34 | return object;
35 | };
36 |
37 | module.exports = merge;
38 |
--------------------------------------------------------------------------------
/jsdoc/jsdoc.public.json:
--------------------------------------------------------------------------------
1 | {
2 | "tags": {
3 | "allowUnknownTags": true
4 | },
5 | "source": {
6 | "include": [
7 | "src/"
8 | ],
9 | "exclude": [],
10 | "includePattern": ".+\\.(js)(doc)?$"
11 | },
12 | "plugins": ["plugins/markdown"],
13 | "markdown": {
14 | "parser": "gfm",
15 | "hardwrap": false
16 | },
17 |
18 | "templates": {
19 | "applicationName": "YandexAudio",
20 | "disqus": "",
21 | "googleAnalytics": "",
22 | "openGraph": {
23 | "title": "",
24 | "type": "website",
25 | "image": "",
26 | "site_name": "",
27 | "url": ""
28 | },
29 | "meta": {
30 | "title": "",
31 | "description": "",
32 | "keyword": ""
33 | },
34 | "linenums": true
35 | },
36 |
37 | "opts": {
38 | "template": "jsdoc/doc",
39 | "recurse": true,
40 | "encoding": "utf8",
41 | "destination": "spec/",
42 | "private": false,
43 | "lenient": true,
44 | "readme": "readme.md",
45 | "tutorials": "tutorial/"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/spec/Promise.md:
--------------------------------------------------------------------------------
1 | ## *class* Promise
2 |
3 | **Доступен извне как:** `ya.music.lib.Promise`
4 |
5 | Обещание по спецификации [ES 2015 promises](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise). В устаревших браузерах и IE используется замена из библиотеки [vow](http://github.com/dfilatov/vow.git)
6 |
7 | #### new Promise()
8 |
9 | ----
10 |
11 | ### Методы
12 |
13 | #### Promise#then (callback: function, errback: null \| function) : [Promise](Promise.md#Promise)
14 |
15 | Назначить обработчики разрешения и отклонения обещания.
16 |
17 | | Имя | Тип | * | Описание |
18 | | --- | --- | --- | --- |
19 | | callback | function | | Обработчик успеха. |
20 | | *\[errback\]* | null \| function | | Обработчик ошибки. |
21 |
22 | > **Возвращает:** новое обещание из результатов обработчика.
23 |
24 | #### Promise#catch (errback: function) : [Promise](Promise.md#Promise)
25 |
26 | Назначить обработчик отклонения обещания.
27 |
28 | | Имя | Тип | * | Описание |
29 | | --- | --- | --- | --- |
30 | | errback | function | | Обработчик ошибки. |
31 |
32 | > **Возвращает:** новое обещание из результатов обработчика.
33 |
34 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | NPM_BIN=./node_modules/.bin
2 |
3 | SOURCEDIR=src
4 | BUILDDIR=dist
5 |
6 | JSDOC=$(NPM_BIN)/jsdoc -c
7 | UGLIFY_JS=$(NPM_BIN)/uglifyjs --mangle --compress --bare-returns --stats
8 | BROWSERIFY=$(NPM_BIN)/browserify -d
9 |
10 |
11 | all: clean build minify jsdoc
12 | git add -A
13 | git commit
14 |
15 |
16 | clean:
17 | -rm -rf $(BUILDDIR)
18 |
19 |
20 | npm:
21 | -npm install
22 |
23 |
24 | prepare: clean npm
25 | mkdir $(BUILDDIR)
26 | cp $(SOURCEDIR)/flash/build/*.swf $(BUILDDIR)/
27 |
28 |
29 | build: $(BUILDDIR)/index.js
30 |
31 |
32 | minify: $(BUILDDIR)/index.min.js
33 |
34 |
35 | $(BUILDDIR)/index.js: prepare
36 | $(BROWSERIFY) $(SOURCEDIR)/index.js > $(BUILDDIR)/index.js
37 |
38 |
39 | $(BUILDDIR)/index.min.js: $(BUILDDIR)/index.js
40 | $(UGLIFY_JS) $(BUILDDIR)/index.js > $(BUILDDIR)/index.min.js --source-map $(BUILDDIR)/index.map.json
41 |
42 |
43 | jsdoc: npm
44 | -rm -rf spec/*
45 | -mkdir spec
46 | $(JSDOC) jsdoc/jsdoc.public.json -q "style=gfm-files"
47 | $(JSDOC) jsdoc/jsdoc.public.json -q "style=gfm-single&out=full"
48 | $(JSDOC) jsdoc/jsdoc.public.json -d $(BUILDDIR)/ -q "style=jsdoc&out=audio"
49 | $(JSDOC) jsdoc/jsdoc.public.json -q "style=jsdoc-tech&out=tech"
50 |
51 |
52 | .PHONY: all clean build minify prepare npm jsdoc
53 |
--------------------------------------------------------------------------------
/spec/AudioError.md:
--------------------------------------------------------------------------------
1 | ## *class* AudioError
2 |
3 | **Доступен извне как:** `ya.music.Audio.AudioError`
4 |
5 | Класс ошибки аудиопллеера.
6 |
7 | **Расширяет:**
8 |
9 | - Error
10 |
11 | #### new AudioError(message: String)
12 |
13 | | Имя | Тип | * | Описание |
14 | | --- | --- | --- | --- |
15 | | message | String | | Текст ошибки. |
16 |
17 | #### AudioError.NO_IMPLEMENTATION : String
18 |
19 | Не найдена реализация плеера или возникла ошибка при инициализации всех доступных реализаций.
20 |
21 | #### AudioError.NOT_PRELOADED : String
22 |
23 | Аудиофайл не был предзагружен или во время загрузки произошла ошибка.
24 |
25 | #### AudioError.BAD_STATE : String
26 |
27 | Действие недоступно из текущего состояния.
28 |
29 | #### AudioError.FLASH_BLOCKER : String
30 |
31 | Flash-плеер был заблокирован.
32 |
33 | #### AudioError.FLASH_UNKNOWN_CRASH : String
34 |
35 | Возникла ошибка при инициализации Flash-плеера по неизвестным причинам.
36 |
37 | #### AudioError.FLASH_INIT_TIMEOUT : String
38 |
39 | Возникла ошибка при инициализации Flash-плеера из-за таймаута.
40 |
41 | #### AudioError.FLASH_INTERNAL_ERROR : String
42 |
43 | Внутренняя ошибка Flash-плеера.
44 |
45 | #### AudioError.FLASH_EMMITER_NOT_FOUND : String
46 |
47 | Попытка вызвать недоступный экземляр Flash-плеера.
48 |
49 | #### AudioError.FLASH_NOT_RESPONDING : String
50 |
51 | Flash-плеер перестал отвечать на запросы.
52 |
53 |
--------------------------------------------------------------------------------
/spec/Logger.md:
--------------------------------------------------------------------------------
1 | ## *class* Logger
2 |
3 | **Доступен извне как:** `ya.music.Logger`
4 |
5 | Настраиваемый логгер для аудиоплеера.
6 |
7 | #### new Logger(channel: String)
8 |
9 | | Имя | Тип | * | Описание |
10 | | --- | --- | --- | --- |
11 | | channel | String | | Имя канала, за который будет отвечать экземляр логгера. |
12 |
13 | #### Logger.ignores : Array.< String >
14 |
15 | Список игнорируемых каналов.
16 |
17 | #### Logger.logLevels : Array.< String >
18 |
19 | Список отображаемых в консоли уровней лога.
20 |
21 | #### Logger#debug
22 |
23 | Запись в лог с уровнем debug.
24 |
25 | #### Logger#log
26 |
27 | Запись в лог с уровнем log.
28 |
29 | #### Logger#info
30 |
31 | Запись в лог с уровнем info.
32 |
33 | #### Logger#warn
34 |
35 | Запись в лог с уровнем warn.
36 |
37 | #### Logger#error
38 |
39 | Запись в лог с уровнем error.
40 |
41 | #### Logger#trace
42 |
43 | Запись в лог с уровнем trace.
44 |
45 | ----
46 |
47 | ### Методы
48 |
49 | #### Logger.log (level: String, channel: String, context: Object, args: *)
50 |
51 | Сделать запись в лог.
52 |
53 | | Имя | Тип | * | Описание |
54 | | --- | --- | --- | --- |
55 | | level | String | | Уровень лога. |
56 | | channel | String | | Канал. |
57 | | context | Object | | Контекст вызова. |
58 | | *\[args\]* | * | | Дополнительные аргументы. |
59 |
60 |
--------------------------------------------------------------------------------
/spec/AbortablePromise.md:
--------------------------------------------------------------------------------
1 | ## *inner* *class* AbortablePromise
2 |
3 | Обещание с возможностью отмены связанного с ним действия.
4 |
5 | **Расширяет:**
6 |
7 | - [Promise](Promise.md#Promise)
8 |
9 | ----
10 |
11 | ### Методы
12 |
13 | #### AbortablePromise#abort (reason: String \| Error)
14 |
15 | Отмена действия, связанного с обещанием. Абстрактный метод.
16 |
17 | | Имя | Тип | * | Описание |
18 | | --- | --- | --- | --- |
19 | | reason | String \| Error | | Причина отмены действия. |
20 |
21 | #### AbortablePromise#then (callback: function, errback: null \| function) : [Promise](Promise.md#Promise) *(inherits [Promise#then](Promise.md#Promise..then))*
22 |
23 | Назначить обработчики разрешения и отклонения обещания.
24 |
25 | | Имя | Тип | * | Описание |
26 | | --- | --- | --- | --- |
27 | | callback | function | | Обработчик успеха. |
28 | | *\[errback\]* | null \| function | | Обработчик ошибки. |
29 |
30 | > **Возвращает:** новое обещание из результатов обработчика.
31 |
32 | #### AbortablePromise#catch (errback: function) : [Promise](Promise.md#Promise) *(inherits [Promise#catch](Promise.md#Promise..catch))*
33 |
34 | Назначить обработчик отклонения обещания.
35 |
36 | | Имя | Тип | * | Описание |
37 | | --- | --- | --- | --- |
38 | | errback | function | | Обработчик ошибки. |
39 |
40 | > **Возвращает:** новое обещание из результатов обработчика.
41 |
42 |
--------------------------------------------------------------------------------
/jsdoc/doc/render.gfm.js:
--------------------------------------------------------------------------------
1 | var cleanupTags = /<\/?(ul|li|p)>/g;
2 | var unescape = /\\(\{|\})/g;
3 | var beautify = /(\n[\t ]*){3,}/g;
4 |
5 | var trim = /^\s*|\s*$/g;
6 | var link = /\{@link (.*?) *\}/g;
7 | var linkhref = /\{@linkhref (.*?) ([^\}]*) *\}/g;
8 | var ltgt = /<(.*?)>/;
9 |
10 | module.exports = function(page, data) {
11 | page = page.replace(cleanupTags, "")
12 | .replace(unescape, "$1")
13 | .replace(/\r/g, "").replace(beautify, "\n\n");
14 |
15 | page = page.replace(link, function(_, link) {
16 | if (ltgt.test(link)) {
17 | return link.replace(ltgt, function(_, parts) {
18 | return "< " + parts.split(",").map(function(types) {
19 | return types.split("|")
20 | .map(function(type) { return "{@link " + type.replace(trim, "") + "}" })
21 | .join(" \\| ");
22 | }).join(", ") + " >";
23 | });
24 | }
25 |
26 | return "{@link " + link + "}";
27 | });
28 |
29 | page = page.replace(link, function(_, linkData) {
30 | linkData = linkData.split(" ");
31 | var linkPath = linkData.shift();
32 | var linkName = linkData.shift() || linkPath;
33 |
34 | if (data.links[linkPath]) {
35 | return "[" + linkName + "](" + data.links[linkPath] + ")";
36 | } else {
37 | return linkName;
38 | }
39 | });
40 |
41 | page = page.replace(linkhref, "[$2]($1)");
42 |
43 | return page;
44 | };
45 |
--------------------------------------------------------------------------------
/spec/volumeLib.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## *ns* volumeLib
4 |
5 | **Доступен извне как:** `ya.music.Audio.fx.volumeLib`
6 |
7 | Методы конвертации значений громкости.
8 |
9 | #### volumeLib.EPSILON : number
10 |
11 | Минимальное значение громкости, при котором происходит отключение звука. Ограничение в 0.01 подобрано эмпирически.
12 |
13 | ----
14 |
15 | ### Методы
16 |
17 | #### volumeLib.toExponent (value: Number) : Number
18 |
19 | Вычисление значение относительной громкости по значению на логарифмической шкале.
20 |
21 | | Имя | Тип | * | Описание |
22 | | --- | --- | --- | --- |
23 | | value | Number | | Значение на шкале. |
24 |
25 | #### volumeLib.fromExponent (volume: Number) : Number
26 |
27 | Вычисление положения на логарифмической шкале по значению относительной громкости громкости
28 |
29 | | Имя | Тип | * | Описание |
30 | | --- | --- | --- | --- |
31 | | volume | Number | | Громкость. |
32 |
33 | #### volumeLib.toDBFS (volume: Number) : Number
34 |
35 | Вычисление значения dBFS из относительного значения громкости.
36 |
37 | | Имя | Тип | * | Описание |
38 | | --- | --- | --- | --- |
39 | | volume | Number | | Относительная громкость. |
40 |
41 | #### volumeLib.fromDBFS (dbfs: Number) : Number
42 |
43 | Вычисление значения относительной громкости из значения dBFS.
44 |
45 | | Имя | Тип | * | Описание |
46 | | --- | --- | --- | --- |
47 | | dbfs | Number | | Громкость в dBFS. |
48 |
49 |
--------------------------------------------------------------------------------
/src/error/playback-error.js:
--------------------------------------------------------------------------------
1 | var ErrorClass = require('../lib/class/error-class');
2 |
3 | /**
4 | * @exported ya.music.Audio.PlaybackError
5 | * @classdesc Класс ошибки воспроизведения.
6 | * @extends Error
7 | * @param {String} message Текст ошибки.
8 | * @param {String} src Ссылка на трек.
9 | *
10 | * @constructor
11 | */
12 | var PlaybackError = function(message, src) {
13 | ErrorClass.call(this, message);
14 |
15 | this.src = src;
16 | };
17 |
18 | PlaybackError.prototype = ErrorClass.create("PlaybackError");
19 |
20 | /**
21 | * Отмена соединения.
22 | * @type {String}
23 | * @const
24 | */
25 | PlaybackError.CONNECTION_ABORTED = "Connection aborted";
26 | /**
27 | * Сетевая ошибка.
28 | * @type {String}
29 | * @const
30 | */
31 | PlaybackError.NETWORK_ERROR = "Network error";
32 | /**
33 | * Ошибка декодирования аудио.
34 | * @type {String}
35 | * @const
36 | */
37 | PlaybackError.DECODE_ERROR = "Decode error";
38 | /**
39 | * Недоступный источник.
40 | * @type {String}
41 | * @const
42 | */
43 | PlaybackError.BAD_DATA = "Bad data";
44 |
45 | /**
46 | * Не запускается воспроизведение.
47 | * @type {String}
48 | * @const
49 | */
50 | PlaybackError.DONT_START = "Playback start error";
51 |
52 | /**
53 | * Таблица соответствия кодов ошибок HTML5-плеера.
54 | *
55 | * @const
56 | * @type {Object}
57 | */
58 | PlaybackError.html5 = {
59 | 1: PlaybackError.CONNECTION_ABORTED,
60 | 2: PlaybackError.NETWORK_ERROR,
61 | 3: PlaybackError.DECODE_ERROR,
62 | 4: PlaybackError.BAD_DATA
63 | };
64 |
65 | //TODO: сделать классификатор ошибок Flash-плеера
66 |
67 | module.exports = PlaybackError;
68 |
--------------------------------------------------------------------------------
/spec/config.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## *ns* config
4 |
5 | **Доступен извне как:** `ya.music.Audio.config`
6 |
7 | Настройки библиотеки.
8 |
9 | ----
10 |
11 | ### Пространства имён
12 |
13 | ## *ns* config.audio
14 |
15 | Общие настройки.
16 |
17 | #### config.audio.retry : Number
18 |
19 | Количество попыток реинициализации
20 |
21 | ## *ns* config.flash
22 |
23 | Настройки подключения Flash-плеера.
24 |
25 | #### config.flash.path : String
26 |
27 | Путь к .swf файлу флеш-плеера
28 |
29 | #### config.flash.name : String
30 |
31 | Имя .swf файла флеш-плеера
32 |
33 | #### config.flash.version : String
34 |
35 | Минимальная версия флеш-плеера
36 |
37 | #### config.flash.playerID : String
38 |
39 | ID, который будет выставлен для элемента с Flash-плеером
40 |
41 | #### config.flash.callback : String
42 |
43 | Имя функции-обработчика событий Flash-плеера
44 |
45 | #### config.flash.initTimeout : Number
46 |
47 | Таймаут инициализации
48 |
49 | #### config.flash.loadTimeout : Number
50 |
51 | Таймаут загрузки
52 |
53 | #### config.flash.clickTimeout : Number
54 |
55 | Таймаут инициализации после клика
56 |
57 | #### config.flash.heartBeatInterval : Number
58 |
59 | Интервал проверки доступности Flash-плеера
60 |
61 | ## *ns* config.html5
62 |
63 | Описание настроек HTML5 плеера.
64 |
65 | #### config.html5.blacklist : Array.< String >
66 |
67 | Список идентификаторов для которых лучше не использовать html5 плеер. Используется при авто-определении типа плеера. Идентификаторы сравниваются со строкой построенной по шаблону @<platform.version> <platform.os>:<browser.name>/<browser.version>
68 |
69 |
--------------------------------------------------------------------------------
/examples/quick-start/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | -webkit-box-sizing: border-box;
3 | -moz-box-sizing: border-box;
4 | box-sizing: border-box;
5 | }
6 |
7 | .player {
8 | display: block;
9 | position: relative;
10 | width: 100%;
11 | height: 140px;
12 | }
13 |
14 | .overlay {
15 | display: block;
16 | position: absolute;
17 | top: 0;
18 | left: 0;
19 | width: 100%;
20 | height: 100%;
21 | z-index: 100;
22 | }
23 |
24 | .overlay_hidden {
25 | display: none;
26 | }
27 |
28 | .overlay_error {
29 | background: #ffcccc;
30 | color: #ff0000;
31 | }
32 |
33 | .controls_play {
34 | padding: 5px 10px;
35 | background: #fff;
36 | border: 1px #aaa solid;
37 | cursor: pointer;
38 | }
39 |
40 | .controls_play:hover {
41 | border-color: #666;
42 | }
43 |
44 | .player_playing .controls_play {
45 | background: #9DBAFF;
46 | }
47 |
48 | .progress {
49 | display: block;
50 | position: relative;
51 | width: 90%;
52 | height: 104px;
53 | border: 1px #ccc solid;
54 | margin: 10px 0;
55 | }
56 |
57 | .progress_loaded, .progress_current {
58 | display: block;
59 | position: absolute;
60 | height: 100%;
61 | top: 0;
62 | left: 0;
63 | }
64 |
65 | .progress_loaded {
66 | background: #ccc;
67 | z-index: 1;
68 | }
69 |
70 | .progress_current {
71 | background: #535BC3;
72 | z-index: 2;
73 | }
74 |
75 | .volume {
76 | display: block;
77 | position: absolute;
78 | right: 0;
79 | width: 9%;
80 | top: 0;
81 | height: 100%;
82 | border: 1px #ccc solid;
83 | }
84 |
85 | .volume_bar {
86 | display: block;
87 | position: absolute;
88 | bottom: 0;
89 | width: 100%;
90 | background: #535BC3;
91 | }
92 |
--------------------------------------------------------------------------------
/src/fx/volume/volume-lib.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Методы конвертации значений громкости.
3 | * @name volumeLib
4 | * @exported ya.music.Audio.fx.volumeLib
5 | * @namespace
6 | */
7 | var volumeLib = {};
8 |
9 | /**
10 | * Минимальное значение громкости, при котором происходит отключение звука.
11 | * Ограничение в 0.01 подобрано эмпирически.
12 | * @type {number}
13 | */
14 | volumeLib.EPSILON = 0.01;
15 |
16 | /**
17 | * Коэффициент для преобразований громкости из относительной шкалы в децибелы.
18 | * @type Number
19 | * @private
20 | */
21 | volumeLib._DBFS_COEF = 20 / Math.log(10);
22 |
23 | /**
24 | * Вычисление значение относительной громкости по значению на логарифмической шкале.
25 | * @param {Number} value Значение на шкале.
26 | * @returns {Number}
27 | */
28 | volumeLib.toExponent = function(value) {
29 | var volume = Math.pow(volumeLib.EPSILON, 1 - value);
30 | return volume > volumeLib.EPSILON ? volume : 0;
31 | };
32 |
33 | /**
34 | * Вычисление положения на логарифмической шкале по значению относительной громкости громкости.
35 | * @param {Number} volume Громкость.
36 | * @returns {Number}
37 | */
38 | volumeLib.fromExponent = function(volume) {
39 | return 1 - Math.log(Math.max(volume, volumeLib.EPSILON)) / Math.log(volumeLib.EPSILON);
40 | };
41 |
42 | /**
43 | * Вычисление значения dBFS из относительного значения громкости.
44 | * @param {Number} volume Относительная громкость.
45 | * @returns {Number}
46 | */
47 | volumeLib.toDBFS = function(volume) {
48 | return Math.log(volume) * volumeLib._DBFS_COEF;
49 | };
50 |
51 | /**
52 | * Вычисление значения относительной громкости из значения dBFS.
53 | * @param {Number} dbfs Громкость в dBFS.
54 | * @returns {Number}
55 | */
56 | volumeLib.fromDBFS = function(dbfs) {
57 | return Math.exp(dbfs / volumeLib._DBFS_COEF);
58 | };
59 |
60 | module.exports = volumeLib;
61 |
--------------------------------------------------------------------------------
/src/fx/equalizer/equalizer-band.js:
--------------------------------------------------------------------------------
1 | var Events = require('../../lib/async/events');
2 | var EqualizerStatic = require('./equalizer-static');
3 |
4 | // =================================================================
5 |
6 | // Конструктор
7 |
8 | // =================================================================
9 |
10 | /**
11 | * Событие изменения значения усиления.
12 | * @event EqualizerBand.EVENT_CHANGE
13 | * @param {Number} value Новое значение.
14 | */
15 |
16 | /**
17 | * @classdesc Полоса пропускания эквалайзера.
18 | * @extends Events
19 | *
20 | * @param {AudioContext} audioContext Контекст Web Audio API.
21 | * @param {String} type Тип фильтра.
22 | * @param {Number} frequency Частота фильтра.
23 | *
24 | * @fires EqualizerBand.EVENT_CHANGE
25 | *
26 | * @constructor
27 | * @private
28 | */
29 | var EqualizerBand = function(audioContext, type, frequency) {
30 | Events.call(this);
31 |
32 | this.type = type;
33 |
34 | this.filter = audioContext.createBiquadFilter();
35 | this.filter.type = type;
36 | this.filter.frequency.value = frequency;
37 | this.filter.Q.value = 1;
38 | this.filter.gain.value = 0;
39 | };
40 | Events.mixin(EqualizerBand);
41 |
42 | // =================================================================
43 |
44 | // Управление настройками
45 |
46 | // =================================================================
47 |
48 | /**
49 | * Получить частоту полосы пропускания.
50 | * @returns {Number}
51 | */
52 | EqualizerBand.prototype.getFreq = function() {
53 | return this.filter.frequency.value;
54 | };
55 |
56 | /**
57 | * Получить значение усиления.
58 | * @returns {Number}
59 | */
60 | EqualizerBand.prototype.getValue = function() {
61 | return this.filter.gain.value;
62 | };
63 |
64 | /**
65 | * Установить значение усиления.
66 | * @param {Number} value Значение.
67 | */
68 | EqualizerBand.prototype.setValue = function(value) {
69 | this.filter.gain.value = value;
70 | this.trigger(EqualizerStatic.EVENT_CHANGE, value);
71 | };
72 |
73 | module.exports = EqualizerBand;
74 |
--------------------------------------------------------------------------------
/src/error/audio-error.js:
--------------------------------------------------------------------------------
1 | var ErrorClass = require('../lib/class/error-class');
2 |
3 | /**
4 | * @exported ya.music.Audio.AudioError
5 | * @classdesc Класс ошибки аудиопллеера.
6 | * @extends Error
7 | * @param {String} message Текст ошибки.
8 | *
9 | * @constructor
10 | */
11 | var AudioError = function(message) {
12 | ErrorClass.call(this, message);
13 | };
14 | AudioError.prototype = ErrorClass.create("AudioError");
15 |
16 | /**
17 | * Не найдена реализация плеера или возникла ошибка при инициализации всех доступных реализаций.
18 | * @type {String}
19 | * @const
20 | */
21 | AudioError.NO_IMPLEMENTATION = "cannot find suitable implementation";
22 | /**
23 | * Аудиофайл не был предзагружен или во время загрузки произошла ошибка.
24 | * @type {String}
25 | * @const
26 | */
27 | AudioError.NOT_PRELOADED = "track is not preloaded";
28 | /**
29 | * Действие недоступно из текущего состояния.
30 | * @type {String}
31 | * @const
32 | */
33 | AudioError.BAD_STATE = "action is not permited from current state";
34 |
35 | /**
36 | * Flash-плеер был заблокирован.
37 | * @type {String}
38 | * @const
39 | */
40 | AudioError.FLASH_BLOCKER = "flash is rejected by flash blocker plugin";
41 | /**
42 | * Возникла ошибка при инициализации Flash-плеера по неизвестным причинам.
43 | * @type {String}
44 | * @const
45 | */
46 | AudioError.FLASH_UNKNOWN_CRASH = "flash is crashed without reason";
47 | /**
48 | * Возникла ошибка при инициализации Flash-плеера из-за таймаута.
49 | * @type {String}
50 | * @const
51 | */
52 | AudioError.FLASH_INIT_TIMEOUT = "flash init timed out";
53 | /**
54 | * Внутренняя ошибка Flash-плеера.
55 | * @type {String}
56 | * @const
57 | */
58 | AudioError.FLASH_INTERNAL_ERROR = "flash internal error";
59 | /**
60 | * Попытка вызвать недоступный экземляр Flash-плеера.
61 | * @type {String}
62 | * @const
63 | */
64 | AudioError.FLASH_EMMITER_NOT_FOUND = "flash event emmiter not found";
65 | /**
66 | * Flash-плеер перестал отвечать на запросы.
67 | * @type {String}
68 | * @const
69 | */
70 | AudioError.FLASH_NOT_RESPONDING = "flash player doesn't response";
71 |
72 | module.exports = AudioError;
73 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | var BUILDDIR = "./dist";
4 |
5 | // Project configuration.
6 | grunt.initConfig({
7 | pkg: grunt.file.readJSON('package.json'),
8 |
9 | clean: [BUILDDIR],
10 |
11 | mkdir: {
12 | options: {
13 | create: [BUILDDIR]
14 | }
15 | },
16 |
17 | browserify: {
18 | options: {
19 | debug: true
20 | },
21 | main: {
22 | src: "./src/index.js",
23 | dest: BUILDDIR + "/index.js"
24 | },
25 | modules: {
26 | src: "./src/modules.js",
27 | dest: BUILDDIR + "/modules.js"
28 | }
29 | },
30 |
31 | uglify: {
32 | options: {
33 | mangle: true,
34 | compress: true,
35 | bareReturns: true,
36 | stats: true
37 | },
38 | main: {
39 | src: BUILDDIR + '/index.js',
40 | dest: BUILDDIR + '/index.min.js',
41 | options: {
42 | sourceMap: true,
43 | sourceMapName: BUILDDIR + "/index.map.json"
44 | }
45 | },
46 | modules: {
47 | src: BUILDDIR + '/modules.js',
48 | dest: BUILDDIR + '/modules.min.js',
49 | options: {
50 | sourceMap: true,
51 | sourceMapName: BUILDDIR + "/modules.map.json"
52 | }
53 | }
54 | },
55 |
56 | copy: {
57 | flash: {
58 | src: "./src/flash/build/player-2_0.swf",
59 | dest: BUILDDIR + "/player-2_0.swf"
60 | }
61 | }
62 | });
63 |
64 | grunt.loadNpmTasks('grunt-contrib-clean');
65 | grunt.loadNpmTasks('grunt-mkdir');
66 | grunt.loadNpmTasks('grunt-browserify');
67 | grunt.loadNpmTasks('grunt-contrib-uglify');
68 | grunt.loadNpmTasks('grunt-contrib-copy');
69 |
70 | grunt.registerTask('build', ['clean', 'mkdir', 'browserify', 'copy']);
71 | grunt.registerTask('all', ['build', 'uglify']);
72 |
73 | // Default task.
74 | grunt.registerTask('default', ['all']);
75 | };
76 |
--------------------------------------------------------------------------------
/jsdoc/doc/render.jsdoc.js:
--------------------------------------------------------------------------------
1 | var cleanupTags = /<\/?(ul|li|p)>/g;
2 | var bbcodes = /\{\[(.*?)\]\}/g;
3 | var unescape = /\\(\{|\})/g;
4 | var beautify_lines = /(\n[\t ]*){3,}/g;
5 | var beautify_asterix = /([\t ]*\*[\t ]*\n){2,}/g;
6 | var linkhref = /\{@linkhref (.*?) ([^\}]*) *\}/g;
7 |
8 | var doclet = /\/\*\*[\s\S]*?\*\//g;
9 |
10 | module.exports = function(page, data, style) {
11 | page = page.replace(cleanupTags, "")
12 | .replace(unescape, "$1")
13 | .replace(bbcodes, "<$1>");
14 |
15 | if (style === 'tech') {
16 | page = page.replace(linkhref, "$2");
17 | } else {
18 | page = page.replace(linkhref, "{@link $1 $2}");
19 | }
20 |
21 | var aliases = [];
22 |
23 | page = page.replace(doclet, function(doclet) {
24 | var alias = /@alias (.*)/g;
25 | var name = /@name (.*)/g;
26 |
27 | var rawName;
28 | if (rawName = name.exec(doclet)) {
29 | rawName = rawName[1];
30 |
31 | doclet = doclet.replace(alias, function(_, aliasName) {
32 | aliases.push({
33 | reg: new RegExp("([^\\w.#~\/])" + rawName + "([^\\w])", "g"),
34 | rep: "$1" + aliasName + "$2"
35 | // rep: aliasName
36 | });
37 |
38 | return "";
39 | });
40 | }
41 |
42 | return doclet;
43 | });
44 |
45 | // console.log("-----------------------------");
46 | // console.log(aliases);
47 |
48 | aliases.forEach(function(alias) {
49 | page = page.replace(alias.reg, alias.rep);
50 | });
51 |
52 | if (style === "tech") {
53 | page = page
54 | .replace(/\.<([^/]*?)>/g, ".<$1>")
55 | .replace(/<(\/?)code>/g, "<$1codeph>")
56 | .replace(/<(\/?)strong>/g, "<$1b>")
57 | .replace(/ya\.music\./g, "")
58 | .replace(/Array\.<([^/]*?)>/g, "$1[]")
59 | .replace(/ Error( |$)/mg,
60 | " Error ");
61 | }
62 |
63 | return page.replace(/\r/g, "").replace(beautify_lines, "\n\n")
64 | .replace(beautify_asterix, " *\n");
65 | };
66 |
--------------------------------------------------------------------------------
/src/lib/async/promise.js:
--------------------------------------------------------------------------------
1 | var vow = require('vow');
2 | var detect = require('../browser/detect');
3 | var merge = require('../data/merge');
4 |
5 | // =================================================================
6 |
7 | // Promise
8 |
9 | // =================================================================
10 |
11 | /**
12 | * @classdesc Обещание по спецификации {@linkhref https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise ES 2015 promises}. В устаревших браузерах и IE используется замена из библиотеки {@linkhref http://github.com/dfilatov/vow.git vow}
13 | *
14 | * @exported ya.music.lib.Promise
15 | * @constructor
16 | */
17 | var Promise;
18 | if (typeof window.Promise !== "function"
19 | || detect.browser.name === "msie" || detect.browser.name === "edge" // мелкие мягкие как всегда ничего не умеют делать правильно
20 | ) {
21 | Promise = function(resolver) {
22 | var promise;
23 | try {
24 | promise = new vow.Promise(resolver);
25 | } catch(e) {
26 | promise = vow.reject(e);
27 | }
28 | return promise;
29 | };
30 | merge(Promise, vow.Promise, true);
31 | Promise.prototype = vow.Promise.prototype;
32 | } else {
33 | Promise = window.Promise;
34 | }
35 |
36 | module.exports = Promise;
37 |
38 | /**
39 | * Назначить обработчики разрешения и отклонения обещания.
40 | * @method Promise#then
41 | * @param {function} callback Обработчик успеха.
42 | * @param {null|function} [errback] Обработчик ошибки.
43 | * @returns {Promise} новое обещание из результатов обработчика.
44 | */
45 |
46 | /**
47 | * Назначить обработчик отклонения обещания.
48 | * @method Promise#catch
49 | * @param {function} errback Обработчик ошибки.
50 | * @returns {Promise} новое обещание из результатов обработчика.
51 | */
52 |
53 | // =================================================================
54 |
55 | // AbortablePromise
56 |
57 | // =================================================================
58 |
59 | /**
60 | * @class AbortablePromise
61 | * @classdesc Обещание с возможностью отмены связанного с ним действия.
62 | * @extends Promise
63 | */
64 |
65 | /**
66 | * Отмена действия, связанного с обещанием. Абстрактный метод.
67 | * @method AbortablePromise#abort
68 | * @param {String|Error} reason Причина отмены действия.
69 | * @abstract
70 | */
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/flash/loader.js:
--------------------------------------------------------------------------------
1 | var FlashBlockNotifier = require('./flashblocknotifier');
2 | var FlashEmbedder = require('./flashembedder');
3 | var detect = require('../lib/browser/detect');
4 |
5 | var winSafari = detect.platform.os === 'windows' && detect.browser.name === 'safari';
6 |
7 | var CONTAINER_CLASS = "ya-flash-player-wrapper";
8 |
9 | /**
10 | * Загрузчик флеш-плеера
11 | *
12 | * @alias FlashManager~flashLoader
13 | *
14 | * @param {string} url - Ссылка на плеера
15 | * @param {string} minVersion - минимальная версия плеера
16 | * @param {string|number} id - идентификатор нового плеера
17 | * @param {function} loadCallback - колбек для события загрузки
18 | * @param {object} flashVars - данные передаваемые во флеш
19 | * @param {HTMLElement} container - контейнер для видимого флеш-плеера
20 | * @param {string} sizeX - размер по горизонтали
21 | * @param {string} sizeY - размер по вертикали
22 | *
23 | * @private
24 | *
25 | * @returns {HTMLElement} -- Контейнер флеш-плеера
26 | */
27 | module.exports = function(url, minVersion, id, loadCallback, flashVars, container, sizeX, sizeY) {
28 | var $flashPlayer = document.createElement("div");
29 | $flashPlayer.id = "wrapper_" + id;
30 | $flashPlayer.innerHTML = '';
31 |
32 | sizeX = sizeX || "1000";
33 | sizeY = sizeY || "1000";
34 |
35 | var embedder,
36 | flashSizeX,
37 | flashSizeY,
38 | options;
39 |
40 | if (container && !winSafari) {
41 | embedder = FlashEmbedder;
42 | flashSizeX = sizeX;
43 | flashSizeY = sizeY;
44 | options = {allowscriptaccess: "always", wmode: "transparent"};
45 |
46 | $flashPlayer.className = CONTAINER_CLASS;
47 | $flashPlayer.style.cssText = 'position: relative; width: 100%; height: 100%; overflow: hidden;';
48 | container.appendChild($flashPlayer);
49 | } else {
50 | embedder = FlashBlockNotifier;
51 | flashSizeX = flashSizeY = "1";
52 | options = {allowscriptaccess: "always"};
53 |
54 | $flashPlayer.style.cssText = 'position: absolute; left: -1px; top: -1px; width: 0px; height: 0px; overflow: hidden;';
55 | document.body.appendChild($flashPlayer);
56 | }
57 |
58 | embedder.embedSWF(
59 | url,
60 | id,
61 | flashSizeX,
62 | flashSizeY,
63 | minVersion,
64 | "",
65 | flashVars,
66 | options,
67 | {},
68 | loadCallback
69 | );
70 |
71 | return $flashPlayer;
72 | };
73 |
--------------------------------------------------------------------------------
/spec/Events.md:
--------------------------------------------------------------------------------
1 | ## *class* Events
2 |
3 | **Доступен извне как:** `ya.music.lib.Events`
4 |
5 | Диспетчер событий.
6 |
7 | #### new Events()
8 |
9 | ----
10 |
11 | ### Методы
12 |
13 | #### Events.mixin (classConstructor: function) : function
14 |
15 | Расширить произвольный класс свойствами диспетчера событий.
16 |
17 | | Имя | Тип | * | Описание |
18 | | --- | --- | --- | --- |
19 | | classConstructor | function | | Конструктор класса. |
20 |
21 | > **Возвращает:** тот же конструктор класса, расширенный свойствами диспетчера событий.
22 |
23 | #### Events.eventize (object: Object) : Object
24 |
25 | Расширить произвольный объект свойствами диспетчера событий.
26 |
27 | | Имя | Тип | * | Описание |
28 | | --- | --- | --- | --- |
29 | | object | Object | | Объект. |
30 |
31 | > **Возвращает:** тот же объект, расширенный свойствами диспетчера событий.
32 |
33 | #### Events#on (event: String, callback: function) : [Events](Events.md#Events)
34 |
35 | Подписаться на событие (цепочный метод).
36 |
37 | | Имя | Тип | * | Описание |
38 | | --- | --- | --- | --- |
39 | | event | String | | Имя события. |
40 | | callback | function | | Обработчик события. |
41 |
42 | > **Возвращает:** ссылку на контекст.
43 |
44 | #### Events#off (event: String, callback: function) : [Events](Events.md#Events)
45 |
46 | Отписаться от события (цепочный метод).
47 |
48 | | Имя | Тип | * | Описание |
49 | | --- | --- | --- | --- |
50 | | event | String | | Имя события. |
51 | | callback | function | | Обработчик события. |
52 |
53 | > **Возвращает:** ссылку на контекст.
54 |
55 | #### Events#once (event: String, callback: function) : [Events](Events.md#Events)
56 |
57 | Подписаться на событие и отписаться сразу после его первого возникновения (цепочный метод).
58 |
59 | | Имя | Тип | * | Описание |
60 | | --- | --- | --- | --- |
61 | | event | String | | Имя события. |
62 | | callback | function | | Обработчик события. |
63 |
64 | > **Возвращает:** ссылку на контекст.
65 |
66 | #### Events#clearListeners () : [Events](Events.md#Events)
67 |
68 | Отписаться от всех слушателей событий (цепочный метод).
69 |
70 | > **Возвращает:** ссылку на контекст.
71 |
72 | #### Events#muteEvents () : [Events](Events.md#Events)
73 |
74 | Остановить запуск событий (цепочный метод).
75 |
76 | > **Возвращает:** ссылку на контекст.
77 |
78 | #### Events#unmuteEvents () : [Events](Events.md#Events)
79 |
80 | Возобновить запуск событий (цепочный метод).
81 |
82 | > **Возвращает:** ссылку на контекст.
83 |
84 |
--------------------------------------------------------------------------------
/src/audio-static.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @alias Audio
3 | * @ignore
4 | */
5 | var AudioStatic = {};
6 |
7 | /**
8 | * Начало воспроизведения трека.
9 | * @type {String}
10 | * @const
11 | * @ignore
12 | */
13 | AudioStatic.EVENT_PLAY = "play";
14 | /**
15 | * Остановка воспроизведения.
16 | * @type {String}
17 | * @const
18 | * @ignore
19 | */
20 | AudioStatic.EVENT_STOP = "stop";
21 | /**
22 | * Пауза воспроизведения.
23 | * @type {String}
24 | * @const
25 | * @ignore
26 | */
27 | AudioStatic.EVENT_PAUSE = "pause";
28 | /**
29 | * Обновление позиции воспроизведения.
30 | * @type {String}
31 | * @const
32 | * @ignore
33 | */
34 | AudioStatic.EVENT_PROGRESS = "progress";
35 | /**
36 | * Началась загрузка трека.
37 | * @type {String}
38 | * @const
39 | * @ignore
40 | */
41 | AudioStatic.EVENT_LOADING = "loading";
42 | /**
43 | * Загрузка трека завершена.
44 | * @type {String}
45 | * @const
46 | * @ignore
47 | */
48 | AudioStatic.EVENT_LOADED = "loaded";
49 | /**
50 | * Изменение громкости.
51 | * @type {String}
52 | * @const
53 | * @ignore
54 | */
55 | AudioStatic.EVENT_VOLUME = "volumechange";
56 |
57 | /**
58 | * Воспроизведение трека завершено.
59 | * @type {String}
60 | * @const
61 | * @ignore
62 | */
63 | AudioStatic.EVENT_ENDED = "ended";
64 | /**
65 | * Возникла ошибка при инициализации плеера.
66 | * @type {String}
67 | * @const
68 | * @ignore
69 | */
70 | AudioStatic.EVENT_CRASHED = "crashed";
71 | /**
72 | * Возникла ошибка при воспроизведении.
73 | * @type {String}
74 | * @const
75 | * @ignore
76 | */
77 | AudioStatic.EVENT_ERROR = "error";
78 | /**
79 | * Изменение статуса плеера.
80 | * @type {String}
81 | * @const
82 | * @ignore
83 | */
84 | AudioStatic.EVENT_STATE = "state";
85 | /**
86 | * Переключение между текущим и предзагруженным треком.
87 | * @type {String}
88 | * @const
89 | * @ignore
90 | */
91 | AudioStatic.EVENT_SWAP = "swap";
92 | /**
93 | * Событие предзагрузчика. Используется в качестве префикса.
94 | * @type {String}
95 | * @const
96 | */
97 | AudioStatic.PRELOADER_EVENT = "preloader:";
98 | /**
99 | * Плеер находится в состоянии инициализации.
100 | * @type {String}
101 | * @const
102 | */
103 | AudioStatic.STATE_INIT = "init";
104 | /**
105 | * Не удалось инициализировать плеер.
106 | * @type {String}
107 | * @const
108 | */
109 | AudioStatic.STATE_CRASHED = "crashed";
110 | /**
111 | * Плеер готов и ожидает.
112 | * @type {String}
113 | * @const
114 | */
115 | AudioStatic.STATE_IDLE = "idle";
116 | /**
117 | * Плеер проигрывает трек.
118 | * @type {String}
119 | * @const
120 | */
121 | AudioStatic.STATE_PLAYING = "playing";
122 | /**
123 | * Плеер поставлен на паузу.
124 | * @type {String}
125 | * @const
126 | */
127 | AudioStatic.STATE_PAUSED = "paused";
128 |
129 | module.exports = AudioStatic;
130 |
--------------------------------------------------------------------------------
/src/fx/equalizer/default.presets.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | "id": "default",
4 | "preamp": 0,
5 | "bands": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
6 | },
7 | {
8 | "id": "Classical",
9 | "preamp": -0.5,
10 | "bands": [-0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -3.5, -3.5, -3.5, -4.5]
11 | },
12 | {
13 | "id": "Club",
14 | "preamp": -3.359999895095825,
15 | "bands": [-0.5, -0.5, 4, 2.5, 2.5, 2.5, 1.5, -0.5, -0.5, -0.5]
16 | },
17 | {
18 | "id": "Dance",
19 | "preamp": -2.1599998474121094,
20 | "bands": [4.5, 3.5, 1, -0.5, -0.5, -2.5, -3.5, -3.5, -0.5, -0.5]
21 | },
22 | {
23 | "id": "Full Bass",
24 | "preamp": -3.5999999046325684,
25 | "bands": [4, 4.5, 4.5, 2.5, 0.5, -2, -4, -5, -5.5, -5.5]
26 | },
27 | {
28 | "id": "Full Bass & Treble",
29 | "preamp": -5.039999961853027,
30 | "bands": [3.5, 2.5, -0.5, -3.5, -2, 0.5, 4, 5.5, 6, 6]
31 | },
32 | {
33 | "id": "Full Treble",
34 | "preamp": -6,
35 | "bands": [-4.5, -4.5, -4.5, -2, 1, 5.5, 8, 8, 8, 8]
36 | },
37 | {
38 | "id": "Laptop Speakers / Headphone",
39 | "preamp": -4.079999923706055,
40 | "bands": [2, 5.5, 2.5, -1.5, -1, 0.5, 2, 4.5, 6, 7]
41 | },
42 | {
43 | "id": "Large Hall",
44 | "preamp": -3.5999999046325684,
45 | "bands": [5, 5, 2.5, 2.5, -0.5, -2, -2, -2, -0.5, -0.5]
46 | },
47 | {
48 | "id": "Live",
49 | "preamp": -2.6399998664855957,
50 | "bands": [-2, -0.5, 2, 2.5, 2.5, 2.5, 2, 1, 1, 1]
51 | },
52 | {
53 | "id": "Party",
54 | "preamp": -2.6399998664855957,
55 | "bands": [3.5, 3.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 3.5, 3.5]
56 | },
57 | {
58 | "id": "Pop",
59 | "preamp": -3.119999885559082,
60 | "bands": [-0.5, 2, 3.5, 4, 2.5, -0.5, -1, -1, -0.5, -0.5]
61 | },
62 | {
63 | "id": "Reggae",
64 | "preamp": -4.079999923706055,
65 | "bands": [-0.5, -0.5, -0.5, -2.5, -0.5, 3, 3, -0.5, -0.5, -0.5]
66 | },
67 | {
68 | "id": "Rock",
69 | "preamp": -5.039999961853027,
70 | "bands": [4, 2, -2.5, -4, -1.5, 2, 4, 5.5, 5.5, 5.5]
71 | },
72 | {
73 | "id": "Ska",
74 | "preamp": -5.519999980926514,
75 | "bands": [-1, -2, -2, -0.5, 2, 2.5, 4, 4.5, 5.5, 4.5]
76 | },
77 | {
78 | "id": "Soft",
79 | "preamp": -4.799999713897705,
80 | "bands": [2, 0.5, -0.5, -1, -0.5, 2, 4, 4.5, 5.5, 6]
81 | },
82 | {
83 | "id": "Soft Rock",
84 | "preamp": -2.6399998664855957,
85 | "bands": [2, 2, 1, -0.5, -2, -2.5, -1.5, -0.5, 1, 4]
86 | },
87 | {
88 | "id": "Techno",
89 | "preamp": -3.8399999141693115,
90 | "bands": [4, 2.5, -0.5, -2.5, -2, -0.5, 4, 4.5, 4.5, 4]
91 | }
92 | ];
93 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Настройки библиотеки.
3 | * @exported ya.music.Audio.config
4 | * @namespace
5 | */
6 | var config = {
7 |
8 | // =================================================================
9 |
10 | // Общие настройки
11 |
12 | // =================================================================
13 |
14 | /**
15 | * Общие настройки.
16 | * @namespace
17 | */
18 | audio: {
19 | /**
20 | * Количество попыток реинициализации.
21 | * @type {Number}
22 | */
23 | retry: 3
24 | },
25 |
26 | // =================================================================
27 |
28 | // Flash-плеер
29 |
30 | // =================================================================
31 |
32 | /**
33 | * Настройки подключения Flash-плеера.
34 | * @namespace
35 | */
36 | flash: {
37 | /**
38 | * Путь к .swf файлу флеш-плеера.
39 | * @type {String}
40 | */
41 | path: "dist",
42 | /**
43 | * Имя .swf файла флеш-плеера.
44 | * @type {String}
45 | */
46 | name: "player-2_1.swf",
47 | /**
48 | * Минимальная версия флеш-плеера.
49 | * @type {String}
50 | */
51 | version: "9.0.28",
52 | /**
53 | * ID, который будет выставлен для элемента с Flash-плеером.
54 | * @type {String}
55 | */
56 | playerID: "YandexAudioFlashPlayer",
57 | /**
58 | * Имя функции-обработчика событий Flash-плеера.
59 | * @type {String}
60 | * @const
61 | */
62 | callback: "ya.music.Audio._flashCallback",
63 | /**
64 | * Таймаут инициализации.
65 | * @type {Number}
66 | */
67 | initTimeout: 3000, // 3 sec
68 | /**
69 | * Таймаут загрузки
70 | * @type {Number}
71 | */
72 | loadTimeout: 5000,
73 | /**
74 | * Таймаут инициализации после клика.
75 | * @type {Number}
76 | */
77 | clickTimeout: 1000,
78 | /**
79 | * Интервал проверки доступности Flash-плеера.
80 | * @type {Number}
81 | */
82 | heartBeatInterval: 1000
83 | },
84 |
85 | // =================================================================
86 |
87 | // HTML5-плеер
88 |
89 | // =================================================================
90 |
91 | /**
92 | * Описание настроек HTML5 плеера.
93 | * @namespace
94 | */
95 | html5: {
96 | /**
97 | * Список идентификаторов для которых лучше не использовать html5 плеер. Используется при
98 | * авто-определении типа плеера. Идентификаторы сравниваются со строкой построенной по шаблону
99 | * `@ :/`
100 | *
101 | * @type { Array. }
102 | */
103 | blacklist: ["linux:mozilla", "unix:mozilla", "macos:mozilla", ":opera", "@NT 5", "@NT 4", ":msie/9"]
104 | }
105 | };
106 |
107 | module.exports = config;
108 |
--------------------------------------------------------------------------------
/src/flash/src/AudioLoader.as:
--------------------------------------------------------------------------------
1 | package {
2 | import flash.events.Event;
3 | import flash.events.EventDispatcher;
4 | import flash.events.IOErrorEvent;
5 | import flash.events.ProgressEvent;
6 | import flash.media.Sound;
7 | import flash.net.URLRequest;
8 |
9 | public final class AudioLoader extends EventDispatcher {
10 | public var sound:Sound;
11 |
12 | private var _duration:int = 0;
13 | private var guessDuration:int = -1;
14 | public var loaded:int = 0;
15 |
16 | public var isLoading:Boolean = false;
17 | public var isLoaded:Boolean = false;
18 | public var preloaded:String = "";
19 | public var src:String = "";
20 |
21 | public function AudioLoader() {
22 | }
23 |
24 | public function get duration():int {
25 | if (this._duration > 0) {
26 | return this._duration
27 | } else {
28 | return this.guessDuration;
29 | }
30 | }
31 |
32 | public function load(src:String, duration:Number):void {
33 | this.abort();
34 |
35 | this.sound = new Sound();
36 | this._duration = duration;
37 |
38 | this.sound.addEventListener(Event.OPEN, this.onLoadingStart);
39 | this.sound.addEventListener(Event.COMPLETE, this.onLoadingEnd);
40 | this.sound.addEventListener(ProgressEvent.PROGRESS, this.onLoadingProgress);
41 | this.sound.addEventListener(IOErrorEvent.IO_ERROR, this.onError);
42 |
43 | this.sound.load(new URLRequest(src));
44 | this.src = src;
45 | }
46 |
47 | public function abort():void {
48 | if (!(this.sound is Sound)) {
49 | return;
50 | }
51 |
52 | this.sound.removeEventListener(Event.OPEN, this.onLoadingStart);
53 | this.sound.removeEventListener(Event.COMPLETE, this.onLoadingEnd);
54 | this.sound.removeEventListener(ProgressEvent.PROGRESS, this.onLoadingProgress);
55 | this.sound.removeEventListener(IOErrorEvent.IO_ERROR, this.onError);
56 |
57 | try {
58 | this.sound.close();
59 | } catch (e:Error) {
60 | }
61 | this.sound = null;
62 |
63 | this._duration = 0;
64 | this.loaded = 0;
65 | this.isLoaded = false;
66 | this.isLoading = false;
67 | }
68 |
69 | private function onLoadingStart(event:Event):void {
70 | this.isLoading = true;
71 | this.dispatchEvent(new Event(AudioEvent.EVENT_LOADING));
72 | }
73 |
74 | private function onLoadingEnd(event:Event):void {
75 | this._duration = this.sound.length;
76 | this.loaded = this.duration;
77 | this.isLoaded = true;
78 |
79 | this.dispatchEvent(new Event(AudioEvent.EVENT_PROGRESS));
80 | this.dispatchEvent(new Event(AudioEvent.EVENT_LOADED));
81 | }
82 |
83 | private function onLoadingProgress(event:ProgressEvent):void {
84 | //FIXME: it will give wrong values on VBR audio (Variable BitRate), and it gives imprecise values with CBR audio
85 | this.guessDuration = this.sound.length * event.bytesTotal / event.bytesLoaded;
86 |
87 | this.loaded = this.duration * event.bytesLoaded / event.bytesTotal;
88 | this.dispatchEvent(new Event(AudioEvent.EVENT_PROGRESS));
89 | }
90 |
91 | private function onError(event:IOErrorEvent):void {
92 | this.abort();
93 | this.dispatchEvent(event);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/jsdoc/doc/render.js:
--------------------------------------------------------------------------------
1 | var fs = require("fs");
2 | var Handlebars = require("handlebars");
3 | var renderType;
4 | var renderStyle;
5 | var files;
6 |
7 | var layout;
8 | var index;
9 | var parser;
10 |
11 | var trim = /^\s*|\s*$|\n(\{\{\/if}})|(\{\{#if [.\w ]*}})\n|\n(\{\{else[.\w ]*}})\n/g;
12 |
13 | var prepare = function(path, style) {
14 | renderType = style.split("-")[0];
15 | renderStyle = style.split("-")[1];
16 | files = renderStyle === "files";
17 |
18 | var partials = fs.readdirSync(path + "/" + style);
19 |
20 | partials.forEach(function(partial) {
21 | Handlebars.registerPartial(partial.replace(".hbs", ""),
22 | fs.readFileSync(path + "/" + style + "/" + partial, {encoding: "utf8"}).replace(trim, "$1$2$3")
23 | );
24 | });
25 |
26 | layout = Handlebars.compile(
27 | fs.readFileSync(path + "/" + style + "/layout.hbs", {encoding: "utf8"}).replace(trim, "$1$2$3")
28 | );
29 |
30 | try {
31 | index = Handlebars.compile(
32 | fs.readFileSync(path + "/" + style + "/index.hbs", {encoding: "utf8"}).replace(trim, "$1$2$3")
33 | );
34 | } catch(e) {}
35 |
36 | parser = require("./render." + renderType + ".js");
37 | };
38 |
39 | var render = function(data, out) {
40 | var renderPage = function(item) {
41 | var page = {};
42 | page[item.kind] = item;
43 | return parser(layout(page), data, item);
44 | };
45 |
46 | var list = {};
47 |
48 | if (files) {
49 | for (var link in data.links) {
50 | var item = data.links[link];
51 | var kind = item.kind;
52 | var parent = item.parent;
53 | var parentKind = parent && parent.kind;
54 | var path;
55 |
56 | if (parent && (parentKind === "class" || parentKind === "typedef" || parentKind === "namespace")) {
57 | path = parent.name;
58 | } else if (kind === "class" || kind === "typedef" || kind === "namespace") {
59 | path = item.name;
60 | } else {
61 | path = "global";
62 | }
63 |
64 | data.links[link] = path + ".md#" + link.replace(/#/g, "..").replace(/~/g, "--");
65 | }
66 |
67 | var makeFile = function(item) { list[item.name] = renderPage(item); };
68 |
69 | // кладём все классы в отдельные файлы даже если они внутренние
70 | data.linear["class"].forEach(makeFile);
71 | // кладём только глобальные тайпдефы в отдельные файлы
72 | data.tree["typedef"].forEach(makeFile);
73 | // кладём только глобальные неймспейсы в отдельные файлы
74 | data.tree["namespace"].forEach(makeFile);
75 |
76 | list.global = "";
77 |
78 | for (var key in data.linear) {
79 | if (key === "class" || key === "typedef" || key === "namespace") {
80 | continue;
81 | }
82 |
83 | list.global += data.linear[key].map(renderPage).join("\n\n");
84 | }
85 |
86 | list[out] = parser(index(data), data, renderStyle);
87 |
88 | if (!list.global) {
89 | delete list.global;
90 | }
91 | } else {
92 | for (var link in data.links) {
93 | data.links[link] = "#" + link.replace(/#/g, "..").replace(/~/g, "--");
94 | }
95 |
96 | list[out] = parser(layout(data), data, renderStyle);
97 | }
98 |
99 | return list;
100 | };
101 |
102 | module.exports = {
103 | prepare: prepare,
104 | render: render
105 | };
106 |
--------------------------------------------------------------------------------
/src/flash/flashembedder.js:
--------------------------------------------------------------------------------
1 | var swfobject = require('../lib/browser/swfobject');
2 |
3 | /**
4 | * Модуль загрузки флеш-плеера
5 | * @namespace
6 | * @private
7 | */
8 | var FlashEmbedder = {
9 |
10 | /**
11 | * CSS-class for swf wrapper.
12 | * @protected
13 | * @default femb-swf-wrapper
14 | * @type String
15 | */
16 | __SWF_WRAPPER_CLASS: 'femb-swf-wrapper',
17 |
18 | /**
19 | * Timeout for flash block detect
20 | * @default 500
21 | * @protected
22 | * @type Number
23 | */
24 | __TIMEOUT: 500,
25 |
26 | /**
27 | * Embed SWF info page. This function has same options as swfobject.embedSWF
28 | * @see http://code.google.com/p/swfobject/wiki/api
29 | * @param swfUrlStr
30 | * @param replaceElemIdStr
31 | * @param widthStr
32 | * @param heightStr
33 | * @param swfVersionStr
34 | * @param xiSwfUrlStr
35 | * @param flashvarsObj
36 | * @param parObj
37 | * @param attObj
38 | * @param callbackFn
39 | */
40 | embedSWF: function(
41 | swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj,
42 | parObj, attObj, callbackFn
43 | ) {
44 | swfobject.addDomLoadEvent(function() {
45 | var replaceElement = document.getElementById(replaceElemIdStr);
46 | if (!replaceElement) {
47 | return;
48 | }
49 |
50 | // We need to create div-wrapper because some flash block plugins replace swf with another content.
51 | // Also some flash requires wrapper to work properly.
52 | var wrapper = document.createElement('div');
53 | wrapper.className = FlashEmbedder.__SWF_WRAPPER_CLASS;
54 |
55 | replaceElement.parentNode.replaceChild(wrapper, replaceElement);
56 | wrapper.appendChild(replaceElement);
57 |
58 | swfobject.embedSWF(swfUrlStr,
59 | replaceElemIdStr,
60 | widthStr,
61 | heightStr,
62 | swfVersionStr,
63 | xiSwfUrlStr,
64 | flashvarsObj,
65 | parObj,
66 | attObj,
67 | function(e) {
68 | // e.success === false means that browser don't have flash or flash is too old
69 | // @see http://code.google.com/p/swfobject/wiki/api
70 | if (!e || e.success === false) {
71 | callbackFn(e);
72 | } else {
73 | var swfElement = e['ref'];
74 | // Opera 11.5 and above replaces flash with SVG button
75 | // msie (and canary chrome 32.0) crashes on swfElement['getSVGDocument']()
76 | var replacedBySVG = false;
77 | try {
78 | replacedBySVG = swfElement && swfElement['getSVGDocument']
79 | && swfElement['getSVGDocument']();
80 | } catch(err) {
81 | }
82 | if (replacedBySVG) {
83 | onFailure(e);
84 |
85 | } else {
86 | //set timeout to let FlashBlock plugin detect swf and replace it some contents
87 | window.setTimeout(function() {
88 | callbackFn(e);
89 | }, FlashEmbedder.__TIMEOUT);
90 | }
91 | }
92 |
93 | function onFailure(e) {
94 | e.success = false;
95 | callbackFn(e);
96 | }
97 | });
98 | });
99 | }
100 | };
101 |
102 | module.exports = FlashEmbedder;
103 |
--------------------------------------------------------------------------------
/src/lib/class/proxy.js:
--------------------------------------------------------------------------------
1 | var Events = require('../async/events');
2 |
3 | //THINK: изучить как работает ES 2015 Proxy и попробовать использовать
4 |
5 | /**
6 | * @classdesc Прокси-класс. Выдаёт наружу лишь публичные методы объекта и статические свойства.
7 | * Не копирует методы из Object.prototype. Все методы имеют привязку контекста к проксируемому объекту.
8 | *
9 | * @param {Object} [object] - объект, который требуется проксировать
10 | * @constructor
11 | * @private
12 | */
13 | var Proxy = function(object) {
14 | if (object) {
15 | for (var key in object) {
16 | if (key[0] === "_"
17 | || typeof object[key] !== "function"
18 | || object[key] === Object.prototype[key]
19 | || object.hasOwnProperty(key)
20 | || Events.prototype.hasOwnProperty(key)) {
21 | continue;
22 | }
23 |
24 | this[key] = object[key].bind(object);
25 | }
26 |
27 | if (object.pipeEvents) {
28 | Events.call(this);
29 |
30 | this.on = Events.prototype.on;
31 | this.once = Events.prototype.once;
32 | this.off = Events.prototype.off;
33 | this.clearListeners = Events.prototype.clearListeners;
34 |
35 | object.pipeEvents(this);
36 | }
37 | }
38 | };
39 |
40 | /**
41 | * Экспортирует статические свойства из одного объекта в другой, исключая указанные, приватные и прототип
42 | * @param {Object} from - откуда копировать
43 | * @param {Object} to - куда копировать
44 | * @param {Array.} [exclude] - свойства которые требуется исключить
45 | */
46 | Proxy.exportStatic = function(from, to, exclude) {
47 | exclude = exclude || [];
48 |
49 | Object.keys(from).forEach(function(key) {
50 | if (!from.hasOwnProperty(key)
51 | || key[0] === "_"
52 | || key === "prototype"
53 | || exclude.indexOf(key) !== -1) {
54 | return;
55 | }
56 |
57 | to[key] = from[key];
58 | });
59 | };
60 |
61 | /**
62 | * Создание прокси-пласса привязанного к указанному классу. Можно назначить родительский класс.
63 | * У родительского класса появляется приватный метод _proxy, который выдаёт прокси-объект для
64 | * данного экземляра. Также появляется свойство __proxy, содержащее ссылку на созданный прокси-объект
65 | *
66 | * @param {function} OriginalClass - оригинальный класс
67 | * @param {function} [ParentProxyClass=Proxy] - родительский класс
68 | * @returns {function} -- конструтор проксированного класса
69 | */
70 | Proxy.createClass = function(OriginalClass, ParentProxyClass, excludeStatic) {
71 |
72 | var ProxyClass = function() {
73 | var OriginalClassConstructor = function() {};
74 | OriginalClassConstructor.prototype = OriginalClass.prototype;
75 |
76 | var original = new OriginalClassConstructor();
77 | OriginalClass.apply(original, arguments);
78 |
79 | return original._proxy();
80 | };
81 |
82 | var ParentProxyClassConstructor = function() {};
83 | ParentProxyClassConstructor.prototype = (ParentProxyClass || Proxy).prototype;
84 | ProxyClass.prototype = new ParentProxyClassConstructor();
85 |
86 | var val;
87 | for (var k in OriginalClass.prototype) {
88 | val = OriginalClass.prototype[k];
89 | if (Object.prototype[k] == val || typeof val === "function" || k[0] === "_") {
90 | continue;
91 | }
92 | ProxyClass.prototype[k] = val;
93 | }
94 |
95 | var createProxy = function(original) {
96 | var proto = Proxy.prototype;
97 | Proxy.prototype = ProxyClass.prototype;
98 | var proxy = new Proxy(original);
99 | Proxy.prototype = proto;
100 | return proxy;
101 | };
102 |
103 | OriginalClass.prototype._proxy = function() {
104 | if (!this.__proxy) {
105 | this.__proxy = createProxy(this);
106 | }
107 |
108 | return this.__proxy;
109 | };
110 |
111 | if (!excludeStatic) {
112 | Proxy.exportStatic(OriginalClass, ProxyClass);
113 | }
114 |
115 | return ProxyClass;
116 | };
117 |
118 | module.exports = Proxy;
119 |
--------------------------------------------------------------------------------
/spec/Equalizer.md:
--------------------------------------------------------------------------------
1 | ## *class* Equalizer
2 |
3 | **Доступен извне как:** `ya.music.Audio.fx.Equalizer`
4 |
5 | Эквалайзер.
6 |
7 | **Расширяет:**
8 |
9 | - [Events](Events.md#Events)
10 |
11 | **Триггерит:**
12 |
13 | - [Equalizer.EVENT_CHANGE](Equalizer.md#Equalizer.EVENT_CHANGE)
14 |
15 | #### new Equalizer(audioContext: AudioContext, bands: Array.< Number >)
16 |
17 | | Имя | Тип | * | Описание |
18 | | --- | --- | --- | --- |
19 | | audioContext | AudioContext | | Контекст Web Audio API. |
20 | | bands | Array.< Number > | | Список частот для полос эквалайзера. |
21 |
22 | #### Equalizer.DEFAULT_BANDS : Array.< Number >
23 |
24 | Набор частот эквалайзера, применяющийся по умолчанию.
25 |
26 | #### Equalizer.DEFAULT_PRESETS : Object.< String, [EqualizerPreset](Equalizer.md#EqualizerPreset) >
27 |
28 | Набор распространенных пресетов эквалайзера для набора частот по умолчанию.
29 |
30 | ----
31 |
32 | ### События
33 |
34 | #### *event* Equalizer.EVENT_CHANGE
35 |
36 | Событие изменения полосы пропускания
37 |
38 | | Имя | Тип | * | Описание |
39 | | --- | --- | --- | --- |
40 | | freq | Number | | Частота полосы пропускания. |
41 | | value | Number | | Значение усиления. |
42 |
43 | ----
44 |
45 | ### Методы
46 |
47 | #### Equalizer#loadPreset (preset: [EqualizerPreset](Equalizer.md#EqualizerPreset))
48 |
49 | Загрузить настройки.
50 |
51 | | Имя | Тип | * | Описание |
52 | | --- | --- | --- | --- |
53 | | preset | [EqualizerPreset](Equalizer.md#EqualizerPreset) | | Настройки. |
54 |
55 | #### Equalizer#savePreset () : [EqualizerPreset](Equalizer.md#EqualizerPreset)
56 |
57 | Сохранить текущие настройки.
58 |
59 | #### Equalizer#guessPreamp () : number
60 |
61 | Вычисляет оптимальное значение предусиления. Функция является экспериментальной.
62 |
63 | > **Возвращает:** значение предусиления.
64 |
65 | #### Equalizer#on (event: String, callback: function) : [Events](Events.md#Events) *(inherits [Events#on](Events.md#Events..on))*
66 |
67 | Подписаться на событие (цепочный метод).
68 |
69 | | Имя | Тип | * | Описание |
70 | | --- | --- | --- | --- |
71 | | event | String | | Имя события. |
72 | | callback | function | | Обработчик события. |
73 |
74 | > **Возвращает:** ссылку на контекст.
75 |
76 | #### Equalizer#off (event: String, callback: function) : [Events](Events.md#Events) *(inherits [Events#off](Events.md#Events..off))*
77 |
78 | Отписаться от события (цепочный метод).
79 |
80 | | Имя | Тип | * | Описание |
81 | | --- | --- | --- | --- |
82 | | event | String | | Имя события. |
83 | | callback | function | | Обработчик события. |
84 |
85 | > **Возвращает:** ссылку на контекст.
86 |
87 | #### Equalizer#once (event: String, callback: function) : [Events](Events.md#Events) *(inherits [Events#once](Events.md#Events..once))*
88 |
89 | Подписаться на событие и отписаться сразу после его первого возникновения (цепочный метод).
90 |
91 | | Имя | Тип | * | Описание |
92 | | --- | --- | --- | --- |
93 | | event | String | | Имя события. |
94 | | callback | function | | Обработчик события. |
95 |
96 | > **Возвращает:** ссылку на контекст.
97 |
98 | #### Equalizer#clearListeners () : [Events](Events.md#Events) *(inherits [Events#clearListeners](Events.md#Events..clearListeners))*
99 |
100 | Отписаться от всех слушателей событий (цепочный метод).
101 |
102 | > **Возвращает:** ссылку на контекст.
103 |
104 | #### Equalizer#muteEvents () : [Events](Events.md#Events) *(inherits [Events#muteEvents](Events.md#Events..muteEvents))*
105 |
106 | Остановить запуск событий (цепочный метод).
107 |
108 | > **Возвращает:** ссылку на контекст.
109 |
110 | #### Equalizer#unmuteEvents () : [Events](Events.md#Events) *(inherits [Events#unmuteEvents](Events.md#Events..unmuteEvents))*
111 |
112 | Возобновить запуск событий (цепочный метод).
113 |
114 | > **Возвращает:** ссылку на контекст.
115 |
116 | ----
117 |
118 | ### Типы
119 |
120 | #### *type* EqualizerPreset : Object
121 |
122 | Описание настроек эквалайзера.
123 |
124 | | Имя | Тип | * | Описание |
125 | | --- | --- | --- | --- |
126 | | *\[id\]* | String | | Идентификатор настроек. |
127 | | preamp | Number | | Предусилитель. |
128 | | bands | Array.< Number > | | Значения для полос эквалайзера. |
129 |
130 |
--------------------------------------------------------------------------------
/src/flash/src/AudioManager.as:
--------------------------------------------------------------------------------
1 | package {
2 | import flash.display.Sprite;
3 | import flash.display.StageAlign;
4 | import flash.display.StageScaleMode;
5 | import flash.events.MouseEvent;
6 | import flash.external.ExternalInterface;
7 | import flash.media.SoundMixer;
8 | import flash.media.SoundTransform;
9 | import flash.system.Security;
10 |
11 | [SWF(width="10", height="10")]
12 | public final class AudioManager extends Sprite {
13 | private static var players:Array/*AudioFlash*/ = [];
14 | private static var volume:Number = 1;
15 |
16 | public static const EVENT_INIT:String = "init";
17 | public static const EVENT_CLICK:String = "click";
18 | public static const EVENT_FAIL:String = "failed";
19 |
20 | private static const FLASH2JS_CALLBACK:String = "Audio._flashCallback";
21 | private static const JS2FLASH_CALLBACK:String = "call";
22 |
23 | public function AudioManager() {
24 | Security.allowDomain("*");
25 |
26 | try {
27 | ExternalInterface.addCallback(JS2FLASH_CALLBACK, this[JS2FLASH_CALLBACK]);
28 | } catch (error:Error) {
29 | jsCall(EVENT_FAIL, -1, -1, error.message);
30 | }
31 |
32 | this.addEventListener(MouseEvent.CLICK, onClick);
33 |
34 | stage.scaleMode = StageScaleMode.EXACT_FIT;
35 | stage.align = StageAlign.TOP_LEFT;
36 |
37 | var overlay:Sprite = new Sprite();
38 | overlay.x = 0;
39 | overlay.y = 0;
40 | overlay.useHandCursor = true;
41 | overlay.buttonMode = true;
42 | overlay.graphics.beginFill(0, 0);
43 | overlay.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
44 | overlay.graphics.endFill();
45 | this.addChild(overlay);
46 |
47 | jsCall(EVENT_INIT);
48 | }
49 |
50 | private static function onClick(event:MouseEvent):void {
51 | jsCall(EVENT_CLICK);
52 | }
53 |
54 | private static function jsCall(eventName:String, id:int = -1, offset:int = -1, error:String = ""):void {
55 | try {
56 | ExternalInterface.call(FLASH2JS_CALLBACK, eventName, id, offset, error);
57 | } catch (error:Error) {
58 | trace(error.message);
59 | }
60 | }
61 |
62 | private static function delegateEvent(event:AudioEvent):void {
63 | var player:int = players.indexOf(event.currentTarget);
64 | jsCall(event.type, player, event.offset, event.data);
65 | }
66 |
67 | public function call(methodName:String, id:int = -1, ...args):* {
68 | try {
69 | if (id !== -1) {
70 | var player:AudioPlayer = players[id];
71 | if (!player) {
72 | jsCall(AudioEvent.EVENT_ERROR, id, -1, "no such player");
73 | return null;
74 | }
75 |
76 | if (typeof player[methodName] === "function") {
77 | return player[methodName].apply(player, args);
78 | } else {
79 | jsCall(AudioEvent.EVENT_ERROR, id, -1, "no such method");
80 | return null;
81 | }
82 | } else {
83 | if (typeof this["export_" + methodName] === "function") {
84 | return this["export_" + methodName].apply(this, args);
85 | } else {
86 | jsCall(AudioEvent.EVENT_ERROR, id, -1, "no such method");
87 | return null;
88 | }
89 | }
90 | } catch (e:Error) {
91 | jsCall(AudioEvent.EVENT_ERROR, id, args[0], e.message);
92 | return null;
93 | }
94 | }
95 |
96 | private function export_addPlayer():uint {
97 | var player:AudioPlayer = new AudioPlayer(players.length);
98 |
99 | player.addEventListener(AudioEvent.EVENT_PLAY, delegateEvent);
100 | player.addEventListener(AudioEvent.EVENT_PAUSE, delegateEvent);
101 | player.addEventListener(AudioEvent.EVENT_ENDED, delegateEvent);
102 | player.addEventListener(AudioEvent.EVENT_PROGRESS, delegateEvent);
103 | player.addEventListener(AudioEvent.EVENT_ERROR, delegateEvent);
104 | player.addEventListener(AudioEvent.EVENT_STOP, delegateEvent);
105 | player.addEventListener(AudioEvent.EVENT_LOADED, delegateEvent);
106 | player.addEventListener(AudioEvent.EVENT_LOADING, delegateEvent);
107 |
108 | player.addEventListener(AudioEvent.EVENT_DEBUG, delegateEvent);
109 |
110 | players.push(player);
111 | return player.id;
112 | }
113 |
114 | private function export_setVolume(value:Number):void {
115 | value = Math.max(0, Math.min(1, value));
116 | if (0 < value && value < 0.01) {
117 | value = 0.01;
118 | }
119 | volume = value;
120 | SoundMixer.soundTransform = new SoundTransform(value, 0);
121 | jsCall(AudioEvent.EVENT_VOLUME);
122 | }
123 |
124 | private function export_getVolume():Number {
125 | return volume;
126 | }
127 |
128 | private function export_heartBeat():Boolean {
129 | return true;
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/fx/equalizer/equalizer.js:
--------------------------------------------------------------------------------
1 | var Events = require('../../lib/async/events');
2 | var merge = require('../../lib/data/merge');
3 |
4 | var EqualizerStatic = require('./equalizer-static');
5 | var EqualizerBand = require('./equalizer-band');
6 |
7 | /**
8 | * Описание настроек эквалайзера.
9 | * @typedef {Object} Equalizer~EqualizerPreset
10 | * @property {String} [id] Идентификатор настроек.
11 | * @property {Number} preamp Предусилитель.
12 | * @property {Array.} bands Значения для полос эквалайзера.
13 | */
14 |
15 | /**
16 | * Событие изменения полосы пропускания.
17 | * @event Equalizer.EVENT_CHANGE
18 | *
19 | * @param {Number} freq Частота полосы пропускания.
20 | * @param {Number} value Значение усиления.
21 | */
22 |
23 | // =================================================================
24 |
25 | // Конструктор
26 |
27 | // =================================================================
28 |
29 | /**
30 | * @classdesc Эквалайзер.
31 | * @exported ya.music.Audio.fx.Equalizer
32 | *
33 | * @param {AudioContext} audioContext Контекст Web Audio API.
34 | * @param {Array.} bands Список частот для полос эквалайзера.
35 | *
36 | * @extends Events
37 | *
38 | * @fires Equalizer.EVENT_CHANGE
39 | *
40 | * @constructor
41 | */
42 | var Equalizer = function(audioContext, bands) {
43 | Events.call(this);
44 |
45 | this.preamp = new EqualizerBand(audioContext, "highshelf", 0);
46 | this.preamp.on("*", this._onBandEvent.bind(this, this.preamp));
47 |
48 | bands = bands || Equalizer.DEFAULT_BANDS;
49 |
50 | var prev;
51 | this.bands = bands.map(function(frequency, idx) {
52 | var band = new EqualizerBand(
53 | audioContext,
54 |
55 | idx == 0 ? 'lowshelf'
56 | : idx + 1 < bands.length ? "peaking"
57 | : "highshelf",
58 |
59 | frequency
60 | );
61 | band.on("*", this._onBandEvent.bind(this, band));
62 |
63 | if (!prev) {
64 | this.preamp.filter.connect(band.filter);
65 | } else {
66 | prev.filter.connect(band.filter);
67 | }
68 |
69 | prev = band;
70 | return band;
71 | }.bind(this));
72 |
73 | this.input = this.preamp.filter;
74 | this.output = this.bands[this.bands.length - 1].filter;
75 | };
76 | Events.mixin(Equalizer);
77 | merge(Equalizer, EqualizerStatic, true);
78 |
79 | // =================================================================
80 |
81 | // Настройки по-умолчанию
82 |
83 | // =================================================================
84 |
85 | /**
86 | * Набор частот эквалайзера, применяющийся по умолчанию.
87 | * @type {Array.}
88 | * @const
89 | */
90 | Equalizer.DEFAULT_BANDS = require('./default.bands.js');
91 |
92 | /**
93 | * Набор распространенных пресетов эквалайзера для набора частот по умолчанию.
94 | * @type {Object.}
95 | * @const
96 | */
97 | Equalizer.DEFAULT_PRESETS = require('./default.presets.js');
98 |
99 | // =================================================================
100 |
101 | // Обработка событий
102 |
103 | // =================================================================
104 |
105 | /**
106 | * Обработка события полосы эквалайзера.
107 | * @param {EqualizerBand} band - полоса эквалайзера
108 | * @param {String} event - событие
109 | * @param {*} data - данные события
110 | * @private
111 | */
112 | Equalizer.prototype._onBandEvent = function(band, event, data) {
113 | this.trigger(event, band.getFreq(), data);
114 | };
115 |
116 | // =================================================================
117 |
118 | // Загрузка и сохранение настроек
119 |
120 | // =================================================================
121 |
122 | /**
123 | * Загрузить настройки.
124 | * @param {Equalizer~EqualizerPreset} preset Настройки.
125 | */
126 | Equalizer.prototype.loadPreset = function(preset) {
127 | preset.bands.forEach(function(value, idx) {
128 | this.bands[idx].setValue(value);
129 | }.bind(this));
130 | this.preamp.setValue(preset.preamp);
131 | };
132 |
133 | /**
134 | * Сохранить текущие настройки.
135 | * @returns {Equalizer~EqualizerPreset}
136 | */
137 | Equalizer.prototype.savePreset = function() {
138 | return {
139 | preamp: this.preamp.getValue(),
140 | bands: this.bands.map(function(band) { return band.getValue(); })
141 | };
142 | };
143 |
144 | // =================================================================
145 |
146 | // Математика
147 |
148 | // =================================================================
149 |
150 | //TODO: проверить предположение (скорее всего нужна карта весов для различных частот или даже некая функция)
151 | /**
152 | * Вычисляет оптимальное значение предусиления. Функция является экспериментальной.
153 | * @experimental
154 | * @returns {number} значение предусиления.
155 | */
156 | Equalizer.prototype.guessPreamp = function() {
157 | var v = 0;
158 | for (var k = 0, l = this.bands.length; k < l; k++) {
159 | v += this.bands[k].getValue();
160 | }
161 |
162 | return -v / 2;
163 | };
164 |
165 | module.exports = Equalizer;
166 |
--------------------------------------------------------------------------------
/test/web-audio-api/index.js:
--------------------------------------------------------------------------------
1 | var template = document.querySelector(".template").innerHTML;
2 | document.body.removeChild(document.querySelector(".template"));
3 |
4 | var WIDTH = 250;
5 | var HEIGHT = 150;
6 | var GUTTER = 2;
7 |
8 | var INNER_HEIGHT = HEIGHT - GUTTER * 2;
9 | var INNER_WIDTH = WIDTH - GUTTER * 2;
10 |
11 | var MIN_FREQ = 20;
12 | var MAX_FREQ = 20000;
13 | var FREQ_STEPS = INNER_WIDTH + 1;
14 |
15 | var A = Math.exp((Math.log(MAX_FREQ) - Math.log(MIN_FREQ)) / (FREQ_STEPS - 1));
16 | var B = Math.log(MIN_FREQ) / Math.log(A);
17 |
18 | var FILTER_LINE = 0.5;
19 | var MIN_DB = -2;
20 | var MAX_DB = 2;
21 |
22 | var norm = function(points, min, max) {
23 | var k = INNER_HEIGHT / (max - min) || 1;
24 | var dy = min * k;
25 | var step = INNER_WIDTH / (points.length - 1);
26 |
27 | return points
28 | .map(function(v) { return INNER_HEIGHT - ((v || 0) * k - dy); })
29 | .map(function(v, idx) { return (GUTTER + idx * step) + " " + (GUTTER + (v || 0)); });
30 | };
31 |
32 | var drawTest = function(name, data) {
33 | var testBlock = document.createElement("DIV");
34 | testBlock.classList.add("test");
35 |
36 | var id = name.replace(/[ =.]/g, "_");
37 |
38 | testBlock.innerHTML = template
39 | .replace("{{title}}", name)
40 | .replace("{{name}}", id);
41 | document.body.appendChild(testBlock);
42 |
43 | var canvas = Raphael(id, WIDTH, HEIGHT);
44 |
45 | var h = "0 " + (GUTTER + INNER_HEIGHT / 2);
46 | var v = (GUTTER + FILTER_LINE * INNER_WIDTH) + " 0";
47 |
48 | canvas.path().attr({
49 | path: "M" + v + "L" + v + " l0 " + HEIGHT,
50 | stroke: "#ff0000",
51 | "stroke-width": 1,
52 | "stroke-linejoin": "round"
53 | });
54 |
55 | canvas.path().attr({
56 | path: "M" + h + "L" + h + " l" + WIDTH + " 0",
57 | stroke: "#000",
58 | "stroke-width": 1,
59 | "stroke-linejoin": "round"
60 | });
61 |
62 | var draw = function(path, color, width) {
63 | canvas.path().attr({
64 | path: "M" + path[0] + " L" + path.join(" "),
65 | stroke: color,
66 | "stroke-width": 1 + width,
67 | "stroke-linejoin": "round"
68 | });
69 | };
70 |
71 | draw(norm(data[1], -Math.PI, Math.PI), "#5B9A1C", 3);
72 | draw(norm(data[0], 0, MAX_DB), "#134b9a", 3);
73 | };
74 |
75 | var audioCtx = new AudioContext();
76 | var freqs = new Float32Array(FREQ_STEPS);
77 |
78 | for (var k = 0; k < FREQ_STEPS; k++) {
79 | freqs[k] = Math.pow(A, B + k);
80 | }
81 |
82 | var FILTER_FREQ = freqs[Math.floor(FREQ_STEPS * FILTER_LINE)];
83 | console.log(FILTER_FREQ);
84 |
85 | var testFilter = function(options, phase) {
86 | return function() {
87 | var filter = audioCtx.createBiquadFilter();
88 | for (var key in options) {
89 | if (options.hasOwnProperty(key)) {
90 | if (key !== "type") {
91 | filter[key].value = options[key];
92 | } else {
93 | filter[key] = options[key];
94 | }
95 | }
96 | }
97 |
98 | var resFreq = new Float32Array(FREQ_STEPS);
99 | var resPhase = new Float32Array(FREQ_STEPS);
100 |
101 | filter.frequency.value = FILTER_FREQ;
102 |
103 | filter.getFrequencyResponse(freqs, resFreq, resPhase);
104 |
105 | resFreq.map = resPhase.map = Array.prototype.map;
106 | return [resFreq, resPhase];
107 | }
108 | };
109 |
110 | var tests = {
111 | "lowpass Q=0.2": testFilter({type: "lowpass", Q: 0.2}),
112 | "lowpass Q=1": testFilter({type: "lowpass", Q: 1}),
113 | "lowpass Q=5": testFilter({type: "lowpass", Q: 5}),
114 |
115 | "highpass Q=0.2": testFilter({type: "highpass", Q: 0.2}),
116 | "highpass Q=1": testFilter({type: "highpass", Q: 1}),
117 | "highpass Q=5": testFilter({type: "highpass", Q: 5}),
118 |
119 | "bandpass Q=0.2": testFilter({type: "bandpass", Q: 0.2}),
120 | "bandpass Q=1": testFilter({type: "bandpass", Q: 1}),
121 | "bandpass Q=5": testFilter({type: "bandpass", Q: 5}),
122 |
123 | "notch Q=0.2": testFilter({type: "notch", Q: 0.2}),
124 | "notch Q=1": testFilter({type: "notch", Q: 1}),
125 | "notch Q=5": testFilter({type: "notch", Q: 5}),
126 |
127 | "peaking gain=6 Q=0.2": testFilter({type: "peaking", Q: 0.2, gain: 6}),
128 | "peaking gain=6 Q=1": testFilter({type: "peaking", Q: 1, gain: 6}),
129 | "peaking gain=6 Q=5": testFilter({type: "peaking", Q: 5, gain: 6}),
130 |
131 | "peaking gain=-6 Q=0.2": testFilter({type: "peaking", Q: 0.2, gain: -6}),
132 | "peaking gain=-6 Q=1": testFilter({type: "peaking", Q: 1, gain: -6}),
133 | "peaking gain=-6 Q=5": testFilter({type: "peaking", Q: 5, gain: -6}),
134 |
135 | "allpass Q=0.2": testFilter({type: "allpass", Q: 0.2}),
136 | "allpass Q=1": testFilter({type: "allpass", Q: 1}),
137 | "allpass Q=5": testFilter({type: "allpass", Q: 5}),
138 |
139 | "lowshelf gain=6": testFilter({type: "lowshelf", gain: 6}),
140 | "lowshelf gain=0": testFilter({type: "lowshelf", gain: 0}),
141 | "lowshelf gain=-6": testFilter({type: "lowshelf", gain: -6}),
142 |
143 | "highshelf gain=6": testFilter({type: "highshelf", gain: 6}),
144 | "highshelf gain=0": testFilter({type: "highshelf", gain: 0}),
145 | "highshelf gain=-6": testFilter({type: "highshelf", gain: -6})
146 | };
147 |
148 | Object.keys(tests).forEach(function(testname) {
149 | drawTest(testname, tests[testname]());
150 | });
151 |
--------------------------------------------------------------------------------
/src/lib/browser/detect.js:
--------------------------------------------------------------------------------
1 | var ua = navigator.userAgent.toLowerCase();
2 |
3 | // =================================================================
4 |
5 | // Получение данных о браузере
6 |
7 | // =================================================================
8 |
9 | // Useragent RegExp
10 | var ruc = /(ucbrowser)\/([\w.]+)/;
11 | var rwebkit = /(webkit)[ \/]([\w.]+)/;
12 | var ryabro = /(yabrowser)[ \/]([\w.]+)/;
13 | var ropera = /(opr|opera)(?:.*version)?[ \/]([\w.]+)/;
14 | var rmsie = /(msie) ([\w.]+)/;
15 | var redge = /(edge)\/([\w.]+)/;
16 | var rmmsie = /(iemobile)\/([\d\.]+)/;
17 | var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
18 | var rsafari = /^((?!chrome).)*version\/([\d\w\.]+).*(safari)/;
19 |
20 | // Порядок очень важен:
21 | // у Ya, Opera и Edge есть и chrome и safari, но не факт, что одновременно,
22 | // поэтому чекаем их первыми, что бы избежать false positive
23 | // у хрома есть сафари, но у сафари нет хрома, поэтому сафари идет первым
24 | // хром считаем неким абстрактным вебкит-браузером, точнее никак, много притворяющихся
25 | var match = ruc.exec(ua)
26 | || ryabro.exec(ua)
27 | || ropera.exec(ua)
28 | || redge.exec(ua)
29 | || rsafari.exec(ua)
30 | || rmmsie.exec(ua)
31 | || rwebkit.exec(ua)
32 | || rmsie.exec(ua)
33 | || ua.indexOf("compatible") < 0 && rmozilla.exec(ua)
34 | || [];
35 |
36 | var browser = {name: match[1] || "", version: match[2] || "0"};
37 |
38 | if (match[3] === "safari") {
39 | browser.name = match[3];
40 | // Сафари четче различать по версии вебкита, а не по маркетинговой
41 | browser.version = rwebkit.exec(ua)[2]
42 | }
43 |
44 | if (browser.name === 'msie') {
45 | if (document.documentMode) { // IE8 or later
46 | browser.documentMode = document.documentMode;
47 | } else { // IE 5-7
48 | browser.documentMode = 5; // Assume quirks mode unless proven otherwise
49 | if (document.compatMode) {
50 | if (document.compatMode === "CSS1Compat") {
51 | browser.documentMode = 7; // standards mode
52 | }
53 | }
54 | }
55 | }
56 |
57 | if (browser.name === "opr") {
58 | browser.name = "opera";
59 | }
60 |
61 | //INFO: IE (как всегда) не корректно выставляет user-agent
62 | if (browser.name === "mozilla" && browser.version.split(".")[0] === "11") {
63 | browser.name = "msie";
64 | }
65 |
66 | // =================================================================
67 |
68 | // Получение данных о платформе
69 |
70 | // =================================================================
71 |
72 | // Useragent RegExp
73 | var rplatform = /(windows phone|ipad|iphone|ipod|android|blackberry|playbook|windows ce)/;
74 | var rtablet = /(ipad|playbook)/;
75 | var randroid = /(android)/;
76 | var rmobile = /(mobile)/;
77 | var rtv = /(netcast|web[0o]s|nettv|netrange|sharp|smart-tv)/;
78 |
79 | platform = rplatform.exec(ua) || [];
80 | var tablet = rtablet.exec(ua) || !rmobile.exec(ua) && randroid.exec(ua) || [];
81 | var tv = rtv.exec(ua) || (!!window.tizen ? [null, 'tizen'] : false) || (typeof window.sony == 'object' && window.sony.tv ? [null, 'sony'] : false) || [];
82 |
83 | if (platform[1]) {
84 | platform[1] = platform[1].replace(/\s/g, "_"); // Change whitespace to underscore. Enables dot notation.
85 | }
86 |
87 | if (tv[1] == 'web0s') {
88 | tv[1] = 'webos';
89 | }
90 |
91 | var platform = {
92 | type: platform[1] || "",
93 | tablet: !!tablet[1],
94 | tv: !!tv[1],
95 | mobile: platform[1] && !tablet[1] || false
96 | };
97 | if (!!tv[1]) {
98 | platform.type = tv[1];
99 | }
100 | if (!platform.type) {
101 | platform.type = 'pc';
102 | }
103 |
104 | platform.os = platform.type;
105 | if (platform.type === 'ipad' || platform.type === 'iphone' || platform.type === 'ipod') {
106 | platform.os = 'ios';
107 | } else if (platform.type === 'android') {
108 | platform.os = 'android';
109 | } else if (platform.type === "windows phone" || navigator.appVersion.indexOf("Win") !== -1) {
110 | platform.os = "windows";
111 | platform.version = navigator.userAgent.match(/win[^ ]* ([^;]*)/i);
112 | platform.version = platform.version && platform.version[1];
113 | } else if (navigator.appVersion.indexOf("Mac") !== -1) {
114 | platform.os = "macos";
115 | } else if (navigator.appVersion.indexOf("X11") !== -1) {
116 | platform.os = "unix";
117 | } else if (navigator.appVersion.indexOf("Linux") !== -1) {
118 | platform.os = "linux";
119 | }
120 |
121 | // =================================================================
122 |
123 | // Получение данных о возможности менять громкость
124 |
125 | // =================================================================
126 | var noVolume = true;
127 | try {
128 | var audio = document.createElement('audio');
129 | audio.volume = 0.63;
130 | noVolume = Math.abs(audio.volume - 0.63) > 0.01;
131 | } catch(e) {
132 | noVolume = true;
133 | }
134 |
135 | /**
136 | * Информация об окружении.
137 | * @namespace
138 | * @exported ya.music.info
139 | */
140 | var info = {
141 | /**
142 | * Информация о браузере.
143 | * @namespace
144 | * @property {string} name Название браузера.
145 | * @property {string} version Версия.
146 | * @property {number} [documentMode] Версия документа (для IE).
147 | */
148 | browser: browser,
149 |
150 | /**
151 | * Информация о платформе.
152 | * @namespace
153 | * @property {string} os Тип операционной системы.
154 | * @property {string} type Тип платформы.
155 | * @property {Boolean} tablet Планшет.
156 | * @property {Boolean} mobile Мобильный.
157 | * @property {boolean} tv Телевизор
158 | */
159 | platform: platform,
160 |
161 | /**
162 | * Настройка громкости.
163 | * @type {Boolean}
164 | */
165 | onlyDeviceVolume: noVolume
166 | };
167 |
168 | module.exports = info;
169 |
--------------------------------------------------------------------------------
/examples/quick-start/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Теперь инициализируем всю эту структуру и создадим экземпляр плеера.
4 | var AudioPlayer = ya.music.Audio;
5 | AudioPlayer.config.flash.path = "../../dist";
6 |
7 | var dom = {
8 | player: document.querySelector(".player"),
9 |
10 | play: document.querySelector(".controls_play"),
11 |
12 | progress: {
13 | bar: document.querySelector(".progress"),
14 | loaded: document.querySelector(".progress_loaded"),
15 | current: document.querySelector(".progress_current")
16 | },
17 |
18 | volume: {
19 | bar: document.querySelector(".volume"),
20 | value: document.querySelector(".volume_bar")
21 | },
22 |
23 | overlay: document.querySelector(".overlay")
24 | };
25 |
26 | // Предоставим плееру самому решать, какой тип реализации использовать.
27 | var audioPlayer = new AudioPlayer(null, dom.overlay);
28 |
29 | audioPlayer.initPromise().then(function() {
30 | // Скрываем оверлей, кнопки управления становятся доступными.
31 | dom.overlay.classList.add("overlay_hidden");
32 | }, function(err) {
33 | // Показываем ошибку инициализации в оверлее.
34 | dom.overlay.innerHTML = err.message;
35 | dom.overlay.classList.add("overlay_error");
36 | });
37 |
38 | // Настроим отображение статуса плеера.
39 | // Для простого плеера нам достаточно знать, запущено воспроизведение или нет.
40 |
41 | audioPlayer.on(ya.music.Audio.EVENT_STATE, function(state) {
42 | if (state === ya.music.Audio.STATE_PLAYING) {
43 | dom.player.classList.add("player_playing");
44 | } else {
45 | dom.player.classList.remove("player_playing");
46 | }
47 | });
48 |
49 | /* Теперь настроим обновление прогресс-бара. В нем предусмотрены 2 шкалы - шкала загрузки и шкала текущей
50 | позиции воспроизведения. */
51 |
52 | audioPlayer.on(ya.music.Audio.EVENT_PROGRESS, function(timings) {
53 | dom.progress.loaded.style.width = (timings.loaded / timings.duration * 100).toFixed(2) + "%";
54 | dom.progress.current.style.width = (timings.position / timings.duration * 100).toFixed(2) + "%";
55 | });
56 |
57 | /* Аналогично будет работать шкала громкости */
58 |
59 | var updateVolume = function(volume) {
60 | dom.volume.value.style.height = (volume * 100).toFixed(2) + "%";
61 | };
62 | audioPlayer.on(ya.music.Audio.EVENT_VOLUME, updateVolume);
63 |
64 | // Отображаем начальную громкость
65 | audioPlayer.initPromise().then(function() {
66 | updateVolume(audioPlayer.getVolume());
67 | });
68 |
69 | /* Теперь нужно настроить взаимодействие с пользователем. Начнем с запуска воспроизведения. */
70 |
71 | var trackUrls = [
72 | "https://download.cdn.yandex.net/tech/ru/audio/doc/examples/files/audio_src/Tchaikovsky1.mp3",
73 | "https://download.cdn.yandex.net/tech/ru/audio/doc/examples/files/audio_src/Tchaikovsky2.mp3",
74 | "https://download.cdn.yandex.net/tech/ru/audio/doc/examples/files/audio_src/Tchaikovsky3.mp3"
75 | ];
76 |
77 | var trackIndex = 0;
78 |
79 | var startPlay = function() {
80 | var track = trackUrls[trackIndex];
81 | if (audioPlayer.isPreloaded(track)) {
82 | audioPlayer.playPreloaded(track);
83 | } else {
84 | audioPlayer.play(track);
85 | }
86 | };
87 |
88 | dom.play.addEventListener("click", function() {
89 | var state = audioPlayer.getState();
90 |
91 | switch (state) {
92 | case ya.music.Audio.STATE_PLAYING:
93 | audioPlayer.pause();
94 | break;
95 |
96 | case ya.music.Audio.STATE_PAUSED:
97 | audioPlayer.resume();
98 | break;
99 |
100 | default:
101 | startPlay();
102 | break;
103 | }
104 | });
105 |
106 | /* Добавим немножко удобства для пользователей: сделаем автозагрузку следующего трека после того, как текущий загрузился.
107 | Для этого потребуется немного изменить функцию `startPlay` и отслеживать момент загрузки трека. */
108 |
109 | audioPlayer.on(ya.music.Audio.EVENT_ENDED, function() {
110 | trackIndex++;
111 |
112 | if (trackIndex < trackUrls.length) {
113 | startPlay();
114 | }
115 | });
116 |
117 | audioPlayer.on(ya.music.Audio.EVENT_LOADED, function() {
118 | if (trackIndex + 1 < trackUrls.length) {
119 | audioPlayer.preload(trackUrls[trackIndex + 1]);
120 | }
121 | });
122 |
123 | /* Осталось только настроить навигацию по треку и регулирование громкости: */
124 |
125 | var offsetLeft = function(node) {
126 | var offset = node.offsetLeft;
127 | if (node.offsetParent) {
128 | offset += offsetLeft(node.offsetParent);
129 | }
130 | return offset;
131 | };
132 |
133 | var offsetTop = function(node) {
134 | var offset = node.offsetTop;
135 | if (node.offsetParent) {
136 | offset += offsetTop(node.offsetParent);
137 | }
138 | return offset;
139 | };
140 |
141 | dom.progress.bar.addEventListener("click", function(evt) {
142 | var fullWidth = dom.progress.bar.offsetWidth;
143 | var offset = offsetLeft(dom.progress.bar);
144 |
145 | var relativePosition = Math.max(0, Math.min(1, ((evt.pageX || evt.screenX) - offset) / fullWidth));
146 | var duration = audioPlayer.getDuration();
147 |
148 | audioPlayer.setPosition(duration * relativePosition);
149 | });
150 |
151 | dom.volume.bar.addEventListener("click", function(evt) {
152 | var fullHeight = dom.volume.bar.offsetHeight;
153 | var offset = offsetTop(dom.volume.bar);
154 |
155 | // тут мы делаем "1 -" т.к. громость принято отмерять снизу, а не сверху
156 | var volume = 1 - Math.max(0, Math.min(1, ((evt.pageY || evt.screenY) - offset) / fullHeight));
157 | audioPlayer.setVolume(volume);
158 | });
159 | })();
160 |
--------------------------------------------------------------------------------
/src/logger/logger.js:
--------------------------------------------------------------------------------
1 | var LEVELS = ["debug", "log", "info", "warn", "error", "trace"];
2 | var noop = require('../lib/noop');
3 |
4 | // =================================================================
5 |
6 | // Конструктор
7 |
8 | // =================================================================
9 |
10 | /**
11 | * @exported ya.music.Logger
12 | * @classdesc Настраиваемый логгер для аудиоплеера.
13 | * @param {String} channel Имя канала, за который будет отвечать экземляр логгера.
14 | * @constructor
15 | */
16 | var Logger = function(channel) {
17 | this.channel = channel;
18 | };
19 |
20 | // =================================================================
21 |
22 | // Настройки
23 |
24 | // =================================================================
25 |
26 | /**
27 | * Список игнорируемых каналов.
28 | * @type {Array.}
29 | */
30 | Logger.ignores = [];
31 |
32 | /**
33 | * Список отображаемых в консоли уровней лога.
34 | * @type {Array.}
35 | */
36 | Logger.logLevels = [];
37 |
38 | // =================================================================
39 |
40 | // Синтаксический сахар
41 |
42 | // =================================================================
43 |
44 | /**
45 | * Запись в лог с уровнем **debug**.
46 | * @param {Object} context Контекст вызова.
47 | * @param {...*} [args] Дополнительные аргументы.
48 | */
49 | Logger.prototype.debug = noop;
50 |
51 | /**
52 | * Запись в лог с уровнем **log**.
53 | * @param {Object} context Контекст вызова.
54 | * @param {...*} [args] Дополнительные аргументы.
55 | */
56 | Logger.prototype.log = noop;
57 |
58 | /**
59 | * Запись в лог с уровнем **info**.
60 | * @param {Object} context Контекст вызова.
61 | * @param {...*} [args] Дополнительные аргументы.
62 | */
63 | Logger.prototype.info = noop;
64 |
65 | /**
66 | * Запись в лог с уровнем **warn**.
67 | * @param {Object} context Контекст вызова.
68 | * @param {...*} [args] Дополнительные аргументы.
69 | */
70 | Logger.prototype.warn = noop;
71 |
72 | /**
73 | * Запись в лог с уровнем **error**.
74 | * @param {Object} context Контекст вызова.
75 | * @param {...*} [args] Дополнительные аргументы.
76 | */
77 | Logger.prototype.error = noop;
78 |
79 | /**
80 | * Запись в лог с уровнем **trace**.
81 | * @param {Object} context Контекст вызова.
82 | * @param {...*} [args] Дополнительные аргументы.
83 | */
84 | Logger.prototype.trace = noop;
85 |
86 | /**
87 | * Метод для обработки ссылок, передаваемых в лог.
88 | * @param url
89 | * @private
90 | */
91 | Logger.prototype._showUrl = function(url) {
92 | return Logger.showUrl(url);
93 | };
94 |
95 | /**
96 | * Метод для обработки ссылок, передаваемых в лог. Можно переопределять. По умолчанию не выполняет никаких действий.
97 | * @name ya.music.Audio.Logger#showUrl
98 | * @param {String} url Ссылка.
99 | * @returns {String} ссылку.
100 | */
101 | Logger.showUrl = function(url) {
102 | return url;
103 | };
104 |
105 | LEVELS.forEach(function(level) {
106 | Logger.prototype[level] = function() {
107 | var args = [].slice.call(arguments);
108 | args.unshift(this.channel);
109 | args.unshift(level);
110 | Logger.log.apply(Logger, args);
111 | };
112 | });
113 |
114 | // =================================================================
115 |
116 | // Запись данных в лог
117 |
118 | // =================================================================
119 |
120 | /**
121 | * Сделать запись в лог.
122 | * @param {String} level Уровень лога.
123 | * @param {String} channel Канал.
124 | * @param {Object} context Контекст вызова.
125 | * @param {...*} [args] Дополнительные аргументы.
126 | */
127 | Logger.log = function(level, channel, context) {
128 | var data = [].slice.call(arguments, 3).map(function(dumpItem) {
129 | return dumpItem && dumpItem._logger && dumpItem._logger() || dumpItem;
130 | });
131 |
132 | var logEntry = {
133 | timestamp: +new Date(),
134 | level: level,
135 | channel: channel,
136 | context: context,
137 | message: data
138 | };
139 |
140 | if (Logger.ignores[channel] || Logger.logLevels.indexOf(level) === -1) {
141 | return;
142 | }
143 |
144 | Logger._dumpEntry(logEntry);
145 | };
146 |
147 | /**
148 | * Запись в логе.
149 | * @typedef {Object} Audio.Logger.LogEntry
150 | * @property {Number} timestamp Время в timestamp формате.
151 | * @property {String} level Уровень лога.
152 | * @property {String} channel Канал.
153 | * @property {Object} context Контекст вызова.
154 | * @property {Array} message Дополнительные аргументы.
155 | *
156 | * @private
157 | */
158 |
159 | /**
160 | * Записать сообщение лога в консоль.
161 | * @param {ya.music.Audio.Logger~LogEntry} logEntry Сообщение лога.
162 | * @private
163 | */
164 | Logger._dumpEntry = function(logEntry) {
165 | try {
166 | var level = logEntry.level;
167 |
168 | var name = logEntry.context && (logEntry.context.taskName || logEntry.context.name);
169 | var context = logEntry.context && (logEntry.context._logger ? logEntry.context._logger() : "");
170 |
171 | if (typeof console[level] !== "function") {
172 | console.log.apply(console, [
173 | level.toUpperCase(),
174 | Logger._formatTimestamp(logEntry.timestamp),
175 | "[" + logEntry.channel + (name ? ":" + name : "") + "]",
176 | context
177 | ].concat(logEntry.message));
178 | } else {
179 | console[level].apply(console, [
180 | Logger._formatTimestamp(logEntry.timestamp),
181 | "[" + logEntry.channel + (name ? ":" + name : "") + "]",
182 | context
183 | ].concat(logEntry.message));
184 | }
185 | } catch(e) {
186 | }
187 | };
188 |
189 | /**
190 | * Вспомогательная функция форматирования даты для вывода в коносоль.
191 | * @param timestamp
192 | * @returns {string}
193 | * @private
194 | */
195 | Logger._formatTimestamp = function(timestamp) {
196 | var date = new Date(timestamp);
197 | var ms = date.getMilliseconds();
198 | ms = ms > 100 ? ms : ms > 10 ? "0" + ms : "00" + ms;
199 | return date.toLocaleTimeString() + "." + ms;
200 | };
201 |
202 | module.exports = Logger;
203 |
--------------------------------------------------------------------------------
/src/lib/async/events.js:
--------------------------------------------------------------------------------
1 | var merge = require('../data/merge');
2 |
3 | var LISTENERS_NAME = "_listeners";
4 | var MUTE_OPTION = "_muted";
5 |
6 | // =================================================================
7 |
8 | // Конструктор
9 |
10 | // =================================================================
11 |
12 | /**
13 | * @classdesc Диспетчер событий.
14 | * @class
15 | * @exported ya.music.lib.Events
16 | */
17 | var Events = function() {
18 | /**
19 | * Контейнер для списков слушателей событий.
20 | * @alias Audio.Events#_listeners
21 | * @type {Object.>}
22 | * @private
23 | */
24 | this[LISTENERS_NAME] = {};
25 |
26 | /** Флаг включения/выключения событий
27 | * @alias Events#_mutes
28 | * @type {Boolean}
29 | * @private
30 | */
31 | this[MUTE_OPTION] = false;
32 | };
33 |
34 | // =================================================================
35 |
36 | // Всяческий сахар
37 |
38 | // =================================================================
39 |
40 | /**
41 | * Расширить произвольный класс свойствами диспетчера событий.
42 | * @param {Function} classConstructor Конструктор класса.
43 | * @returns {Function} тот же конструктор класса, расширенный свойствами диспетчера событий.
44 | * @static
45 | */
46 | Events.mixin = function(classConstructor) {
47 | merge(classConstructor.prototype, Events.prototype, true);
48 | return classConstructor;
49 | };
50 |
51 | /**
52 | * Расширить произвольный объект свойствами диспетчера событий.
53 | * @param {Object} object Объект.
54 | * @returns {Object} тот же объект, расширенный свойствами диспетчера событий.
55 | */
56 | Events.eventize = function(object) {
57 | merge(object, Events.prototype, true);
58 | Events.call(object);
59 | return object;
60 | };
61 |
62 | // =================================================================
63 |
64 | // Подписка и отписка от событий
65 |
66 | // =================================================================
67 |
68 | /**
69 | * Подписаться на событие (цепочный метод).
70 | * @param {String} event Имя события.
71 | * @param {function} callback Обработчик события.
72 | * @returns {Events} ссылку на контекст.
73 | */
74 | Events.prototype.on = function(event, callback) {
75 | if (!this[LISTENERS_NAME][event]) {
76 | this[LISTENERS_NAME][event] = [];
77 | }
78 |
79 | this[LISTENERS_NAME][event].push(callback);
80 | return this;
81 | };
82 |
83 | /**
84 | * Отписаться от события (цепочный метод).
85 | * @param {String} event Имя события.
86 | * @param {function} callback Обработчик события.
87 | * @returns {Events} ссылку на контекст.
88 | */
89 | Events.prototype.off = function(event, callback) {
90 | if (!this[LISTENERS_NAME][event]) {
91 | return this;
92 | }
93 |
94 | if (!callback) {
95 | delete this[LISTENERS_NAME][event];
96 | return this;
97 | }
98 |
99 | var callbacks = this[LISTENERS_NAME][event];
100 | for (var k = 0, l = callbacks.length; k < l; k++) {
101 | if (callbacks[k] === callback || callbacks[k].callback === callback) {
102 | callbacks.splice(k, 1);
103 | if (!callbacks.length) {
104 | delete this[LISTENERS_NAME][event];
105 | }
106 | break;
107 | }
108 | }
109 |
110 | return this;
111 | };
112 |
113 | /**
114 | * Подписаться на событие и отписаться сразу после его первого возникновения (цепочный метод).
115 | * @param {String} event Имя события.
116 | * @param {function} callback Обработчик события.
117 | * @returns {Events} ссылку на контекст.
118 | */
119 | Events.prototype.once = function(event, callback) {
120 | var self = this;
121 |
122 | var wrapper = function() {
123 | self.off(event, wrapper);
124 | callback.apply(this, arguments);
125 | };
126 |
127 | wrapper.callback = callback;
128 | self.on(event, wrapper);
129 |
130 | return this;
131 | };
132 |
133 | /**
134 | * Отписаться от всех слушателей событий (цепочный метод).
135 | * @returns {Events} ссылку на контекст.
136 | */
137 | Events.prototype.clearListeners = function() {
138 | for (var key in this[LISTENERS_NAME]) {
139 | if (this[LISTENERS_NAME].hasOwnProperty(key)) {
140 | delete this[LISTENERS_NAME][key];
141 | }
142 | }
143 |
144 | return this;
145 | };
146 |
147 | // =================================================================
148 |
149 | // Триггер событий
150 |
151 | // =================================================================
152 |
153 | /**
154 | * Запустить событие (цепочный метод).
155 | * @param {String} event Имя события.
156 | * @param {...args} args Параметры для передачи вместе с событием.
157 | * @returns {Events} ссылку на контекст.
158 | * @private
159 | */
160 | Events.prototype.trigger = function(event, args) {
161 | if (this[MUTE_OPTION]) {
162 | return this;
163 | }
164 |
165 | args = [].slice.call(arguments, 1);
166 |
167 | if (event !== "*") {
168 | Events.prototype.trigger.apply(this, ["*", event].concat(args));
169 | }
170 |
171 | if (!this[LISTENERS_NAME][event]) {
172 | return this;
173 | }
174 |
175 | var callbacks = [].concat(this[LISTENERS_NAME][event]);
176 | for (var k = 0, l = callbacks.length; k < l; k++) {
177 | callbacks[k].apply(null, args);
178 | }
179 |
180 | return this;
181 | };
182 |
183 | /**
184 | * Делегировать все события другому диспетчеру событий (цепочный метод).
185 | * @param {Events} acceptor Получатель событий.
186 | * @returns {Events} ссылку на контекст.
187 | * @private
188 | */
189 | Events.prototype.pipeEvents = function(acceptor) {
190 | this.on("*", Events.prototype.trigger.bind(acceptor));
191 | return this;
192 | };
193 |
194 | // =================================================================
195 |
196 | // Включение/выключение триггера событий
197 |
198 | // =================================================================
199 |
200 | /**
201 | * Остановить запуск событий (цепочный метод).
202 | * @returns {Events} ссылку на контекст.
203 | */
204 | Events.prototype.muteEvents = function() {
205 | this[MUTE_OPTION] = true;
206 | return this;
207 | };
208 |
209 | /**
210 | * Возобновить запуск событий (цепочный метод).
211 | * @returns {Events} ссылку на контекст.
212 | */
213 | Events.prototype.unmuteEvents = function() {
214 | delete this[MUTE_OPTION];
215 | return this;
216 | };
217 |
218 | module.exports = Events;
219 |
--------------------------------------------------------------------------------
/tutorial/contrib.md:
--------------------------------------------------------------------------------
1 | Контрибьюторам
2 | ==============
3 | Если вы решили помочь в разработке данной библиотеки или вам требуется внести некоторые изменения для своих нужд, эта документация будет вам полезна.
4 |
5 | Code-style
6 | ----------
7 | Есть несколько требований к стилю кода в данном проекте:
8 |
9 | #### Одна сущность - один файл
10 | Каждый класс или отдельная функция должны быть оформлены в виде отдельного файла,
11 | название которого состоит из имени этой сущности в css-case (нижний регистр, слова разделяются дефисами).
12 |
13 | #### Документация кода
14 | Все сущности должны иметь документацию в формате jsdoc. Подробнее об этом можно почитать [тут](http://usejsdoc.org/)
15 | или посмотреть примеры прямо в коде. Не обязательно подробно описывать работу функций или классов,
16 | достаточно краткого описания. Также обязательным является указание типа переменных
17 | и возвращаемых значений.
18 |
19 | В генераторе документации используется специальная директива `@exported` для указания под каким именем данный объект доступен в публичном неймспейсе (см. [src/audio.js](https://github.yandex-team.ru/music/audio/blob/mddoc/src/audio-player.js#L178))
20 |
21 |
22 | Если требуется в коде визуально отделить один блок от другого, следует использовать вот такие вставки c описанием идущего
23 | за ними блока
24 | ```
25 | // =================================================================
26 |
27 | // Проверка доступности Flash-плеера
28 |
29 | // =================================================================
30 | ```
31 | Данный формат выбран т.к. многие IDE сворачивают многострочные комментарии, а это как раз в данном случае не нужно.
32 | Подобный комментарий не будет свернут и он хорошо выделяется.
33 |
34 | #### Форматирование
35 | - `camelCase` - все сущности в проекте именуются согласно форматированию camelCase. Имена классов начинаются с
36 | заглавной буквы, остальные сущности именуются с маленькой буквы.
37 | - `ALL_CAPS_SNAKE_CASE` - все имена констант записываются заглавными буквами, в качестве разделителя слов
38 | используется нижнее подчеркивание.
39 | - `_privateMethod` - приватные методы должны начинаться с подчеркивания. Все свойства считаются приватными и пишутся
40 | без подчеркивания.
41 | - `__boundMethod` - если требуется создавать версию метода, с привязанным контекстом, новый метод должен называться
42 | так же, как оригинальный и начинаться с 2х подчеркиваний (приватные методы в данном случае все равно начинаются с 2х
43 | подчеркиваний, а не с 3х). Такие методы следует создавать в конструкторе класса (желательно использовать именно такой
44 | подход при создании обработчиков событий).
45 | - `"Строки"` - строки заключаются в двойные кавычки.
46 | - `require('some-file')` - инструкции подключения файлов выносятся в начало файла. Имена файлов указываются в
47 | одинарных кавычках без расширения (расширение следует указывать явно только в том случае, если в имени файла есть точка).
48 | Если требуется подключать файлы json, их следует перевести в формат js, добавив в начало файла `module.exports =`.
49 | - `\n` - для переводов строк используется LF-нотация (Unix и OS X стандарт).
50 | - `;\n` - каждая инструкция должна завершаться точкой с запятой и переводом строки.
51 | - `\n&&`, ... - при необходимости переноса знак операции переносится в начало строки.
52 | - ` var a;` - для форматирования отступов используется 4 пробела.
53 | - `a = b + c` - знаки равенства, математических операций и сравнения следует отделять пробелами.
54 | - `var someFunction = function() {\n ... \n};` - функции объявляются через `var`. Не следует использовать именованные
55 | функции. Между объявлением функции/метода и скобками пробела быть не должно. Фигурные скобки ставятся на той же
56 | строке и отделяются пробелом. После закрывающей фигурной скобки следует ставить точку с запятой. После открывающей
57 | и перед закрывающей фигурной скобкой ставится перевод строки. Запись в одну строку допускается только в случае,
58 | если функция/метод содержит ровно 1 инструкцию.
59 | - `if (true) {\n ... \n} else {\n ... \n}` - в конструкции if-else не допускается опускание фигурных скобок. Фигурные
60 | скобки ставятся на той же строке и отделяются пробелом. После открывающей и перед закрывающей фигурной скобкой
61 | ставится перевод строки.
62 | - `<-- 120 -->` - длина строки кода или комментария не должна превышать 120 символов.
63 |
64 | #### Циклы (на графе)
65 | Граф зависимостей проекта не должен содержать циклов. Поясню: нельзя делать так, чтобы
66 | - файл **A** использует файл **B**
67 | - файл **B** использует файл **A**
68 |
69 | Данную конструкцию следует исправлять подобным образом:
70 | - файл **A** использует файлы **B** и **С**
71 | - файл **B** использует файл **C**
72 |
73 | Сборка javascipt
74 | ----------------
75 | Сборка библиотеки производится с помощью npm-пакетов [browserify](https://www.npmjs.com/package/browserify) и [uglify-js](https://www.npmjs.com/package/uglify-js).
76 | Для того чтобы подготовить окружение для сборки библиотеки, требуется установить [node-js](https://nodejs.org/en/) и выполнить `npm install` в корне репозитория.
77 | Сам процесс сборки доступен в двух вариантах: с помощью утилиты make (основой метод) и с помощью библиотеки [grunt](http://gruntjs.com/).
78 |
79 | ### Makefile
80 | Является основным методом сборки. Доступные команды:
81 |
82 | - **make all** - делает полную сборку библиотеки
83 | - **make clean** - удаляет каталог сборки
84 | - **make build** - собирает библиотеку
85 | - **make minify** - собирает минифицированную версию библиотеки (не пересобирает библиотеку, если она была уже собрана через make build)
86 |
87 | Если вызывать команду **make** без аргументов, то будет выполнен сценарий **make all**.
88 | Перед тем как делать pull request, следует сделать полную сборку библиотеки с помощью make.
89 |
90 | ### Grunt
91 | Запасной вариант сборки для тех, у кого по каким-то причинам нет возможности воспользоваться утилитой make.
92 | Чтобы использовать сборку через grunt, требуется установить глобально пакет [grunt-cli](https://www.npmjs.com/package/grunt-cli) (`npm install -g grunt-cli`).
93 | Доступные команды:
94 |
95 | - **grunt all** - делает полную сборку библиотеки
96 | - **grunt clean** - удаляет каталог сборки
97 | - **grunt build** - собирает библиотеку
98 |
99 | Без аргументов **grunt** выполняет сценарий **grunt all**.
100 |
101 |
102 | Сборка Flash
103 | ------------
104 | Сборка Flash-плеера в автоматическом режиме не доступна. Для ручной сборки требуется настроить какой-либо сборщик (наиболее удобные: IntelliJ IDEA, FlashDevelop). Параметры, используемые для сборки: FlexSDK 3.6.0, целевая версия плеера - 9, основной класс - AudioManager, имя собранного файла - player-2_1.swf
105 |
106 |
--------------------------------------------------------------------------------
/tutorial/quick-start.md:
--------------------------------------------------------------------------------
1 | Быстрый старт
2 | =============
3 | В данном разделе на примере создания простого плеера проиллюстрированы основные принципы работы с YandexAudio.
4 |
5 | Для начала создадим html каркас будущего плеера. Нам понадобятся кнопки play, шкала с позицией воспроизведения
6 | и шкала громкости. Также для инициализации flash-плеера, отображения ошибок и блокировки управления потребуется оверлей.
7 |
8 | ***index.html***
9 | ```html
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ```
30 |
31 | Теперь инициализируем всю эту структуру и создадим экземпляр плеера.
32 |
33 | ***index.js***
34 | ```javascript
35 | var AudioPlayer = ya.music.Audio;
36 |
37 | var dom = {
38 | player: document.querySelector(".player"),
39 |
40 | play: document.querySelector(".controls__play"),
41 |
42 | progress: {
43 | bar: document.querySelector(".progress"),
44 | loaded: document.querySelector(".progress__loaded"),
45 | current: document.querySelector(".progress__current")
46 | },
47 |
48 | volume: {
49 | bar: document.querySelector(".volume"),
50 | value: document.querySelector(".volume__bar"),
51 | },
52 |
53 | overlay: document.querySelector(".overlay")
54 | };
55 |
56 | // Предоставим плееру самому решать, какой тип реализации использовать
57 | var audioPlayer = new AudioPlayer(null, dom.overlay);
58 |
59 | audioPlayer.initPromise().then(function() {
60 | // Скрываем оверлей, кнопки управления становятся доступны
61 | dom.overlay.classList.add("overlay_hidden");
62 | }, function(err) {
63 | // Показываем ошибку инициализации в оверлее
64 | dom.overlay.innerHTML = err.message;
65 | dom.overlay.classList.add("overlay_error");
66 | });
67 | ```
68 |
69 | Настроим отображение статуса плеера. Для простого плеера нам достаточно знать, запущено воспроизведение или нет.
70 |
71 | ```javascript
72 | audioPlayer.on(ya.music.Audio.EVENT_STATE, function(state) {
73 | if (state === ya.music.Audio.STATE_PLAYING) {
74 | dom.player.classList.add("player_playing");
75 | } else {
76 | dom.player.classList.remove("player_playing");
77 | }
78 | });
79 | ```
80 |
81 | Теперь настроим обновление прогресс-бара. В нем предусмотрены две шкалы - шкала загрузки и шкала текущей
82 | позиции воспроизведения.
83 |
84 | ```javascript
85 | audioPlayer.on(ya.music.Audio.EVENT_PROGRESS, function(timings) {
86 | dom.progress.loaded.style.width = (timings.loaded / timings.duration * 100).toFixed(2) + "%";
87 | dom.progress.current.style.width = (timings.position / timings.duration * 100).toFixed(2) + "%";
88 | });
89 | ```
90 |
91 | Аналогично будет работать шкала громкости.
92 |
93 | ```javascript
94 | var updateVolume = function(volume) {
95 | dom.volume.value.style.height = (volume * 100).toFixed(2) + "%";
96 | };
97 | audioPlayer.on(ya.music.Audio.EVENT_VOLUME, updateVolume);
98 |
99 | // Отображаем начальную громкость
100 | audioPlayer.initPromise().then(function() {
101 | updateVolume(audioPlayer.getVolume());
102 | });
103 | ```
104 |
105 | Теперь нужно настроить взаимодействие плеера с пользователем. Начнем с запуска воспроизведения.
106 |
107 | ```javascript
108 | var trackUrls = [
109 | "http://some.domain.and.zone/Здесь-могла-быть-ваша-реклама-1.mp3",
110 | "http://some.domain.and.zone/Здесь-могла-быть-ваша-реклама-2.mp3",
111 | "http://some.domain.and.zone/Здесь-могла-быть-ваша-реклама-3.mp3"
112 | ];
113 |
114 | var trackIndex = 0;
115 |
116 | var startPlay = function() {
117 | audioPlayer.play(trackUrls[trackIndex]);
118 | };
119 |
120 | dom.play.addEventListener("click", function() {
121 | var state = audioPlayer.getState();
122 |
123 | switch (state) {
124 | case ya.music.Audio.STATE_PLAYING:
125 | audioPlayer.pause();
126 | break;
127 |
128 | case ya.music.Audio.STATE_PAUSED:
129 | audioPlayer.resume();
130 | break;
131 |
132 | default:
133 | startPlay();
134 | break;
135 | }
136 | });
137 |
138 | audioPlayer.on(ya.music.Audio.EVENT_ENDED, function() {
139 | trackIndex++;
140 |
141 | if (trackIndex < trackUrls.length) {
142 | startPlay();
143 | }
144 | });
145 | ```
146 |
147 | Добавим немножко удобства для пользователей: сделаем автозагрузку следующего трека после того, как текущий загрузился.
148 | Для этого потребуется немного изменить функцию `startPlay` и отслеживать момент загрузки трека:
149 |
150 | ```javascript
151 | var startPlay = function() {
152 | var track = trackUrls[trackIndex];
153 | if (audioPlayer.isPreloaded(track)) {
154 | audioPlayer.playPreloaded(track);
155 | } else {
156 | audioPlayer.play(track);
157 | }
158 | };
159 |
160 | audioPlayer.on(ya.music.Audio.EVENT_LOADED, function() {
161 | if (trackIndex + 1 < trackUrls.length) {
162 | audioPlayer.preload(trackUrls[trackIndex + 1]);
163 | }
164 | });
165 | ```
166 |
167 | Осталось только настроить навигацию по треку и регулирование громкости:
168 | ```javascript
169 | var offsetLeft = function(node) {
170 | var offset = node.offsetLeft;
171 | if (node.offsetParent) {
172 | offset += offsetLeft(node.offsetParent);
173 | }
174 | return offset;
175 | };
176 |
177 | var offsetTop = function(node) {
178 | var offset = node.offsetTop;
179 | if (node.offsetParent) {
180 | offset += offsetTop(node.offsetParent);
181 | }
182 | return offset;
183 | };
184 |
185 | dom.progress.bar.addEventListener("click", function(evt) {
186 | var fullWidth = dom.progress.bar.offsetWidth;
187 | var offset = offsetLeft(dom.progress.bar);
188 |
189 | var relativePosition = Math.max(0, Math.min(1, ((evt.pageX || evt.screenX) - offset) / fullWidth));
190 | var duration = audioPlayer.getDuration();
191 |
192 | audioPlayer.setPosition(duration * relativePosition);
193 | });
194 |
195 | dom.volume.bar.addEventListener("click", function(evt) {
196 | var fullHeight = dom.volume.bar.offsetHeight;
197 | var offset = offsetTop(dom.volume.bar);
198 |
199 | // тут мы делаем "1 -" т.к. громость принято отмерять снизу, а не сверху
200 | var volume = 1 - Math.max(0, Math.min(1, ((evt.pageY || evt.screenY) - offset) / fullHeight));
201 | audioPlayer.setVolume(volume);
202 | });
203 | ```
204 |
205 | Полный код можно [посмотреть тут](https://github.yandex-team.ru/music/audio/tree/master/examples/quick-start).
206 | Рабочий пример кода можно [посмотреть тут](https://music.yandex.ru/api/audio/examples/quick-start/index.html).
207 |
--------------------------------------------------------------------------------
/src/IAudioImplementation.jsdoc:
--------------------------------------------------------------------------------
1 | // =================================================================
2 |
3 | // Интерфейс
4 |
5 | // =================================================================
6 |
7 | /**
8 | * Интерфейс внутренней реализации плеера.
9 | * @typedef {function} IAudioImplementation
10 | * @kind class
11 | *
12 | * @extends Events
13 | *
14 | * @param {HTMLElement} [overlay] - место для встраивания плеера (актуально только для Flash-плеера)
15 | *
16 | * @property {string} type - Тип плеера
17 | * @property {Boolean} available - Доступность реализации
18 | *
19 | * @fires IAudioImplementation#EVENT_PLAY
20 | * @fires IAudioImplementation#EVENT_ENDED
21 | * @fires IAudioImplementation#EVENT_VOLUME
22 | * @fires IAudioImplementation#EVENT_CRASHED
23 | * @fires IAudioImplementation#EVENT_SWAP
24 | *
25 | * @fires IAudioImplementation#EVENT_STOP
26 | * @fires IAudioImplementation#EVENT_PAUSE
27 | * @fires IAudioImplementation#EVENT_PROGRESS
28 | * @fires IAudioImplementation#EVENT_LOADING
29 | * @fires IAudioImplementation#EVENT_LOADED
30 | * @fires IAudioImplementation#EVENT_ERROR
31 | *
32 | * @abstract
33 | * @private
34 | */
35 |
36 | // =================================================================
37 |
38 | // События
39 |
40 | // =================================================================
41 |
42 | /** Событие начала воспроизведения
43 | * @event IAudioImplementation#EVENT_PLAY
44 | */
45 | /** Событие завершения воспроизведения
46 | * @event IAudioImplementation#EVENT_ENDED
47 | */
48 | /** Событие изменения громкости
49 | * @event IAudioImplementation#EVENT_VOLUME
50 | * @param {Number} volume - громкость
51 | */
52 | /** Событие краха плеера
53 | * @event IAudioImplementation#EVENT_CRASHED
54 | */
55 | /** Событие переключения активного плеера и прелоадера
56 | * @event IAudioImplementation#EVENT_SWAP
57 | */
58 |
59 | /** Событие остановки воспроизведения
60 | * @event IAudioImplementation#EVENT_STOP
61 | */
62 | /** Событие начала воспроизведения
63 | * @event IAudioImplementation#EVENT_PAUSE
64 | */
65 | /** Событие обновления позиции воспроизведения/загруженной части
66 | * @event IAudioImplementation#EVENT_PROGRESS
67 | * @param {Audio~AudioPlayerTimes} times - информация о временных данных трека
68 | */
69 | /** Событие начала загрузки трека
70 | * @event IAudioImplementation#EVENT_LOADING
71 | */
72 | /** Событие завершения загрузки трека
73 | * @event IAudioImplementation#EVENT_LOADED
74 | */
75 | /** Событие ошибки воспроизведения
76 | * @event IAudioImplementation#EVENT_ERROR
77 | */
78 |
79 | // =================================================================
80 |
81 | // Методы управления воспроизведением
82 |
83 | // =================================================================
84 |
85 | /**
86 | * Обещание, которое разрешается при завершении инициализации
87 | * @member IAudioImplementation#whenReady
88 | * @type {Promise}
89 | */
90 |
91 | /**
92 | * Проиграть трек
93 | * @method IAudioImplementation#play
94 | * @param {String} src - ссылка на трек
95 | * @param {Number} [duration] - Длительность трека (не используется)
96 | * @abstract
97 | */
98 |
99 | /**
100 | * Поставить трек на паузу
101 | * @method IAudioImplementation#pause
102 | * @abstract
103 | */
104 |
105 | /**
106 | * Снять трек с паузы
107 | * @method IAudioImplementation#resume
108 | * @abstract
109 | */
110 |
111 | /**
112 | * Остановить воспроизведение и загрузку трека
113 | * @method IAudioImplementation#stop
114 | * @param {int} [offset=0] - 0: для текущего загрузчика, 1: для следующего загрузчика
115 | * @abstract
116 | */
117 |
118 | /**
119 | * Получить позицию воспроизведения
120 | * @method IAudioImplementation#getPosition
121 | * @returns {number}
122 | * @abstract
123 | */
124 |
125 | /**
126 | * Установить текущую позицию воспроизведения
127 | * @method IAudioImplementation#setPosition
128 | * @param {number} position
129 | * @abstract
130 | */
131 |
132 | /**
133 | * Получить длительность трека
134 | * @method IAudioImplementation#getDuration
135 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
136 | * @returns {number}
137 | * @abstract
138 | */
139 |
140 | /**
141 | * Получить длительность загруженной части трека
142 | * @method IAudioImplementation#getLoaded
143 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
144 | * @returns {number}
145 | * @abstract
146 | */
147 |
148 | /**
149 | * Получить максимально возможную точку перемотки
150 | * @method IAudioImplementation#getMaxSeekablePosition
151 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
152 | * @returns {number}
153 | * @abstract
154 | */
155 |
156 | /**
157 | * Получить текущее значение громкости
158 | * @method IAudioImplementation#getVolume
159 | * @returns {number}
160 | * @abstract
161 | */
162 |
163 | /**
164 | * Установить значение громкости
165 | * @method IAudioImplementation#setVolume
166 | * @param {number} volume - желаемая громкость
167 | * @abstract
168 | */
169 |
170 | // =================================================================
171 |
172 | // Методы предзагрузчика
173 |
174 | // =================================================================
175 |
176 | /**
177 | * Предзагрузить трек
178 | * @method IAudioImplementation#preload
179 | * @param {String} src - Ссылка на трек
180 | * @param {Number} [duration] - Длительность трека (не используется)
181 | * @param {int} [offset=1] - 0: текущий загрузчик, 1: следующий загрузчик
182 | * @abstract
183 | */
184 |
185 | /**
186 | * Проверить что трек предзагружается
187 | * @method IAudioImplementation#isPreloaded
188 | * @param {String} src - ссылка на трек
189 | * @param {int} [offset=1] - 0: текущий загрузчик, 1: следующий загрузчик
190 | * @returns {boolean}
191 | * @abstract
192 | */
193 |
194 | /**
195 | * Проверить что трек начал предзагружаться
196 | * @method IAudioImplementation#isPreloading
197 | * @param {String} src - ссылка на трек
198 | * @param {int} [offset=1] - 0: текущий загрузчик, 1: следующий загрузчик
199 | * @returns {boolean}
200 | * @abstract
201 | */
202 |
203 | /**
204 | * Запустить воспроизведение предзагруженного трека
205 | * @method IAudioImplementation#playPreloaded
206 | * @param {int} [offset=1] - 0: текущий загрузчик, 1: следующий загрузчик
207 | * @returns {boolean} -- доступность данного действия
208 | * @abstract
209 | */
210 |
211 | // =================================================================
212 |
213 | // Методы получения данных
214 |
215 | // =================================================================
216 |
217 | /**
218 | * Получить ссылку на трек
219 | * @method IAudioImplementation#getSrc
220 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
221 | * @returns {String|Boolean} -- Ссылка на трек или false, если нет загружаемого трека
222 | * @abstract
223 | */
224 |
225 | /**
226 | * Проверить доступен ли программный контроль громкости
227 | * @method IAudioImplementation#isDeviceVolume
228 | * @returns {boolean}
229 | * @abstract
230 | */
231 |
232 | /**
233 | * Проверить доступность воcпроизведения без пользовательского взаимодействия
234 | * @method IAudioImplementation#isAutoplayable
235 | * @returns {boolean}
236 | * @abstract
237 | */
238 |
--------------------------------------------------------------------------------
/tutorial/corner-case.md:
--------------------------------------------------------------------------------
1 | Подводные камни
2 | ===============
3 |
4 | Данное руководство объясняет некоторые особенности работы библиотеки. Как html5 audio, так и flash являются весьма
5 | нестабильными технологиями и имеют разную поддержку в разных браузерах. Большую часть этих проблем данная библиотека
6 | решает, но, тем не менее, остаются вещи, которые нельзя решить с помощью js или которые являются неочевидными для разработчиков.
7 |
8 | Flash
9 | -----
10 |
11 | ### Flash и оверлей
12 |
13 | Существует по меньшей мере две проблемы с flash, которые невозможно 100% продиагностировать с помощью js:
14 | - узнать точную версию flash-плеера;
15 | - узнать, есть ли блокировщик flash в браузере.
16 |
17 | Узнать точную версию flash нельзя, т.к. некоторые плагины для браузеров выводят совершенно неадекватные номера версий.
18 | Встречались случаи, когда номер версии был 99.999.999 или наоборот 0.0.0. С блокировщиками flash ситуация усложняется -
19 | некоторые блокировщики специально стараются замаскировать свое присутствие, другие не очень корректно
20 | производят блокировку (например, позволяют flash-апплету полностью загрузиться и даже выполнить какой-нибудь код и лишь
21 | после этого блокируют их). Более того, если flash-апплет невидим, то у пользователя нет никакой возможности
22 | узнать о факте блокировки и тем более нет возможности разблокировать его.
23 |
24 | Для решения данной проблемы конструктор плеера принимает в качестве необязательного аргумента html-элемент для
25 | отображения оверлея. В этот оверлей помещается прозрачный flash-апплет плеера. Это позволяет в случае необходимости
26 | показать пользователю, что flash-блокировщик сработал некорректно и его нужно отключить. Большая часть блокировщиков позволяет
27 | отменить блокировку простым кликом по данному html-элементу, поэтому плеер прослушивает событие клика. Если после
28 | наступления этого события flash-апплет по-прежнему оказывается недоступен, плеер пытается применить другую технологию воспроизведения или перезагружает апплет.
29 |
30 | ***ВАЖНО!*** После того как плеер был успешно инициализирован, не нужный более оверлей нельзя просто удалить из DOM дерева.
31 | Также нельзя скрыть его с помощью стиля `display: none`. Это приведет к тому, что плеер перейдет в нерабочее состояние.
32 | Для того чтобы скрыть ненужный оверлей, можно, например, вынести его за пределы страницы следующим css-стилем:
33 |
34 | ```css
35 | position: absolute;
36 | top: -9999px;
37 | left: -9999px;
38 | ```
39 |
40 | ### Некоторые особенности flash
41 | Во flash-реализации плеера есть несколько особенностей, про которые стоит знать.
42 |
43 | 1. flash-апплет является синглтоном. При создании любого количества плееров с внутренним типом flash - апплет
44 | будет загружен и включен в страницу только один раз. Сам апплет является чем-то вроде фабрики и моста одновременно.
45 | Он создает во flash окружении экземпляры flash-плееров и коммутирует запросы и события, связанные с этими плеерами.
46 | У этого решения есть как плюсы, так и минусы:
47 | Плюсы:
48 | - существенно меньше потребление памяти при использовании нескольких плееров на странице;
49 | - при наличии flash-блокеров инициализация требуется всего один раз для любого количества плееров.
50 | Минус:
51 | - при крахе апплета "умрут" сразу все плееры, которые используют flash-реализацию (такое пока не наблюдалось, но
52 | вероятность все же существует).
53 |
54 | 2. Текущая реализация flash-плеера достаточно проста и потому имеет одно ограничение: нельзя ставить позицию
55 | воспроизведения дальше загруженной части трека. При установке позиции воспроизведения следует всегда считывать
56 | возвращаемое значение - оно будет отражать реальную позицию, на которую было установлено воспроизведение (или как
57 | вариант полагаться только на события обновления позиции воспроизведения).
58 |
59 | HTML5
60 | -----
61 |
62 | ### HTML5 и мобильные браузеры
63 | В мобильных браузерах есть сразу несколько проблем с воспроизведением звука в html5 audio элементе.
64 |
65 | 1. Элементы audio начинают работать и воспроизводить звук, только если команда play была вызвана внутри обработчика
66 | пользовательского события. Это поведние присутствует также и в большинстве десктопных браузеров. Задачу начальной
67 | инициализации API берет на себя, прослушивая события keydown, mousedown, и touchstart на объекте body.
68 | Это требуется выполнить всего лишь один раз, далее можно спокойно пользоваться элементом.
69 |
70 | 2. Мобильные браузеры работают некорректно, когда на странице присутствует два и более элементов audio:
71 |
72 | - Только один элемент может быть активным. Если запустить воспроизведение во втором, то в первом оно остановится.
73 | - После такого сценария остановки первый элемент "умирает". Повторная попытка запустить воспроизведение в этом
74 | элементе не даст результатов. От этого помогает только вызов на первом элементе
75 | метода load.
76 | - Даже если трек полностью закешировался и ссылка в элементе audio не менялась, повторный вызов метода load
77 | повторно загрузит трек снуля. Кеширование не применяется.
78 |
79 | Данная библиотека решает перечисленные выше проблемы - при запуске воспроизведения трека
80 | скрипт следит за процессом загрузки и обновления позиции воспроизведения и, если воспроизведение на самом деле не
81 | начинается, то скрипт перезагружает трек и запускает воспроизведение заново с точки, на которой оно остановилось.
82 | При отладке работы плеера следует учитывать данное поведение.
83 |
84 | 3. Некоторые мобильные браузеры запрещают элементу audio изменять громкость. Для того чтобы проверить, есть ли
85 | возможность программного изменения громкости, сделан метод isDeviceVolume.
86 |
87 | ### Web Audio API
88 | Технология Web Audio API самая новая из всех перечисленных, но самая перспективная. Однако у данной технологии есть ряд недостатков:
89 |
90 | 1. Для воспроизведения треков со стороннего домена с помощью Web Audio API требуется, чтобы медиа-файлы приходили
91 | с правильным заголовком Access-Control-Allow-Origin, иначе Web Audio API не сможет получить доступ к данным трека. При
92 | этом в разных браузерах это приводит к разным результатам: одни браузеры просто ничего не делают, в других наступает
93 | тишина. Если планируется динамически подключать Web Audio API во время воспроизведения трека, следует заранее включить
94 | режим CORS (метод toggleCrossDomain); в противном случае потребуется перезагрузить трек после включения этого режима.
95 | Более того, в FF 39 обнаружена ошибка, при которой даже перезагрузка трека не обеспечивает корректной
96 | работы Web Audio API.
97 |
98 | ***ВАЖНО!*** - при включенном режиме CORS аудио элемент не сможет загружать
99 | данные со сторонних доменов, если в ответе не будет правильного заголовка Access-Control-Allow-Origin. Не рекомендуется включать этот
100 | режим, если не планируется использование Web Audio API.
101 |
102 | 2. Web Audio API не работает в полной мере в большинстве мобильных браузеров, несмотря на то что удается создать контекст и при создании различных AudioNode
103 | не возникает никаких ошибок и нормально воспроизводится звук. Поэтому для мобильных браузеров Web Audio API превентивно
104 | отключен.
105 |
106 | 3. Web Audio API может существенно увеличить нагрузку на CPU, в т.ч. вызвать "заикание" звука. Стоит предоставить
107 | пользователю возможность отключить использование Web Audio API.
108 |
--------------------------------------------------------------------------------
/src/lib/browser/audioContextMonkeyPatch.js:
--------------------------------------------------------------------------------
1 | /* Copyright 2013 Chris Wilson
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | /*
17 |
18 | This monkeypatch library is intended to be included in projects that are
19 | written to the proper AudioContext spec (instead of webkitAudioContext),
20 | and that use the new naming and proper bits of the Web Audio API (e.g.
21 | using BufferSourceNode.start() instead of BufferSourceNode.noteOn()), but may
22 | have to run on systems that only support the deprecated bits.
23 |
24 | This library should be harmless to include if the browser supports
25 | unprefixed "AudioContext", and/or if it supports the new names.
26 |
27 | The patches this library handles:
28 | if window.AudioContext is unsupported, it will be aliased to webkitAudioContext().
29 | if AudioBufferSourceNode.start() is unimplemented, it will be routed to noteOn() or
30 | noteGrainOn(), depending on parameters.
31 |
32 | The following aliases only take effect if the new names are not already in place:
33 |
34 | AudioBufferSourceNode.stop() is aliased to noteOff()
35 | AudioContext.createGain() is aliased to createGainNode()
36 | AudioContext.createDelay() is aliased to createDelayNode()
37 | AudioContext.createScriptProcessor() is aliased to createJavaScriptNode()
38 | AudioContext.createPeriodicWave() is aliased to createWaveTable()
39 | OscillatorNode.start() is aliased to noteOn()
40 | OscillatorNode.stop() is aliased to noteOff()
41 | OscillatorNode.setPeriodicWave() is aliased to setWaveTable()
42 | AudioParam.setTargetAtTime() is aliased to setTargetValueAtTime()
43 |
44 | This library does NOT patch the enumerated type changes, as it is
45 | recommended in the specification that implementations support both integer
46 | and string types for AudioPannerNode.panningModel, AudioPannerNode.distanceModel
47 | BiquadFilterNode.type and OscillatorNode.type.
48 |
49 | */
50 | (function (global, exports, perf) {
51 | 'use strict';
52 |
53 | function fixSetTarget(param) {
54 | if (!param) // if NYI, just return
55 | return;
56 | if (!param.setTargetAtTime)
57 | param.setTargetAtTime = param.setTargetValueAtTime;
58 | }
59 |
60 | if (window.hasOwnProperty('webkitAudioContext') &&
61 | !window.hasOwnProperty('AudioContext')) {
62 | window.AudioContext = webkitAudioContext;
63 |
64 | if (!AudioContext.prototype.hasOwnProperty('createGain'))
65 | AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
66 | if (!AudioContext.prototype.hasOwnProperty('createDelay'))
67 | AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
68 | if (!AudioContext.prototype.hasOwnProperty('createScriptProcessor'))
69 | AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createJavaScriptNode;
70 | if (!AudioContext.prototype.hasOwnProperty('createPeriodicWave'))
71 | AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
72 |
73 |
74 | AudioContext.prototype.internal_createGain = AudioContext.prototype.createGain;
75 | AudioContext.prototype.createGain = function() {
76 | var node = this.internal_createGain();
77 | fixSetTarget(node.gain);
78 | return node;
79 | };
80 |
81 | AudioContext.prototype.internal_createDelay = AudioContext.prototype.createDelay;
82 | AudioContext.prototype.createDelay = function(maxDelayTime) {
83 | var node = maxDelayTime ? this.internal_createDelay(maxDelayTime) : this.internal_createDelay();
84 | fixSetTarget(node.delayTime);
85 | return node;
86 | };
87 |
88 | AudioContext.prototype.internal_createBufferSource = AudioContext.prototype.createBufferSource;
89 | AudioContext.prototype.createBufferSource = function() {
90 | var node = this.internal_createBufferSource();
91 | if (!node.start) {
92 | node.start = function ( when, offset, duration ) {
93 | if ( offset || duration )
94 | this.noteGrainOn( when || 0, offset, duration );
95 | else
96 | this.noteOn( when || 0 );
97 | };
98 | } else {
99 | node.internal_start = node.start;
100 | node.start = function( when, offset, duration ) {
101 | if( typeof duration !== 'undefined' )
102 | node.internal_start( when || 0, offset, duration );
103 | else
104 | node.internal_start( when || 0, offset );
105 | };
106 | }
107 | if (!node.stop) {
108 | node.stop = function ( when ) {
109 | this.noteOff( when || 0 );
110 | };
111 | } else {
112 | node.internal_stop = node.stop;
113 | node.stop = function( when ) {
114 | node.internal_stop( when || 0 );
115 | };
116 | }
117 | fixSetTarget(node.playbackRate);
118 | return node;
119 | };
120 |
121 | AudioContext.prototype.internal_createDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor;
122 | AudioContext.prototype.createDynamicsCompressor = function() {
123 | var node = this.internal_createDynamicsCompressor();
124 | fixSetTarget(node.threshold);
125 | fixSetTarget(node.knee);
126 | fixSetTarget(node.ratio);
127 | fixSetTarget(node.reduction);
128 | fixSetTarget(node.attack);
129 | fixSetTarget(node.release);
130 | return node;
131 | };
132 |
133 | AudioContext.prototype.internal_createBiquadFilter = AudioContext.prototype.createBiquadFilter;
134 | AudioContext.prototype.createBiquadFilter = function() {
135 | var node = this.internal_createBiquadFilter();
136 | fixSetTarget(node.frequency);
137 | fixSetTarget(node.detune);
138 | fixSetTarget(node.Q);
139 | fixSetTarget(node.gain);
140 | return node;
141 | };
142 |
143 | if (AudioContext.prototype.hasOwnProperty( 'createOscillator' )) {
144 | AudioContext.prototype.internal_createOscillator = AudioContext.prototype.createOscillator;
145 | AudioContext.prototype.createOscillator = function() {
146 | var node = this.internal_createOscillator();
147 | if (!node.start) {
148 | node.start = function ( when ) {
149 | this.noteOn( when || 0 );
150 | };
151 | } else {
152 | node.internal_start = node.start;
153 | node.start = function ( when ) {
154 | node.internal_start( when || 0);
155 | };
156 | }
157 | if (!node.stop) {
158 | node.stop = function ( when ) {
159 | this.noteOff( when || 0 );
160 | };
161 | } else {
162 | node.internal_stop = node.stop;
163 | node.stop = function( when ) {
164 | node.internal_stop( when || 0 );
165 | };
166 | }
167 | if (!node.setPeriodicWave)
168 | node.setPeriodicWave = node.setWaveTable;
169 | fixSetTarget(node.frequency);
170 | fixSetTarget(node.detune);
171 | return node;
172 | };
173 | }
174 | }
175 |
176 | if (window.hasOwnProperty('webkitOfflineAudioContext') &&
177 | !window.hasOwnProperty('OfflineAudioContext')) {
178 | window.OfflineAudioContext = webkitOfflineAudioContext;
179 | }
180 |
181 | }(window));
182 |
--------------------------------------------------------------------------------
/src/flash/flash-interface.js:
--------------------------------------------------------------------------------
1 | var Logger = require('../logger/logger');
2 | var logger = new Logger('FlashInterface');
3 |
4 | // =================================================================
5 |
6 | // Конструктор
7 |
8 | // =================================================================
9 |
10 | /**
11 | * @classdesc Описание внешнего интерфейса Flash-плеера
12 | * @param {Object} flash - swf-объект
13 | * @constructor
14 | * @private
15 | */
16 | var FlashInterface = function(flash) {
17 | //FIXME: нужно придумать нормальный метод экспорта
18 | this.flash = ya.music.Audio._flash = flash;
19 | };
20 |
21 | // =================================================================
22 |
23 | // Общение с Flash-плеером
24 |
25 | // =================================================================
26 |
27 | /**
28 | * Вызвать метод Flash-плеера
29 | * @param {String} fn - название метода
30 | * @returns {*}
31 | * @private
32 | */
33 | FlashInterface.prototype._callFlash = function(fn) {
34 | //DEV && logger.debug(this, fn, arguments);
35 |
36 | try {
37 | return this.flash.call.apply(this.flash, arguments);
38 | } catch(e) {
39 | logger.error(this, "_callFlashError", e, arguments[0], arguments[1], arguments[2]);
40 | return null;
41 | }
42 | };
43 |
44 | /**
45 | * Проверка обратной связи с Flash-плеером
46 | * @throws Ошибка доступа к Flash-плееру
47 | * @private
48 | */
49 | FlashInterface.prototype._heartBeat = function() {
50 | this._callFlash("heartBeat", -1);
51 | };
52 |
53 | /**
54 | * Добавить новый плеер
55 | * @returns {int} -- id нового плеера
56 | * @private
57 | */
58 | FlashInterface.prototype._addPlayer = function() {
59 | return this._callFlash("addPlayer", -1);
60 | };
61 |
62 | // =================================================================
63 |
64 | // Методы управления плеером
65 |
66 | // =================================================================
67 |
68 | /**
69 | * Установить громкость
70 | * @param {int} id - id плеера
71 | * @param {Number} volume - желаемая громкость
72 | */
73 | FlashInterface.prototype.setVolume = function(id, volume) {
74 | this._callFlash("setVolume", -1, volume);
75 | };
76 |
77 | /**
78 | * Получить значение громкости
79 | * @returns {Number}
80 | */
81 | FlashInterface.prototype.getVolume = function() {
82 | return this._callFlash("getVolume", -1);
83 | };
84 |
85 | /**
86 | * Запустить воспроизведение трека
87 | * @param {int} id - id плеера
88 | * @param {String} src - ссылка на трек
89 | * @param {Number} duration - длительность трека
90 | */
91 | FlashInterface.prototype.play = function(id, src, duration) {
92 | this._callFlash("play", id, src, duration && duration * 1000);
93 | };
94 |
95 | /**
96 | * Остановить воспроизведение и загрузку трека
97 | * @param {int} id - id плеера
98 | * @param {int} [offset=0] - 0: для текущего загрузчика, 1: для следующего загрузчика
99 | */
100 | FlashInterface.prototype.stop = function(id, offset) {
101 | this._callFlash("stop", id, offset || 0);
102 | };
103 |
104 | /**
105 | * Поставить трек на паузу
106 | * @param {int} id - id плеера
107 | */
108 | FlashInterface.prototype.pause = function(id) {
109 | this._callFlash("pause", id);
110 | };
111 |
112 | /**
113 | * Снять трек с паузы
114 | * @param {int} id - id плеера
115 | */
116 | FlashInterface.prototype.resume = function(id) {
117 | this._callFlash("resume", id);
118 | };
119 |
120 | /**
121 | * Получить позицию воспроизведения
122 | * @param {int} id - id плеера
123 | * @returns {Number}
124 | */
125 | FlashInterface.prototype.getPosition = function(id) {
126 | return this._callFlash("getPosition", id);
127 | };
128 |
129 | /**
130 | * Установить текущую позицию воспроизведения
131 | * @param {int} id - id плеера
132 | * @param {number} position
133 | */
134 | FlashInterface.prototype.setPosition = function(id, position) {
135 | this._callFlash("setPosition", id, position);
136 | };
137 |
138 | /**
139 | * Получить длительность трека
140 | * @param {int} id - id плеера
141 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
142 | * @returns {Number}
143 | */
144 | FlashInterface.prototype.getDuration = function(id, offset) {
145 | return this._callFlash("getDuration", id, offset || 0);
146 | };
147 |
148 | /**
149 | * Получить длительность загруженной части трека
150 | * @param {int} id - id плеера
151 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
152 | * @returns {Number}
153 | */
154 | FlashInterface.prototype.getLoaded = function(id, offset) {
155 | return this._callFlash("getLoaded", id, offset || 0);
156 | };
157 |
158 | /**
159 | * Получить максимально возможную точку перемотки
160 | * @param {int} id - id плеера
161 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
162 | * @returns {number}
163 | */
164 | FlashInterface.prototype.getMaxSeekablePosition = function(id, offset) {
165 | return this.getLoaded(id, offset);
166 | };
167 |
168 | // =================================================================
169 |
170 | // Предзагрузка
171 |
172 | // =================================================================
173 |
174 | /**
175 | * Предзагрузить трек
176 | * @param {int} id - id плеера
177 | * @param {String} src - ссылка на трек
178 | * @param {Number} duration - длительность трека
179 | * @param {int} [offset=0] - 0: для текущего загрузчика, 1: для следующего загрузчика
180 | * @returns {Boolean} -- возможность данного действия
181 | */
182 | FlashInterface.prototype.preload = function(id, src, duration, offset) {
183 | return this._callFlash("preload", id, src, duration && duration * 1000, offset == null ? 1 : offset);
184 | };
185 |
186 | /**
187 | * Проверить что трек предзагружается
188 | * @param {int} id - id плеера
189 | * @param {String} src - ссылка на трек
190 | * @param {int} [offset=1] - 0: для текущего загрузчика, 1: для следующего загрузчика
191 | * @returns {Boolean}
192 | */
193 | FlashInterface.prototype.isPreloaded = function(id, src, offset) {
194 | return this._callFlash("isPreloaded", id, src, offset == null ? 1 : offset);
195 | };
196 |
197 | /**
198 | * Проверить что трек начал предзагружаться
199 | * @param {int} id - id плеера
200 | * @param {String} src - ссылка на трек
201 | * @param {int} [offset=1] - 0: для текущего загрузчика, 1: для следующего загрузчика
202 | * @returns {Boolean}
203 | */
204 | FlashInterface.prototype.isPreloading = function(id, src, offset) {
205 | return this._callFlash("isPreloading", id, src, offset == null ? 1 : offset);
206 | };
207 |
208 | /**
209 | * Запустить воспроизведение предзагруженного трека
210 | * @param {int} id - id плеера
211 | * @param {int} [offset=1] - 0: текущий загрузчик, 1: следующий загрузчик
212 | * @returns {Boolean} -- доступность данного действия
213 | */
214 | FlashInterface.prototype.playPreloaded = function(id, offset) {
215 | return this._callFlash("playPreloaded", id, offset == null ? 1 : offset);
216 | };
217 |
218 | // =================================================================
219 |
220 | // Получение данных о плеере
221 |
222 | // =================================================================
223 |
224 | /**
225 | * Получить ссылку на трек
226 | * @param {int} id - id плеера
227 | * @param {int} [offset=0] - 0: текущий загрузчик, 1: следующий загрузчик
228 | * @returns {String}
229 | */
230 | FlashInterface.prototype.getSrc = function(id, offset) {
231 | return this._callFlash("getSrc", id, offset || 0);
232 | };
233 |
234 | /**
235 | * Проверить доступность воcпроизведения без пользовательского взаимодействия
236 | * @returns {boolean}
237 | */
238 | FlashInterface.prototype.isAutoplayable = function() {
239 | return true;
240 | };
241 |
242 | module.exports = FlashInterface;
243 |
--------------------------------------------------------------------------------
/tutorial/fx.md:
--------------------------------------------------------------------------------
1 | Обработка звука
2 | ===============
3 |
4 | В состав данной библиотеки входят 2 подмодуля:
5 |
6 | - эквалайзер;
7 | - набор функций для работы с разными единицами и шкалами для измерения громкости.
8 |
9 | Оба подмодуля несут исключительно вспомогательную функцию, для работы самой библиотеки их использование не требуется. Также эти модули можно применять не только к самому плееру.
10 |
11 | Web Audio API
12 | -------------
13 |
14 | Стоит сразу оговориться, что эквалайзер - это технология Web Audio API и нужно понимать то, как данная библиотека работает с этим самым API. Есть несколько особенностей, которые стоит учитывать проектируя собственные фильтры препроцессора (да и просто для использования эквалайзера не плохо знать это).
15 |
16 | 1. При использовании YandexAudio API не требуется вручную создавать контекст AudioContext. Этот объект создается библиотекой YandexAudio автоматически, после ее загрузки на страницу (если есть поддержка технологии Web Audio API в браузере). Созданный аудио-контекст доступен через `ya.music.Audio.audioContext`.
17 |
18 | 2. Web Audio API более требователен к контенту - в случае если аудио-данные берутся со стороннего домена, требуется чтобы был включен режим CORS и целевой домен проставлял корректные заголовки `Access-Control-Allow-Origin`. Для включения данного режима используется метод `YadexAudio#toggleCrossDomain(true)`. **Важно** - если сервер не отправляет необходимые заголовки включение данного режима не позволит загрузить трек даже если не используется Web Audio API, так что не стоит включать этот режим превентивно, если не планируется использование Web Audio API.
19 |
20 | 3. Web Audio API не умеет напрямую использовать элементы `audio` в графе. Для их использования требуется создать элемент `MediaElementAudioSourceNode`, указав `audio` элемент как источник звука (опять же, подробнее в статье [Web Audio API](web-audio-api.md) в разделе **Источники сигнала**). Далее, так как мы хотим работать с изначальным сигналом без всяких искажений (включая изменение громкости, которое очень критично для некоторых фильтров), нам потребуется создать элемент `GainNode`, который будет управлять итоговой громкостью, а самому элементу `audio` выставить максимальную громкость. Именно это проделывает вызов `YandexAudio#toggleWebAudioAPI(true)` - создает необходимое окружение для использования Web Audio API.
21 |
22 |
23 | 4. **Важно**. Однажды включенный режим Web Audio API отключить нельзя! Когда вызывается метод `YandexAudio#toggleWebAudioAPI(false)` граф просто максимально сокращается. Из него удаляется препроцессор, управление громкостью при этом по-прежнему ведется через `GainNode`, так как некоторые браузеры не возвращают корректно управление громкостью элементу `audio`.
24 |
25 | Сам процесс подключения различных препроцессоров и фильтров предельно прост. Препроцессор описывается двумя нодами - входной и выходной (для простых фильтров это может быть одна и та же нода). Выход `MediaElementAudioSourceNode` подключается ко входу входной ноды, а выход выходной ноды препроцессора подключается к входу `GainNode`. Таким образом, если требуется подключить более одного фильтра, то достаточно соединить их в нужной последовательности и передать в качестве входной и выходной ноды соответствующие ноды этой конструкции.
26 |
27 | ```(javascript)
28 | equalizer.output.connect(convolverNode);
29 | convolverNode.connect(dynamicsCompressorNode);
30 | dynamicsCompressorNode.connect(analyzerNode);
31 |
32 | player.setAudioPreprocessor({
33 | input: equalizer.input,
34 | output: dynamicsCompressorNode // нам не нужен выход analyzerNode, и ее не обязательно подключать к какому-либо выходу
35 | });
36 | ```
37 |
38 |
39 |
40 |
41 | Эквалайзер
42 | ----------
43 |
44 | Эквалайзер - это инструмент для фильтрации звука, позволяющий настраивать различную громкость для разных полос пропускания. Он используется преимущественно для компенсации недостатков записывающей и/или воспроизводящей аппаратуры, но может применяться и для придания большей выразительности звучанию.
45 |
46 | Стоит сразу упомянуть, что эквалайзер реализован только для html5-версии плеера, т.к. он использует технологию [Web Audio API](web-audio-api.md). Рассмотрим небольшой пример того, как подключить и использовать эквалайзер:
47 |
48 | ```(javascript)
49 | // Сахар для удобства доступа
50 | var YandexAudio = ya.music.Audio;
51 | var Equalizer = YandexAudio.fx.Equalizer;
52 |
53 | // При создании плеера непосредственно указываем html5, т.к. эквалайзер не будет работать с flash-плеером
54 | var player = new YandexAudio("html5");
55 | var equalizer = null;
56 |
57 | // Дожидаемся завершения инициализации плеера
58 | player.initPromise().then(function() {
59 | if (!player.toggleWebAudioAPI(true)) {
60 | // Если не удается включить Web Audio API, значит либо запустился flash-плеер, либо нет поддержки Web Audio API
61 | console.warn("Эквалайзер недоступен");
62 | } else {
63 | // Создаем эквалайзер со стандартным набором полос пропускания
64 | equalizer = new Equalizer(YandexAudio.audioContext, Equalizer.DEFAULT_BANDS);
65 |
66 | // Находим нужный пресет из списка стандартных
67 | for (var i = 0, l = Equalizer.DEFAULT_PRESETS.length; i < l; i++) {
68 | if (Equalizer.DEFAULT_PRESETS[i].id === "Full Bass & Treble") {
69 | var preset = Equalizer.DEFAULT_PRESETS[i];
70 | break;
71 | }
72 | }
73 |
74 | // Загружаем пресет в эквалайзер
75 | equalizer.loadPreset(preset);
76 |
77 | // Подключаем эквалайзер к плееру
78 | player.setAudioPreprocessor(equalizer);
79 | }
80 | });
81 | ```
82 |
83 | Тут стоит пояснить некоторые вещи.
84 |
85 | Полосой пропускания называется диапазон частот, к которому применяется некий фильтр. В нашем случае этот фильтр реализован с помощью `BiquadFilterNode`, для первой и последней полосы он имеет тип `lowshelf` и `highshelf` соответственно, а для всех остальных - `peaking`. Подробнее про это можно почитать в статье [Web Audio API](web-audio-api.md) в разделе **BiquadFilterNode**. Фильтры для каждой полосы соединены последовательно начиная с самой маленькой частоты и заканчивая самой высокой. В данном примере используется стандартный набор из 10 полос с частотами `[60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]` (значения указаны в герцах).
86 |
87 | Пресеты эквалайзера - это набор настроек усиления для каждой полосы пропускания, плюс значение предусиления. Предусиление выбирается таким образом, чтобы после применения эквалайзера общая громкость звука сохранялась неизменной. Альтернативный подход - использовать предусиление, с отрицательным значением равным максимальному усилению среди всех полос, чтобы гарантированно избежать клиппинга сигнала в результате последующей обработки (однако, в данном случае общая громкость будет ниже, чем у входного сигнала).
88 |
89 |
90 |
91 | Громкость
92 | ---------
93 |
94 | С данным подмодулем все намного проще - это всего лишь набор формул для перевода следующих величин:
95 |
96 | - **dBFS** - полный динамический диапазон. Шкала в децибелах от минус бесконечности до нуля (подробнее в статье про [теорию звука](sound.md) в разделе **Уровень сигнала**),
97 | - экспоненциальная шкала - шкала относительной громкости (от 0 до 1) с экспоненциальным шагом. Позволяет более точно регулировать громкость вблизи нижней границы и делает более существенные изменения громкости вблизи верхней границы.
98 |
99 | Оригинальная шкала громкости, является линейной шкалой относительной громкости (от 0 до 1). Все методы данного подмодуля переводят значения из этой шкалы или в эту шкалу.
100 |
--------------------------------------------------------------------------------
/jsdoc/doc/publish.js:
--------------------------------------------------------------------------------
1 | var util = require("util");
2 | var render = require('./render');
3 | var fs = require("fs");
4 |
5 | var makeKinds = function() {
6 | return {
7 | "class": [],
8 | "typedef": [],
9 | "namespace": [],
10 |
11 | "event": [],
12 |
13 | "function": [],
14 | "member": [],
15 |
16 | // "file": [],
17 | "package": []
18 | };
19 | };
20 |
21 | var checkDoc = function(data, includePrivate) {
22 | return (includePrivate || !data.access || data.access == "public")
23 | && (!data.undocumented)
24 | && (!data.ignore);
25 | };
26 |
27 | var optimize = function(tree, indent) {
28 | indent = indent || 0;
29 | var orig = Object.keys(tree.sub);
30 | var key, keys;
31 |
32 | for (var k = 0; k < orig.length; k++) {
33 | key = orig[k];
34 | keys = Object.keys(tree.sub[key].sub);
35 |
36 | if (keys.length == 1 && !tree.sub[key].sub[keys[0]].link) {
37 | tree.sub[key + "." + keys[0]] = tree.sub[key].sub[keys[0]];
38 | delete tree.sub[key];
39 | orig.push(key + "." + keys[0]);
40 | } else {
41 | optimize(tree.sub[key], indent + 1);
42 | }
43 | }
44 | tree.indent = new Array(indent + 1).join(" ");
45 | };
46 | var makePath = function(path, name) {
47 | path = path.split(".");
48 | // var symbol = path.pop();
49 | var ns = docs.exportTree;
50 | var full = "";
51 |
52 | path.forEach(function(chunk) {
53 | full += (full ? "." : "") + chunk;
54 | if (!ns.sub[chunk]) {
55 | ns.sub[chunk] = {
56 | sub: {},
57 | indent: 0,
58 | full: full,
59 | link: null
60 | };
61 | }
62 | ns = ns.sub[chunk];
63 | });
64 |
65 | ns.link = name;
66 | };
67 |
68 | var weight = function(data) {
69 | return (data.kind != "class" && data.static ? 10 : 0)
70 | + (data.inner ? -1 : 0)
71 | + (data.exported ? 5 : 0);
72 | };
73 | var sort = function(kinds) {
74 | for (var kind in kinds) {
75 | if (Array.isArray(kinds[kind])) {
76 | kinds[kind] = kinds[kind].sort(function(a, b) {
77 | return weight(b) - weight(a);
78 | });
79 | }
80 | }
81 | };
82 |
83 | var fixParams = function(params) {
84 | params.forEach(function(param) {
85 | param.description = param.description && param.description.replace(/\n/g, " ");
86 |
87 | param.type && param.type.names && (param.type.names = param.type.names.map(function(type) {
88 | type = type
89 | .replace(/[\w.#~]*~/, "")
90 | .replace(/\(|\)/g, "");
91 |
92 | return type;
93 | }));
94 | });
95 | return params;
96 | };
97 | var fixes = function(data) {
98 | data.type && fixParams([data]);
99 | data.params && fixParams(data.params);
100 | data.properties && fixParams(data.properties);
101 | data.returns && fixParams(data.returns) && (data.returns = data.returns[0]);
102 |
103 | if (data.kind === 'constant') {
104 | data.kind = 'member';
105 | data.const = true;
106 | }
107 | if (data.kind === 'namespace') {
108 | data.namespace = true;
109 | }
110 |
111 | if (data.kind === 'class') {
112 | data["class"] = true;
113 | }
114 |
115 | if (data.scope === 'static') {
116 | data.static = true;
117 | } else if (data.scope === 'inner') {
118 | data.inner = true;
119 | }
120 |
121 | data.fires && (data.fires = data.fires.map(function(event) { return event.replace("event:", ""); }));
122 | if (data.kind === 'event') { data.longname = data.longname.replace("event:", ""); }
123 |
124 | data.tags && data.tags.forEach(function(tag) {
125 | if (tag.title === "exported") {
126 | data.exported = tag.value;
127 | makePath(tag.value, data.name);
128 | }
129 | if (tag.title === "unignore") {
130 | data.unignore = true;
131 | }
132 | });
133 | };
134 |
135 | var subtree = function(taffy, data) {
136 | data.children = makeKinds();
137 |
138 | taffy({memberof: data.longname}).each(function(member) {
139 | if (!checkDoc(member)) {
140 | return;
141 | }
142 |
143 | if (data.children[member.kind]) {
144 | data.children[member.kind].push(member);
145 | } else {
146 | console.warn("Unexpected type", member.kind);
147 | return;
148 | }
149 |
150 | member.parent = data;
151 | subtree(taffy, member);
152 | });
153 |
154 | delete data.children.package;
155 | sort(data.children);
156 | };
157 |
158 | var docs = {
159 | exportTree: {
160 | sub: {},
161 | symbols: []
162 | },
163 | links: {},
164 | tree: makeKinds(),
165 | linear: makeKinds()
166 | };
167 |
168 | var pendingExported = {};
169 | var unignore = {};
170 |
171 | var prepare = function(taffy, style) {
172 | taffy().each(function(data) {
173 | fixes(data);
174 |
175 | if (unignore[data.longname]) {
176 | data.ignore = false;
177 | }
178 |
179 | if (!checkDoc(data)) {
180 | if (data.exported) {
181 | pendingExported[data.longname] = data.exported;
182 | }
183 | if (data.unignore) {
184 | unignore[data.longname] = true;
185 | }
186 |
187 | return;
188 | }
189 |
190 | if (pendingExported[data.longname]) {
191 | data.exported = pendingExported[data.longname];
192 | }
193 |
194 | if (data.kind === 'function' && !data.description) {
195 | // console.log(util.inspect(data, {color: true, depth: 0}));
196 | }
197 |
198 | if (!docs.linear[data.kind]) {
199 | console.warn("Unexpected kind", data.kind);
200 | return;
201 | }
202 |
203 | var link = (data.memberof && !data.inner ? data.memberof + (
204 | data.static ? "." : "#")
205 | : ""
206 | ) + data.name;
207 |
208 | docs.links[link] = data;
209 |
210 | if (data.scope === 'global'
211 | || data.kind === 'class'
212 | || data.kind === 'typedef'
213 | || data.kind === 'namespace') {
214 | docs.linear[data.kind].push(data);
215 | }
216 | });
217 |
218 | taffy({scope: "global"}).each(function(data) {
219 | if (!checkDoc(data)) {
220 | return;
221 | }
222 |
223 | if (docs.tree[data.kind]) {
224 | docs.tree[data.kind].push(data);
225 | subtree(taffy, data);
226 | }
227 | });
228 |
229 | delete docs.tree.package;
230 | delete docs.linear.package;
231 |
232 | sort(docs.tree);
233 | sort(docs.linear);
234 |
235 | if (!/jsdoc/.test(style)) {
236 | optimize(docs.exportTree);
237 | }
238 | };
239 |
240 | var styles = {
241 | "jsdoc": "jsdoc",
242 | "jsdoc-tech": "jsdoc",
243 | "gfm-single": "md",
244 | "gfm-files": "md"
245 | };
246 |
247 | exports.publish = function(taffyData, opts, tutorials) {
248 | var style = opts.query && opts.query.style || "gfm-single";
249 | var out = opts.query && opts.query.out || "readme";
250 |
251 | prepare(taffyData, style);
252 |
253 | render.prepare(opts.template, style);
254 | var files = render.render(docs, out);
255 |
256 | var ext = "." + styles[style];
257 |
258 | try {
259 | fs.mkdirSync(opts.destination);
260 | } catch(e) {}
261 |
262 | for (var name in files) {
263 | fs.writeFileSync(opts.destination + name.replace(/~/g, "-") + ext, files[name]);
264 | }
265 |
266 | // console.log(util.inspect(docs.linear["class"], {color: true, depth: 2}));
267 | };
268 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | YandexAudio
2 | ===========
3 | YandexAudio — JavaScript-библиотека, предназначенная для воспроизведения аудиоданных в браузере. Работа YandexAudio базируется на технологиях html5 audio и flash. Библиотека предоставляет удобный интерфейс для добавления этих технологий на страницу и управления их настройками. При этом YandexAudio решает основные проблемы, связанные с использованием audio и flash – неполная поддержка браузерами, трудная отладка ошибок и др.
4 |
5 | Основные возможности YandexAudio:
6 | - автоматическое определение поддерживаемых браузером технологий;
7 | - подключение flash-плеера на страницу с возможностью отображения видимого flash-апплета;
8 | - автоматическая перезагрузка плеера при возникновении ошибки (например из-за заблокированного flash-содержимого);
9 | - предзагрузка следующего трека, параллельно с воспроизведением текущего;
10 | - детектирование и использование технологий Web Audio API;
11 | - эквалайзер (с готовым набором пресетов) с возможностью настройки количества и частоты полос пропускания.
12 |
13 |
14 |
15 | Подключение
16 | ----------
17 | Существует 2 способа подключения данной библиотеки:
18 |
19 | - **npm** - если ваш проект использует сборку скриптов с помощью browserify или аналога, то можно просто подключать
20 | библиотку как npm-пакет `var YandexAudio = require('YandexAudio')`;
21 | - **скрипт** - достаточно подключить основной файл скрипта
22 | ([dist/index.js](https://music.yandex.ru/api/audio/dist/index.js)
23 | или [dist/index.min.js](https://music.yandex.ru/api/audio/dist/index.min.js) - минифицированную версию)
24 | в тело страницы и далее использовать глобально доступный объект [spec/Audio.md](ya.music.Audio).
25 |
26 |
27 | Использование
28 | ------------
29 | ### Создание экземпляра плеера:
30 |
31 | ```javascript
32 | var audioPlayer = new ya.music.Audio(preferredPlayerType, flashOverlayElement);
33 | audioPlayer.initPromise().then(function() {
34 | console.log("Аудио-плеер готов к работе");
35 | }, function() {
36 | console.error("Не удалось инициализировать аудио-плеер");
37 | });
38 | ```
39 |
40 | - **preferredPlayerType** - предпочитаемый тип плеера. Может принимать значения: `"html5"`, `"flash"` или любое ложное значение (false, null, undefined, 0, ""). Если выбранный тип плеера окажется недоступен, будет запущен оставшийся тип. Если параметр не передан, либо указано ложное значение, то API автоматически выберет поддерживаемый тип плеера.
41 | - **flashOverlayElement** - HTMLElement, в который требуется встроить flash-апплет. Передается в том случае, если необходимо отобразить видимый flash-апплет для отключения различных блокировщиков flash'а.
42 |
43 | ### Запуск воспроизведения
44 |
45 | ```javascript
46 | audioPlayer.play(src).then(function() {
47 | console.log("Воспроизведение успешно началось");
48 | }, function(err) {
49 | console.error("Не удалось начать воспроизведенние", err);
50 | });
51 | ```
52 |
53 | ### Управление воспроизведением
54 |
55 | ```javascript
56 | audioPlayer.pause(); // пауза
57 | audioPlayer.resume(); // продолжение воспроизведения
58 | audioPlayer.stop(); // остановка воспроизведения и загрузки трека
59 |
60 | console.log("Ссылка на текущий трек", audioPlayer.getSrc());
61 | console.log("Длительность трека", audioPlayer.getDuration());
62 | console.log("Текущая позиция воспроизведения", audioPlayer.getPosition());
63 | console.log("Длительность загруженной части", audioPlayer.getLoaded());
64 | console.log("Время воспроизведения трека", audioPlayer.getPlayed());
65 |
66 | console.log("Новая позиция воспроизведения", audioPlayer.setPosition(position));
67 | ```
68 |
69 | ### Прослушивание событий
70 |
71 | ```javascript
72 | audioPlayer.on(ya.music.Audio.EVENT_STATE, function(state) {
73 | switch(state) {
74 | case ya.music.Audio.STATE_INIT: console.log("Инициализация плеера"); break;
75 | case ya.music.Audio.STATE_IDLE: console.log("Плеер готов и ожидает"); break;
76 | case ya.music.Audio.STATE_PLAYING: console.log("Плеер проигрывает музыку"); break;
77 | case ya.music.Audio.STATE_PAUSED: console.log("Плеер поставлен на паузу"); break;
78 | case ya.music.Audio.STATE_CRASHED: console.log("Не удалось инициализировать плеер"); break;
79 | }
80 | });
81 |
82 | var logEvent = function(text) { return function(data) { console.log(text, data); }; };
83 | audioPlayer.on(ya.music.Audio.EVENT_PLAY, logEvent("Плеер начал воспроизведение трека"));
84 | audioPlayer.on(ya.music.Audio.EVENT_STOP, logEvent("Остановка воспроизведения"));
85 | audioPlayer.on(ya.music.Audio.EVENT_PAUSE, logEvent("Пауза воспроизведения"));
86 |
87 | audioPlayer.on(ya.music.Audio.EVENT_PROGRESS, logEvent("Обновление позиции воспроизведения"));
88 | audioPlayer.on(ya.music.Audio.EVENT_ENDED, logEvent("Воспроизведение трека завершено"));
89 |
90 | audioPlayer.on(ya.music.Audio.EVENT_LOADING, logEvent("Трек начал загружаться"));
91 | audioPlayer.on(ya.music.Audio.EVENT_LOADED, logEvent("Трек загружен полностью"));
92 |
93 | audioPlayer.on(ya.music.Audio.EVENT_VOLUME, logEvent("Изменение громкости"));
94 |
95 | audioPlayer.on(ya.music.Audio.EVENT_ERROR, logEvent("Возникла ошибка при воспроизведении"));
96 | audioPlayer.on(ya.music.Audio.EVENT_CRASHED, logEvent("Крах инициализации"));
97 |
98 | audioPlayer.on(ya.music.Audio.EVENT_SWAP, logEvent("Переключение между текущим и предзагруженным треком"));
99 | ```
100 |
101 | ### Прелоадер
102 | В большинство команд управления можно передать вторым аргументом `1`, чтобы они применялись к прелоадеру вместо текущего плеера.
103 | Для прослушивания событий плероадера следует использовать префикс `ya.music.Audio.PRELOADER_EVENT`.
104 |
105 | ```javascript
106 | // Следует обратить внимание, что обещание разрешится, когда трек начал загружаться, а не когда загрузился
107 | audioPlayer.preload(src).then(function() {
108 | console.log("Началась предзагрузка трека");
109 | }, function(err) {
110 | console.error("Ну удалось начать загрузку трека", err);
111 | });
112 |
113 | audioPlayer.playPreloaded().then(function() {
114 | console.log("Воспроизведение успешно началось");
115 | }, function(err) {
116 | console.error("Не удалось начать воспроизведенние", err);
117 | });
118 |
119 | audioPlayer.stop(1); // остановка загрузки трека
120 |
121 | console.log("Ссылка на текущий трек", audioPlayer.getSrc(1));
122 | console.log("Длительность трека", audioPlayer.getDuration(1));
123 | console.log("Длительность загруженной части", audioPlayer.getLoaded(1));
124 |
125 | // Два похожих метода, но первый является сахаром для audioPlayer.getSrc(1) == src, а второй проверяет успех начала загрузки
126 | console.log("Трек " + (audioPlayer.isPreloading(src) ? "загружается/ожидает загрузки" : "не загружается"));
127 | console.log("Трек " + (audioPlayer.isPreloaded(src) ? "начал загружаться" : "не загружается"));
128 |
129 | var logEvent = function(text) { return function(data) { console.log(text, data); }; };
130 | audioPlayer.on(ya.music.Audio.PRELOADER_EVENT + ya.music.Audio.EVENT_STOP, logEvent("Остановка загрузки"));
131 |
132 | audioPlayer.on(ya.music.Audio.PRELOADER_EVENT + ya.music.Audio.EVENT_PROGRESS, logEvent("Процесс загрузки"));
133 |
134 | audioPlayer.on(ya.music.Audio.PRELOADER_EVENT + ya.music.Audio.EVENT_LOADING, logEvent("Трек начал загружаться"));
135 | audioPlayer.on(ya.music.Audio.PRELOADER_EVENT + ya.music.Audio.EVENT_LOADED, logEvent("Трек загружен полностью"));
136 |
137 | audioPlayer.on(ya.music.Audio.PRELOADER_EVENT + ya.music.Audio.EVENT_ERROR, logEvent("Возникла ошибка при загрузке"));
138 | ```
139 |
140 | ### Документация
141 | * [Справочник API](spec)
142 | * [Быстрый старт](tutorial/quick-start.md)
143 | * [Обработка звука](tutorial/fx.md)
144 | * [Подводные камни](tutorial/corner-case.md)
145 | * [Полезная теоретическая информация](tutorial/sound.md)
146 | * [Web Audio API](tutorial/web-audio-api.md)
147 | * [Инструкции для контрибьюторов](tutorial/contrib.md)
148 |
--------------------------------------------------------------------------------