mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-04 10:19:25 +02:00
Add Aurora.js support in webplayer
Add transcode settings per player
This commit is contained in:
parent
83b41887cc
commit
2bbed12eb7
25 changed files with 18340 additions and 134 deletions
|
@ -719,6 +719,21 @@ refresh_limit = "60"
|
|||
; DEFAULT: none
|
||||
;encode_target_flac = ogg
|
||||
|
||||
; Override the default TYPE transcoding behavior on a per-player basis
|
||||
; transcode_player_PLAYER_TYPE = TYPE
|
||||
; Valid PLAYER is: webplayer, api
|
||||
; DEFAULT: none
|
||||
;transcode_player_webplayer_m4a = required
|
||||
;transcode_player_webplayer_flac = required
|
||||
;transcode_player_webplayer_mpc = required
|
||||
|
||||
; Override the default output format on a per-player basis
|
||||
; encode_player_PLAYER_target = TYPE
|
||||
; Valid PLAYER is: webplayer, api
|
||||
; DEFAULT: none
|
||||
;encode_player_webplayer_target = mp3
|
||||
;encode_player_api_target = mp3
|
||||
|
||||
; Allow clients to override transcode settings (output type, bitrate, codec ...)
|
||||
; DEFAULT: true
|
||||
transcode_player_customize = true
|
||||
|
|
|
@ -394,7 +394,7 @@ class Channel extends database_object implements media, library_item
|
|||
$options = array(
|
||||
'bitrate' => $this->bitrate
|
||||
);
|
||||
$this->transcoder = Stream::start_transcode($this->media, $this->stream_type, $options);
|
||||
$this->transcoder = Stream::start_transcode($this->media, $this->stream_type, null, $options);
|
||||
$this->media_bytes_streamed = 0;
|
||||
}
|
||||
|
||||
|
@ -463,7 +463,7 @@ class Channel extends database_object implements media, library_item
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
public function get_transcode_settings($array, $options=array())
|
||||
public function get_transcode_settings($target = null, $player = null, $options=array())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -268,7 +268,7 @@ class Live_Stream extends database_object implements media, library_item
|
|||
*
|
||||
* This will probably never be implemented
|
||||
*/
|
||||
public function get_transcode_settings($target = null, $options=array())
|
||||
public function get_transcode_settings($target = null, $player = null, $options=array())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ interface media
|
|||
* parameter can be used to request a specific format instead of the
|
||||
* default from the configuration file.
|
||||
*/
|
||||
public function get_transcode_settings($target = null, $options=array());
|
||||
public function get_transcode_settings($target = null, $player = null, $options=array());
|
||||
|
||||
/**
|
||||
* get_stream_name
|
||||
|
|
|
@ -469,7 +469,7 @@ class Preference extends database_object
|
|||
if (strlen($results['theme_name']) > 0) {
|
||||
$results['theme_path'] = '/themes/' . $results['theme_name'];
|
||||
// In case the theme was removed
|
||||
if (Core::is_readable(AmpConfig::get('web_path') . $results['theme_path'])) {
|
||||
if (Core::is_readable(AmpConfig::get('prefix') . $results['theme_path'])) {
|
||||
unset($results['theme_path']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1493,8 +1493,8 @@ class Song extends database_object implements media, library_item
|
|||
// Checking if the media is gonna be transcoded into another type
|
||||
// Some players doesn't allow a type streamed into another without giving the right extension
|
||||
$transcode_cfg = AmpConfig::get('transcode');
|
||||
$transcode_mode = AmpConfig::get('transcode_' . $type);
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && $transcode_mode == 'required')) {
|
||||
$valid_types = Song::get_stream_types_for_type($song->type, 'api');
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && !in_array('native', $valid_types))) {
|
||||
$transcode_settings = $media->get_transcode_settings(null);
|
||||
if ($transcode_settings) {
|
||||
debug_event("media", "Changing play url type from {".$type."} to {".$transcode_settings['format']."} due to encoding settings...", 5);
|
||||
|
@ -1596,10 +1596,16 @@ class Song extends database_object implements media, library_item
|
|||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public static function get_stream_types_for_type($type)
|
||||
public static function get_stream_types_for_type($type, $player = null)
|
||||
{
|
||||
$types = array();
|
||||
$transcode = AmpConfig::get('transcode_' . $type);
|
||||
if ($player) {
|
||||
$player_transcode = AmpConfig::get('transcode_player_' . $player . '_' . $type);
|
||||
if ($player_transcode) {
|
||||
$transcode = $player_transcode;
|
||||
}
|
||||
}
|
||||
|
||||
if ($transcode != 'required') {
|
||||
$types[] = 'native';
|
||||
|
@ -1619,13 +1625,23 @@ class Song extends database_object implements media, library_item
|
|||
* @param array $options
|
||||
* @return array|boolean
|
||||
*/
|
||||
public static function get_transcode_settings_for_media($source, $target = null, $media_type = 'song', $options=array())
|
||||
public static function get_transcode_settings_for_media($source, $target = null, $player = null, $media_type = 'song', $options=array())
|
||||
{
|
||||
$setting_target = 'encode_target';
|
||||
if ($media_type != 'song') {
|
||||
$setting_target = 'encode_' . $media_type . '_target';
|
||||
}
|
||||
|
||||
if ($player) {
|
||||
$player_setting_target = 'encode_player_' . $player . '_target';
|
||||
if ($media_type != 'song') {
|
||||
$player_setting_target = 'encode_' . $media_type . '_player_' . $player . '_target';
|
||||
}
|
||||
if (AmpConfig::get($player_setting_target)) {
|
||||
$setting_target = $player_setting_target;
|
||||
}
|
||||
}
|
||||
|
||||
if ($target) {
|
||||
debug_event('media', 'Explicit format request {' . $target . '}', 5);
|
||||
} else if ($target = AmpConfig::get('encode_target_' . $source)) {
|
||||
|
@ -1672,9 +1688,9 @@ class Song extends database_object implements media, library_item
|
|||
* @param array $options
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function get_transcode_settings($target = null, $options=array())
|
||||
public function get_transcode_settings($target = null, $player = null, $options=array())
|
||||
{
|
||||
return Song::get_transcode_settings_for_media($this->type, $target, 'song', $options);
|
||||
return Song::get_transcode_settings_for_media($this->type, $target, $player, 'song', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -303,7 +303,7 @@ class Song_Preview extends database_object implements media, playable_item
|
|||
*
|
||||
* FIXME: Song Preview transcoding is not implemented
|
||||
*/
|
||||
public function get_transcode_settings($target = null, $options=array())
|
||||
public function get_transcode_settings($target = null, $player = null, $options=array())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -136,11 +136,11 @@ class Stream
|
|||
* This is a rather complex function that starts the transcoding or
|
||||
* resampling of a media and returns the opened file handle.
|
||||
*/
|
||||
public static function start_transcode($media, $type = null, $options = array())
|
||||
public static function start_transcode($media, $type = null, $player = null, $options = array())
|
||||
{
|
||||
debug_event('stream.class.php', 'Starting transcode for {'.$media->file.'}. Type {'.$type.'}. Options: ' . print_r($options, true) . '}...', 5);
|
||||
|
||||
$transcode_settings = $media->get_transcode_settings($type, $options);
|
||||
$transcode_settings = $media->get_transcode_settings($type, $player, $options);
|
||||
// Bail out early if we're unutterably broken
|
||||
if ($transcode_settings == false) {
|
||||
debug_event('stream', 'Transcode requested, but get_transcode_settings failed', 2);
|
||||
|
|
|
@ -350,9 +350,9 @@ class Subsonic_XML_Data
|
|||
|
||||
// Set transcoding information if required
|
||||
$transcode_cfg = AmpConfig::get('transcode');
|
||||
$transcode_mode = AmpConfig::get('transcode_' . $song->type);
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && $transcode_mode == 'required')) {
|
||||
$transcode_settings = $song->get_transcode_settings(null);
|
||||
$valid_types = Song::get_stream_types_for_type($song->type, 'api');
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && !in_array('native', $valid_types))) {
|
||||
$transcode_settings = $song->get_transcode_settings(null, 'api');
|
||||
if ($transcode_settings) {
|
||||
$transcode_type = $transcode_settings['format'];
|
||||
$xsong->addAttribute('transcodedSuffix', $transcode_type);
|
||||
|
@ -448,9 +448,9 @@ class Subsonic_XML_Data
|
|||
|
||||
// Set transcoding information if required
|
||||
$transcode_cfg = AmpConfig::get('transcode');
|
||||
$transcode_mode = AmpConfig::get('transcode_' . $video->type);
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && $transcode_mode == 'required')) {
|
||||
$transcode_settings = $video->get_transcode_settings(null);
|
||||
$valid_types = Song::get_stream_types_for_type($song->type, 'api');
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && !in_array('native', $valid_types))) {
|
||||
$transcode_settings = $video->get_transcode_settings(null, 'api');
|
||||
if ($transcode_settings) {
|
||||
$transcode_type = $transcode_settings['format'];
|
||||
$xvideo->addAttribute('transcodedSuffix', $transcode_type);
|
||||
|
|
|
@ -467,6 +467,9 @@ class Update
|
|||
$update_string = '- Add users geolocation.<br />';
|
||||
$version[] = array('version' => '370022','description' => $update_string);
|
||||
|
||||
$update_string = " - Add Aurora.js webplayer option<br />";
|
||||
$version[] = array('version' => '370023','description' => $update_string);
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
|
@ -3155,4 +3158,25 @@ class Update
|
|||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* update 370023
|
||||
*
|
||||
* Add Aurora.js webplayer option
|
||||
*/
|
||||
public static function update_370023()
|
||||
{
|
||||
$retval = true;
|
||||
|
||||
$sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " .
|
||||
"VALUES ('webplayer_aurora','1','Authorize JavaScript decoder (Aurora.js) in Web Player(s)',25,'boolean','streaming')";
|
||||
$retval = Dba::write($sql) ? $retval : false;
|
||||
|
||||
$id = Dba::insert_id();
|
||||
|
||||
$sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')";
|
||||
$retval = Dba::write($sql, array($id)) ? $retval : false;
|
||||
|
||||
return $retval;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -426,9 +426,9 @@ class Video extends database_object implements media, library_item
|
|||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function get_transcode_settings($target = null, $options=array())
|
||||
public function get_transcode_settings($target = null, $player = null, $options=array())
|
||||
{
|
||||
return Song::get_transcode_settings_for_media($this->type, $target, 'video', $options);
|
||||
return Song::get_transcode_settings_for_media($this->type, $target, $player, 'video', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -120,17 +120,16 @@ class WebPlayer
|
|||
$transcode = false;
|
||||
$transcode_cfg = AmpConfig::get('transcode');
|
||||
// Check transcode is required
|
||||
$ftype_transcode = AmpConfig::get('transcode_' . $ftype);
|
||||
$valid_types = Song::get_stream_types_for_type($ftype);
|
||||
if ($transcode_cfg == 'always' || !empty($force_type) || $ftype_transcode == 'required' || ($types['real'] != $ftype && (!AmpConfig::get('webplayer_flash') || $urlinfo['type'] != 'song'))) {
|
||||
$valid_types = Song::get_stream_types_for_type($ftype, 'webplayer');
|
||||
if ($transcode_cfg == 'always' || !empty($force_type) || !in_array('native', $valid_types) || ($types['real'] != $ftype && (!AmpConfig::get('webplayer_flash') || $urlinfo['type'] != 'song'))) {
|
||||
if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && in_array('transcode', $valid_types))) {
|
||||
// Transcode only if excepted type available
|
||||
$transcode_settings = $media->get_transcode_settings($types['real'], null, $urlinfo['type']);
|
||||
$transcode_settings = $media->get_transcode_settings($types['real'], 'webplayer');
|
||||
if ($transcode_settings && AmpConfig::get('transcode_player_customize')) {
|
||||
$transcode = true;
|
||||
} else {
|
||||
if (!in_array('native', $valid_types)) {
|
||||
$transcode_settings = $media->get_transcode_settings(null);
|
||||
$transcode_settings = $media->get_transcode_settings(null, 'webplayer');
|
||||
if ($transcode_settings) {
|
||||
$types['real'] = $transcode_settings['format'];
|
||||
$transcode = true;
|
||||
|
|
|
@ -192,6 +192,7 @@ function create_preference_input($name,$value)
|
|||
case 'browser_notify':
|
||||
case 'allow_video':
|
||||
case 'geolocation':
|
||||
case 'webplayer_aurora':
|
||||
$is_true = '';
|
||||
$is_false = '';
|
||||
if ($value == '1') {
|
||||
|
|
4651
modules/aurora.js/aac.js
Normal file
4651
modules/aurora.js/aac.js
Normal file
File diff suppressed because it is too large
Load diff
725
modules/aurora.js/alac.js
Normal file
725
modules/aurora.js/alac.js
Normal file
|
@ -0,0 +1,725 @@
|
|||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
// Generated by CoffeeScript 1.7.1
|
||||
(function() {
|
||||
var Aglib;
|
||||
|
||||
Aglib = (function() {
|
||||
var BITOFF, KB0, MAX_DATATYPE_BITS_16, MAX_PREFIX_16, MAX_PREFIX_32, MAX_RUN_DEFAULT, MB0, MDENSHIFT, MMULSHIFT, MOFF, N_MAX_MEAN_CLAMP, N_MEAN_CLAMP_VAL, PB0, QB, QBSHIFT, dyn_get_16, dyn_get_32, lead;
|
||||
|
||||
function Aglib() {}
|
||||
|
||||
PB0 = 40;
|
||||
|
||||
MB0 = 10;
|
||||
|
||||
KB0 = 14;
|
||||
|
||||
MAX_RUN_DEFAULT = 255;
|
||||
|
||||
MAX_PREFIX_16 = 9;
|
||||
|
||||
MAX_PREFIX_32 = 9;
|
||||
|
||||
QBSHIFT = 9;
|
||||
|
||||
QB = 1 << QBSHIFT;
|
||||
|
||||
MMULSHIFT = 2;
|
||||
|
||||
MDENSHIFT = QBSHIFT - MMULSHIFT - 1;
|
||||
|
||||
MOFF = 1 << (MDENSHIFT - 2);
|
||||
|
||||
N_MAX_MEAN_CLAMP = 0xFFFF;
|
||||
|
||||
N_MEAN_CLAMP_VAL = 0xFFFF;
|
||||
|
||||
MMULSHIFT = 2;
|
||||
|
||||
BITOFF = 24;
|
||||
|
||||
MAX_DATATYPE_BITS_16 = 16;
|
||||
|
||||
lead = function(input) {
|
||||
var curbyte, output;
|
||||
output = 0;
|
||||
curbyte = 0;
|
||||
while (true) {
|
||||
curbyte = input >>> 24;
|
||||
if (curbyte) {
|
||||
break;
|
||||
}
|
||||
output += 8;
|
||||
curbyte = input >>> 16;
|
||||
if (curbyte & 0xff) {
|
||||
break;
|
||||
}
|
||||
output += 8;
|
||||
curbyte = input >>> 8;
|
||||
if (curbyte & 0xff) {
|
||||
break;
|
||||
}
|
||||
output += 8;
|
||||
curbyte = input;
|
||||
if (curbyte & 0xff) {
|
||||
break;
|
||||
}
|
||||
output += 8;
|
||||
return output;
|
||||
}
|
||||
if (curbyte & 0xf0) {
|
||||
curbyte >>>= 4;
|
||||
} else {
|
||||
output += 4;
|
||||
}
|
||||
if (curbyte & 0x8) {
|
||||
return output;
|
||||
}
|
||||
if (curbyte & 0x4) {
|
||||
return output + 1;
|
||||
}
|
||||
if (curbyte & 0x2) {
|
||||
return output + 2;
|
||||
}
|
||||
if (curbyte & 0x1) {
|
||||
return output + 3;
|
||||
}
|
||||
return output + 4;
|
||||
};
|
||||
|
||||
dyn_get_16 = function(data, m, k) {
|
||||
var bitsInPrefix, offs, result, stream, v;
|
||||
offs = data.bitPosition;
|
||||
stream = data.peek(32 - offs) << offs;
|
||||
bitsInPrefix = lead(~stream);
|
||||
if (bitsInPrefix >= MAX_PREFIX_16) {
|
||||
data.advance(MAX_PREFIX_16 + MAX_DATATYPE_BITS_16);
|
||||
stream <<= MAX_PREFIX_16;
|
||||
result = stream >>> (32 - MAX_DATATYPE_BITS_16);
|
||||
} else {
|
||||
data.advance(bitsInPrefix + k);
|
||||
stream <<= bitsInPrefix + 1;
|
||||
v = stream >>> (32 - k);
|
||||
result = bitsInPrefix * m + v - 1;
|
||||
if (v < 2) {
|
||||
result -= v - 1;
|
||||
} else {
|
||||
data.advance(1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
dyn_get_32 = function(data, m, k, maxbits) {
|
||||
var offs, result, stream, v;
|
||||
offs = data.bitPosition;
|
||||
stream = data.peek(32 - offs) << offs;
|
||||
result = lead(~stream);
|
||||
if (result >= MAX_PREFIX_32) {
|
||||
data.advance(MAX_PREFIX_32);
|
||||
return data.read(maxbits);
|
||||
} else {
|
||||
data.advance(result + 1);
|
||||
if (k !== 1) {
|
||||
stream <<= result + 1;
|
||||
result *= m;
|
||||
v = stream >>> (32 - k);
|
||||
data.advance(k - 1);
|
||||
if (v > 1) {
|
||||
result += v - 1;
|
||||
data.advance(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Aglib.ag_params = function(m, p, k, f, s, maxrun) {
|
||||
return {
|
||||
mb: m,
|
||||
mb0: m,
|
||||
pb: p,
|
||||
kb: k,
|
||||
wb: (1 << k) - 1,
|
||||
qb: QB - p,
|
||||
fw: f,
|
||||
sw: s,
|
||||
maxrun: maxrun
|
||||
};
|
||||
};
|
||||
|
||||
Aglib.dyn_decomp = function(params, data, pc, samples, maxSize) {
|
||||
var c, j, k, kb, m, mb, multiplier, mz, n, ndecode, pb, wb, zmode, _i;
|
||||
pb = params.pb, kb = params.kb, wb = params.wb, mb = params.mb0;
|
||||
zmode = 0;
|
||||
c = 0;
|
||||
while (c < samples) {
|
||||
m = mb >>> QBSHIFT;
|
||||
k = Math.min(31 - lead(m + 3), kb);
|
||||
m = (1 << k) - 1;
|
||||
n = dyn_get_32(data, m, k, maxSize);
|
||||
ndecode = n + zmode;
|
||||
multiplier = -(ndecode & 1) | 1;
|
||||
pc[c++] = ((ndecode + 1) >>> 1) * multiplier;
|
||||
mb = pb * (n + zmode) + mb - ((pb * mb) >> QBSHIFT);
|
||||
if (n > N_MAX_MEAN_CLAMP) {
|
||||
mb = N_MEAN_CLAMP_VAL;
|
||||
}
|
||||
zmode = 0;
|
||||
if (((mb << MMULSHIFT) < QB) && (c < samples)) {
|
||||
zmode = 1;
|
||||
k = lead(mb) - BITOFF + ((mb + MOFF) >> MDENSHIFT);
|
||||
mz = ((1 << k) - 1) & wb;
|
||||
n = dyn_get_16(data, mz, k);
|
||||
if (!(c + n <= samples)) {
|
||||
return false;
|
||||
}
|
||||
for (j = _i = 0; _i < n; j = _i += 1) {
|
||||
pc[c++] = 0;
|
||||
}
|
||||
if (n >= 65535) {
|
||||
zmode = 0;
|
||||
}
|
||||
mb = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return Aglib;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = Aglib;
|
||||
|
||||
}).call(this);
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
// Generated by CoffeeScript 1.7.1
|
||||
(function() {
|
||||
var ALACDecoder, AV, Aglib, Dplib, Matrixlib,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
AV = (window.AV);
|
||||
|
||||
Aglib = require('./ag_dec');
|
||||
|
||||
Dplib = require('./dp_dec');
|
||||
|
||||
Matrixlib = require('./matrix_dec');
|
||||
|
||||
ALACDecoder = (function(_super) {
|
||||
var ID_CCE, ID_CPE, ID_DSE, ID_END, ID_FIL, ID_LFE, ID_PCE, ID_SCE;
|
||||
|
||||
__extends(ALACDecoder, _super);
|
||||
|
||||
function ALACDecoder() {
|
||||
return ALACDecoder.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
AV.Decoder.register('alac', ALACDecoder);
|
||||
|
||||
ID_SCE = 0;
|
||||
|
||||
ID_CPE = 1;
|
||||
|
||||
ID_CCE = 2;
|
||||
|
||||
ID_LFE = 3;
|
||||
|
||||
ID_DSE = 4;
|
||||
|
||||
ID_PCE = 5;
|
||||
|
||||
ID_FIL = 6;
|
||||
|
||||
ID_END = 7;
|
||||
|
||||
ALACDecoder.prototype.setCookie = function(cookie) {
|
||||
var data, predictorBuffer, _base;
|
||||
data = AV.Stream.fromBuffer(cookie);
|
||||
if (data.peekString(4, 4) === 'frma') {
|
||||
data.advance(12);
|
||||
}
|
||||
if (data.peekString(4, 4) === 'alac') {
|
||||
data.advance(12);
|
||||
}
|
||||
this.config = {
|
||||
frameLength: data.readUInt32(),
|
||||
compatibleVersion: data.readUInt8(),
|
||||
bitDepth: data.readUInt8(),
|
||||
pb: data.readUInt8(),
|
||||
mb: data.readUInt8(),
|
||||
kb: data.readUInt8(),
|
||||
numChannels: data.readUInt8(),
|
||||
maxRun: data.readUInt16(),
|
||||
maxFrameBytes: data.readUInt32(),
|
||||
avgBitRate: data.readUInt32(),
|
||||
sampleRate: data.readUInt32()
|
||||
};
|
||||
(_base = this.format).bitsPerChannel || (_base.bitsPerChannel = this.config.bitDepth);
|
||||
this.mixBuffers = [new Int32Array(this.config.frameLength), new Int32Array(this.config.frameLength)];
|
||||
predictorBuffer = new ArrayBuffer(this.config.frameLength * 4);
|
||||
this.predictor = new Int32Array(predictorBuffer);
|
||||
return this.shiftBuffer = new Int16Array(predictorBuffer);
|
||||
};
|
||||
|
||||
ALACDecoder.prototype.readChunk = function(data) {
|
||||
var buf, bytesShifted, ch, chanBits, channelIndex, channels, coefs, count, dataByteAlignFlag, denShift, elementInstanceTag, end, escapeFlag, i, j, kb, maxRun, mb, mixBits, mixRes, mode, num, numChannels, out16, output, params, partialFrame, pb, pbFactor, samples, shift, shiftbits, status, table, tag, unused, val, _i, _j, _k, _l, _m, _n, _o, _ref, _ref1, _ref2;
|
||||
if (!this.stream.available(4)) {
|
||||
return;
|
||||
}
|
||||
data = this.bitstream;
|
||||
samples = this.config.frameLength;
|
||||
numChannels = this.config.numChannels;
|
||||
channelIndex = 0;
|
||||
output = new ArrayBuffer(samples * numChannels * this.config.bitDepth / 8);
|
||||
end = false;
|
||||
while (!end) {
|
||||
tag = data.read(3);
|
||||
switch (tag) {
|
||||
case ID_SCE:
|
||||
case ID_LFE:
|
||||
case ID_CPE:
|
||||
channels = tag === ID_CPE ? 2 : 1;
|
||||
if (channelIndex + channels > numChannels) {
|
||||
throw new Error('Too many channels!');
|
||||
}
|
||||
elementInstanceTag = data.read(4);
|
||||
unused = data.read(12);
|
||||
if (unused !== 0) {
|
||||
throw new Error('Unused part of header does not contain 0, it should');
|
||||
}
|
||||
partialFrame = data.read(1);
|
||||
bytesShifted = data.read(2);
|
||||
escapeFlag = data.read(1);
|
||||
if (bytesShifted === 3) {
|
||||
throw new Error("Bytes are shifted by 3, they shouldn't be");
|
||||
}
|
||||
if (partialFrame) {
|
||||
samples = data.read(32);
|
||||
}
|
||||
if (escapeFlag === 0) {
|
||||
shift = bytesShifted * 8;
|
||||
chanBits = this.config.bitDepth - shift + channels - 1;
|
||||
mixBits = data.read(8);
|
||||
mixRes = data.read(8);
|
||||
mode = [];
|
||||
denShift = [];
|
||||
pbFactor = [];
|
||||
num = [];
|
||||
coefs = [];
|
||||
for (ch = _i = 0; _i < channels; ch = _i += 1) {
|
||||
mode[ch] = data.read(4);
|
||||
denShift[ch] = data.read(4);
|
||||
pbFactor[ch] = data.read(3);
|
||||
num[ch] = data.read(5);
|
||||
table = coefs[ch] = new Int16Array(32);
|
||||
for (i = _j = 0, _ref = num[ch]; _j < _ref; i = _j += 1) {
|
||||
table[i] = data.read(16);
|
||||
}
|
||||
}
|
||||
if (bytesShifted) {
|
||||
shiftbits = data.copy();
|
||||
data.advance(shift * channels * samples);
|
||||
}
|
||||
_ref1 = this.config, mb = _ref1.mb, pb = _ref1.pb, kb = _ref1.kb, maxRun = _ref1.maxRun;
|
||||
for (ch = _k = 0; _k < channels; ch = _k += 1) {
|
||||
params = Aglib.ag_params(mb, (pb * pbFactor[ch]) / 4, kb, samples, samples, maxRun);
|
||||
status = Aglib.dyn_decomp(params, data, this.predictor, samples, chanBits);
|
||||
if (!status) {
|
||||
throw new Error('Error in Aglib.dyn_decomp');
|
||||
}
|
||||
if (mode[ch] === 0) {
|
||||
Dplib.unpc_block(this.predictor, this.mixBuffers[ch], samples, coefs[ch], num[ch], chanBits, denShift[ch]);
|
||||
} else {
|
||||
Dplib.unpc_block(this.predictor, this.predictor, samples, null, 31, chanBits, 0);
|
||||
Dplib.unpc_block(this.predictor, this.mixBuffers[ch], samples, coefs[ch], num[ch], chanBits, denShift[ch]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chanBits = this.config.bitDepth;
|
||||
shift = 32 - chanBits;
|
||||
for (i = _l = 0; _l < samples; i = _l += 1) {
|
||||
for (ch = _m = 0; _m < channels; ch = _m += 1) {
|
||||
val = (data.read(chanBits) << shift) >> shift;
|
||||
this.mixBuffers[ch][i] = val;
|
||||
}
|
||||
}
|
||||
mixBits = mixRes = 0;
|
||||
bytesShifted = 0;
|
||||
}
|
||||
if (bytesShifted) {
|
||||
shift = bytesShifted * 8;
|
||||
for (i = _n = 0, _ref2 = samples * channels; _n < _ref2; i = _n += 1) {
|
||||
this.shiftBuffer[i] = shiftbits.read(shift);
|
||||
}
|
||||
}
|
||||
switch (this.config.bitDepth) {
|
||||
case 16:
|
||||
out16 = new Int16Array(output, channelIndex);
|
||||
if (channels === 2) {
|
||||
Matrixlib.unmix16(this.mixBuffers[0], this.mixBuffers[1], out16, numChannels, samples, mixBits, mixRes);
|
||||
} else {
|
||||
j = 0;
|
||||
buf = this.mixBuffers[0];
|
||||
for (i = _o = 0; _o < samples; i = _o += 1) {
|
||||
out16[j] = buf[i];
|
||||
j += numChannels;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error('Only supports 16-bit samples right now');
|
||||
}
|
||||
channelIndex += channels;
|
||||
break;
|
||||
case ID_CCE:
|
||||
case ID_PCE:
|
||||
throw new Error("Unsupported element: " + tag);
|
||||
break;
|
||||
case ID_DSE:
|
||||
elementInstanceTag = data.read(4);
|
||||
dataByteAlignFlag = data.read(1);
|
||||
count = data.read(8);
|
||||
if (count === 255) {
|
||||
count += data.read(8);
|
||||
}
|
||||
if (dataByteAlignFlag) {
|
||||
data.align();
|
||||
}
|
||||
data.advance(count * 8);
|
||||
if (!(data.pos < data.length)) {
|
||||
throw new Error('buffer overrun');
|
||||
}
|
||||
break;
|
||||
case ID_FIL:
|
||||
count = data.read(4);
|
||||
if (count === 15) {
|
||||
count += data.read(8) - 1;
|
||||
}
|
||||
data.advance(count * 8);
|
||||
if (!(data.pos < data.length)) {
|
||||
throw new Error('buffer overrun');
|
||||
}
|
||||
break;
|
||||
case ID_END:
|
||||
data.align();
|
||||
end = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown element: " + tag);
|
||||
}
|
||||
if (channelIndex > numChannels) {
|
||||
throw new Error('Channel index too large.');
|
||||
}
|
||||
}
|
||||
return new Int16Array(output);
|
||||
};
|
||||
|
||||
return ALACDecoder;
|
||||
|
||||
})(AV.Decoder);
|
||||
|
||||
module.exports = ALACDecoder;
|
||||
|
||||
}).call(this);
|
||||
|
||||
},{"./ag_dec":1,"./dp_dec":3,"./matrix_dec":4}],3:[function(require,module,exports){
|
||||
// Generated by CoffeeScript 1.7.1
|
||||
(function() {
|
||||
var Dplib;
|
||||
|
||||
Dplib = (function() {
|
||||
var copy;
|
||||
|
||||
function Dplib() {}
|
||||
|
||||
copy = function(dst, dstOffset, src, srcOffset, n) {
|
||||
var destination, source;
|
||||
destination = new Uint8Array(dst, dstOffset, n);
|
||||
source = new Uint8Array(src, srcOffset, n);
|
||||
destination.set(source);
|
||||
return dst;
|
||||
};
|
||||
|
||||
Dplib.unpc_block = function(pc1, out, num, coefs, active, chanbits, denshift) {
|
||||
var a0, a1, a2, a3, a4, a5, a6, a7, b0, b1, b2, b3, b4, b5, b6, b7, chanshift, dd, del, del0, denhalf, i, j, lim, offset, prev, sg, sgn, sum1, top, _i, _j, _k, _l, _m, _n, _o, _p, _ref, _ref1;
|
||||
chanshift = 32 - chanbits;
|
||||
denhalf = 1 << (denshift - 1);
|
||||
out[0] = pc1[0];
|
||||
if (active === 0) {
|
||||
return copy(out, 0, pc1, 0, num * 4);
|
||||
}
|
||||
if (active === 31) {
|
||||
prev = out[0];
|
||||
for (i = _i = 1; _i < num; i = _i += 1) {
|
||||
del = pc1[i] + prev;
|
||||
prev = (del << chanshift) >> chanshift;
|
||||
out[i] = prev;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (i = _j = 1; _j <= active; i = _j += 1) {
|
||||
del = pc1[i] + out[i - 1];
|
||||
out[i] = (del << chanshift) >> chanshift;
|
||||
}
|
||||
lim = active + 1;
|
||||
if (active === 4) {
|
||||
a0 = coefs[0], a1 = coefs[1], a2 = coefs[2], a3 = coefs[3];
|
||||
for (j = _k = lim; _k < num; j = _k += 1) {
|
||||
top = out[j - lim];
|
||||
offset = j - 1;
|
||||
b0 = top - out[offset];
|
||||
b1 = top - out[offset - 1];
|
||||
b2 = top - out[offset - 2];
|
||||
b3 = top - out[offset - 3];
|
||||
sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3) >> denshift;
|
||||
del = del0 = pc1[j];
|
||||
sg = (-del >>> 31) | (del >> 31);
|
||||
del += top + sum1;
|
||||
out[j] = (del << chanshift) >> chanshift;
|
||||
if (sg > 0) {
|
||||
sgn = (-b3 >>> 31) | (b3 >> 31);
|
||||
a3 -= sgn;
|
||||
del0 -= 1 * ((sgn * b3) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b2 >>> 31) | (b2 >> 31);
|
||||
a2 -= sgn;
|
||||
del0 -= 2 * ((sgn * b2) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b1 >>> 31) | (b1 >> 31);
|
||||
a1 -= sgn;
|
||||
del0 -= 3 * ((sgn * b1) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
a0 -= (-b0 >>> 31) | (b0 >> 31);
|
||||
} else if (sg < 0) {
|
||||
sgn = -((-b3 >>> 31) | (b3 >> 31));
|
||||
a3 -= sgn;
|
||||
del0 -= 1 * ((sgn * b3) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b2 >>> 31) | (b2 >> 31));
|
||||
a2 -= sgn;
|
||||
del0 -= 2 * ((sgn * b2) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b1 >>> 31) | (b1 >> 31));
|
||||
a1 -= sgn;
|
||||
del0 -= 3 * ((sgn * b1) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
a0 += (-b0 >>> 31) | (b0 >> 31);
|
||||
}
|
||||
}
|
||||
coefs[0] = a0;
|
||||
coefs[1] = a1;
|
||||
coefs[2] = a2;
|
||||
coefs[3] = a3;
|
||||
} else if (active === 8) {
|
||||
a0 = coefs[0], a1 = coefs[1], a2 = coefs[2], a3 = coefs[3], a4 = coefs[4], a5 = coefs[5], a6 = coefs[6], a7 = coefs[7];
|
||||
for (j = _l = lim; _l < num; j = _l += 1) {
|
||||
top = out[j - lim];
|
||||
offset = j - 1;
|
||||
b0 = top - out[offset];
|
||||
b1 = top - out[offset - 1];
|
||||
b2 = top - out[offset - 2];
|
||||
b3 = top - out[offset - 3];
|
||||
b4 = top - out[offset - 4];
|
||||
b5 = top - out[offset - 5];
|
||||
b6 = top - out[offset - 6];
|
||||
b7 = top - out[offset - 7];
|
||||
sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3 - a4 * b4 - a5 * b5 - a6 * b6 - a7 * b7) >> denshift;
|
||||
del = del0 = pc1[j];
|
||||
sg = (-del >>> 31) | (del >> 31);
|
||||
del += top + sum1;
|
||||
out[j] = (del << chanshift) >> chanshift;
|
||||
if (sg > 0) {
|
||||
sgn = (-b7 >>> 31) | (b7 >> 31);
|
||||
a7 -= sgn;
|
||||
del0 -= 1 * ((sgn * b7) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b6 >>> 31) | (b6 >> 31);
|
||||
a6 -= sgn;
|
||||
del0 -= 2 * ((sgn * b6) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b5 >>> 31) | (b5 >> 31);
|
||||
a5 -= sgn;
|
||||
del0 -= 3 * ((sgn * b5) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b4 >>> 31) | (b4 >> 31);
|
||||
a4 -= sgn;
|
||||
del0 -= 4 * ((sgn * b4) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b3 >>> 31) | (b3 >> 31);
|
||||
a3 -= sgn;
|
||||
del0 -= 5 * ((sgn * b3) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b2 >>> 31) | (b2 >> 31);
|
||||
a2 -= sgn;
|
||||
del0 -= 6 * ((sgn * b2) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = (-b1 >>> 31) | (b1 >> 31);
|
||||
a1 -= sgn;
|
||||
del0 -= 7 * ((sgn * b1) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
continue;
|
||||
}
|
||||
a0 -= (-b0 >>> 31) | (b0 >> 31);
|
||||
} else if (sg < 0) {
|
||||
sgn = -((-b7 >>> 31) | (b7 >> 31));
|
||||
a7 -= sgn;
|
||||
del0 -= 1 * ((sgn * b7) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b6 >>> 31) | (b6 >> 31));
|
||||
a6 -= sgn;
|
||||
del0 -= 2 * ((sgn * b6) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b5 >>> 31) | (b5 >> 31));
|
||||
a5 -= sgn;
|
||||
del0 -= 3 * ((sgn * b5) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b4 >>> 31) | (b4 >> 31));
|
||||
a4 -= sgn;
|
||||
del0 -= 4 * ((sgn * b4) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b3 >>> 31) | (b3 >> 31));
|
||||
a3 -= sgn;
|
||||
del0 -= 5 * ((sgn * b3) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b2 >>> 31) | (b2 >> 31));
|
||||
a2 -= sgn;
|
||||
del0 -= 6 * ((sgn * b2) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
sgn = -((-b1 >>> 31) | (b1 >> 31));
|
||||
a1 -= sgn;
|
||||
del0 -= 7 * ((sgn * b1) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
continue;
|
||||
}
|
||||
a0 += (-b0 >>> 31) | (b0 >> 31);
|
||||
}
|
||||
}
|
||||
coefs[0] = a0;
|
||||
coefs[1] = a1;
|
||||
coefs[2] = a2;
|
||||
coefs[3] = a3;
|
||||
coefs[4] = a4;
|
||||
coefs[5] = a5;
|
||||
coefs[6] = a6;
|
||||
coefs[7] = a7;
|
||||
} else {
|
||||
for (i = _m = lim; _m < num; i = _m += 1) {
|
||||
sum1 = 0;
|
||||
top = out[i - lim];
|
||||
offset = i - 1;
|
||||
for (j = _n = 0; _n < active; j = _n += 1) {
|
||||
sum1 += coefs[j] * (out[offset - j] - top);
|
||||
}
|
||||
del = del0 = pc1[i];
|
||||
sg = (-del >>> 31) | (del >> 31);
|
||||
del += top + ((sum1 + denhalf) >> denshift);
|
||||
out[i] = (del << chanshift) >> chanshift;
|
||||
if (sg > 0) {
|
||||
for (j = _o = _ref = active - 1; _o >= 0; j = _o += -1) {
|
||||
dd = top - out[offset - j];
|
||||
sgn = (-dd >>> 31) | (dd >> 31);
|
||||
coefs[j] -= sgn;
|
||||
del0 -= (active - j) * ((sgn * dd) >> denshift);
|
||||
if (del0 <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (sg < 0) {
|
||||
for (j = _p = _ref1 = active - 1; _p >= 0; j = _p += -1) {
|
||||
dd = top - out[offset - j];
|
||||
sgn = (-dd >>> 31) | (dd >> 31);
|
||||
coefs[j] += sgn;
|
||||
del0 -= (active - j) * ((-sgn * dd) >> denshift);
|
||||
if (del0 >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Dplib;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = Dplib;
|
||||
|
||||
}).call(this);
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
// Generated by CoffeeScript 1.7.1
|
||||
(function() {
|
||||
var Matrixlib;
|
||||
|
||||
Matrixlib = (function() {
|
||||
function Matrixlib() {}
|
||||
|
||||
Matrixlib.unmix16 = function(u, v, out, stride, samples, mixbits, mixres) {
|
||||
var i, l, _i, _j;
|
||||
if (mixres === 0) {
|
||||
for (i = _i = 0; _i < samples; i = _i += 1) {
|
||||
out[i * stride + 0] = u[i];
|
||||
out[i * stride + 1] = v[i];
|
||||
}
|
||||
} else {
|
||||
for (i = _j = 0; _j < samples; i = _j += 1) {
|
||||
l = u[i] + v[i] - ((mixres * v[i]) >> mixbits);
|
||||
out[i * stride + 0] = l;
|
||||
out[i * stride + 1] = l - v[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Matrixlib;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = Matrixlib;
|
||||
|
||||
}).call(this);
|
||||
|
||||
},{}]},{},[2])
|
||||
|
||||
|
||||
//# sourceMappingURL=alac.js.map
|
3937
modules/aurora.js/aurora.js
Normal file
3937
modules/aurora.js/aurora.js
Normal file
File diff suppressed because it is too large
Load diff
793
modules/aurora.js/flac.js
Normal file
793
modules/aurora.js/flac.js
Normal file
|
@ -0,0 +1,793 @@
|
|||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
exports.FLACDemuxer = require('./src/demuxer');
|
||||
exports.FLACDecoder = require('./src/decoder');
|
||||
require('./src/ogg');
|
||||
|
||||
},{"./src/decoder":2,"./src/demuxer":3,"./src/ogg":4}],2:[function(require,module,exports){
|
||||
/*
|
||||
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
|
||||
* Original C version from FFmpeg (c) 2003 Alex Beregszaszi
|
||||
* JavaScript port by Devon Govett and Jens Nockert of Official.fm Labs
|
||||
*
|
||||
* Licensed under the same terms as the original. The original
|
||||
* license follows.
|
||||
*
|
||||
* FLAC.js is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FLAC.js is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
var AV = (window.AV);
|
||||
|
||||
var FLACDecoder = AV.Decoder.extend(function() {
|
||||
AV.Decoder.register('flac', this);
|
||||
|
||||
this.prototype.setCookie = function(cookie) {
|
||||
this.cookie = cookie;
|
||||
|
||||
// initialize arrays
|
||||
this.decoded = [];
|
||||
for (var i = 0; i < this.format.channelsPerFrame; i++) {
|
||||
this.decoded[i] = new Int32Array(cookie.maxBlockSize);
|
||||
}
|
||||
|
||||
// for 24 bit lpc frames, this is used to simulate a 64 bit int
|
||||
this.lpc_total = new Int32Array(2);
|
||||
};
|
||||
|
||||
const BLOCK_SIZES = new Int16Array([
|
||||
0, 192, 576 << 0, 576 << 1, 576 << 2, 576 << 3, 0, 0,
|
||||
256 << 0, 256 << 1, 256 << 2, 256 << 3, 256 << 4, 256 << 5, 256 << 6, 256 << 7
|
||||
]);
|
||||
|
||||
const SAMPLE_RATES = new Int32Array([
|
||||
0, 88200, 176400, 192000,
|
||||
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
|
||||
0, 0, 0, 0
|
||||
]);
|
||||
|
||||
const SAMPLE_SIZES = new Int8Array([
|
||||
0, 8, 12, 0, 16, 20, 24, 0
|
||||
]);
|
||||
|
||||
const MAX_CHANNELS = 8,
|
||||
CHMODE_INDEPENDENT = 0,
|
||||
CHMODE_LEFT_SIDE = 8,
|
||||
CHMODE_RIGHT_SIDE = 9,
|
||||
CHMODE_MID_SIDE = 10;
|
||||
|
||||
this.prototype.readChunk = function() {
|
||||
var stream = this.bitstream;
|
||||
if (!stream.available(32))
|
||||
return;
|
||||
|
||||
// frame sync code
|
||||
if ((stream.read(15) & 0x7FFF) !== 0x7FFC)
|
||||
throw new Error('Invalid sync code');
|
||||
|
||||
var isVarSize = stream.read(1), // variable block size stream code
|
||||
bsCode = stream.read(4), // block size
|
||||
srCode = stream.read(4), // sample rate code
|
||||
chMode = stream.read(4), // channel mode
|
||||
bpsCode = stream.read(3); // bits per sample
|
||||
|
||||
stream.advance(1); // reserved bit
|
||||
|
||||
// channels
|
||||
this.chMode = chMode;
|
||||
var channels;
|
||||
|
||||
if (chMode < MAX_CHANNELS) {
|
||||
channels = chMode + 1;
|
||||
this.chMode = CHMODE_INDEPENDENT;
|
||||
} else if (chMode <= CHMODE_MID_SIDE) {
|
||||
channels = 2;
|
||||
} else {
|
||||
throw new Error('Invalid channel mode');
|
||||
}
|
||||
|
||||
if (channels !== this.format.channelsPerFrame)
|
||||
throw new Error('Switching channel layout mid-stream not supported.');
|
||||
|
||||
// bits per sample
|
||||
if (bpsCode === 3 || bpsCode === 7)
|
||||
throw new Error('Invalid sample size code');
|
||||
|
||||
this.bps = SAMPLE_SIZES[bpsCode];
|
||||
if (this.bps !== this.format.bitsPerChannel)
|
||||
throw new Error('Switching bits per sample mid-stream not supported.');
|
||||
|
||||
// sample number or frame number
|
||||
// see http://www.hydrogenaudio.org/forums/index.php?s=ea7085ffe6d57132c36e6105c0d434c9&showtopic=88390&pid=754269&st=0&#entry754269
|
||||
var ones = 0;
|
||||
while (stream.read(1) === 1)
|
||||
ones++;
|
||||
|
||||
var frame_or_sample_num = stream.read(7 - ones);
|
||||
for (; ones > 1; ones--) {
|
||||
stream.advance(2); // == 2
|
||||
frame_or_sample_num = (frame_or_sample_num << 6) | stream.read(6);
|
||||
}
|
||||
|
||||
// block size
|
||||
if (bsCode === 0)
|
||||
throw new Error('Reserved blocksize code');
|
||||
else if (bsCode === 6)
|
||||
this.blockSize = stream.read(8) + 1;
|
||||
else if (bsCode === 7)
|
||||
this.blockSize = stream.read(16) + 1;
|
||||
else
|
||||
this.blockSize = BLOCK_SIZES[bsCode];
|
||||
|
||||
// sample rate
|
||||
var sampleRate;
|
||||
if (srCode < 12)
|
||||
sampleRate = SAMPLE_RATES[srCode];
|
||||
else if (srCode === 12)
|
||||
sampleRate = stream.read(8) * 1000;
|
||||
else if (srCode === 13)
|
||||
sampleRate = stream.read(16);
|
||||
else if (srCode === 14)
|
||||
sampleRate = stream.read(16) * 10;
|
||||
else
|
||||
throw new Error('Invalid sample rate code');
|
||||
|
||||
stream.advance(8); // skip CRC check
|
||||
|
||||
// subframes
|
||||
for (var i = 0; i < channels; i++)
|
||||
this.decodeSubframe(i);
|
||||
|
||||
stream.align();
|
||||
stream.advance(16); // skip CRC frame footer
|
||||
|
||||
var is32 = this.bps > 16,
|
||||
output = new ArrayBuffer(this.blockSize * channels * (is32 ? 4 : 2)),
|
||||
buf = is32 ? new Int32Array(output) : new Int16Array(output),
|
||||
blockSize = this.blockSize,
|
||||
decoded = this.decoded,
|
||||
j = 0;
|
||||
|
||||
switch (this.chMode) {
|
||||
case CHMODE_INDEPENDENT:
|
||||
for (var k = 0; k < blockSize; k++) {
|
||||
for (var i = 0; i < channels; i++) {
|
||||
buf[j++] = decoded[i][k];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CHMODE_LEFT_SIDE:
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
var left = decoded[0][i],
|
||||
right = decoded[1][i];
|
||||
|
||||
buf[j++] = left;
|
||||
buf[j++] = (left - right);
|
||||
}
|
||||
break;
|
||||
|
||||
case CHMODE_RIGHT_SIDE:
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
var left = decoded[0][i],
|
||||
right = decoded[1][i];
|
||||
|
||||
buf[j++] = (left + right);
|
||||
buf[j++] = right;
|
||||
}
|
||||
break;
|
||||
|
||||
case CHMODE_MID_SIDE:
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
var left = decoded[0][i],
|
||||
right = decoded[1][i];
|
||||
|
||||
left -= right >> 1;
|
||||
buf[j++] = (left + right);
|
||||
buf[j++] = left;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return buf;
|
||||
};
|
||||
|
||||
this.prototype.decodeSubframe = function(channel) {
|
||||
var wasted = 0,
|
||||
stream = this.bitstream,
|
||||
blockSize = this.blockSize,
|
||||
decoded = this.decoded;
|
||||
|
||||
this.curr_bps = this.bps;
|
||||
if (channel === 0) {
|
||||
if (this.chMode === CHMODE_RIGHT_SIDE)
|
||||
this.curr_bps++;
|
||||
} else {
|
||||
if (this.chMode === CHMODE_LEFT_SIDE || this.chMode === CHMODE_MID_SIDE)
|
||||
this.curr_bps++;
|
||||
}
|
||||
|
||||
if (stream.read(1))
|
||||
throw new Error("Invalid subframe padding");
|
||||
|
||||
var type = stream.read(6);
|
||||
|
||||
if (stream.read(1)) {
|
||||
wasted = 1;
|
||||
while (!stream.read(1))
|
||||
wasted++;
|
||||
|
||||
this.curr_bps -= wasted;
|
||||
}
|
||||
|
||||
if (this.curr_bps > 32)
|
||||
throw new Error("decorrelated bit depth > 32 (" + this.curr_bps + ")");
|
||||
|
||||
if (type === 0) {
|
||||
var tmp = stream.read(this.curr_bps, true);
|
||||
for (var i = 0; i < blockSize; i++)
|
||||
decoded[channel][i] = tmp;
|
||||
|
||||
} else if (type === 1) {
|
||||
var bps = this.curr_bps;
|
||||
for (var i = 0; i < blockSize; i++)
|
||||
decoded[channel][i] = stream.read(bps, true);
|
||||
|
||||
} else if ((type >= 8) && (type <= 12)) {
|
||||
this.decode_subframe_fixed(channel, type & ~0x8);
|
||||
|
||||
} else if (type >= 32) {
|
||||
this.decode_subframe_lpc(channel, (type & ~0x20) + 1);
|
||||
|
||||
} else {
|
||||
throw new Error("Invalid coding type");
|
||||
}
|
||||
|
||||
if (wasted) {
|
||||
for (var i = 0; i < blockSize; i++)
|
||||
decoded[channel][i] <<= wasted;
|
||||
}
|
||||
};
|
||||
|
||||
this.prototype.decode_subframe_fixed = function(channel, predictor_order) {
|
||||
var decoded = this.decoded[channel],
|
||||
stream = this.bitstream,
|
||||
bps = this.curr_bps;
|
||||
|
||||
// warm up samples
|
||||
for (var i = 0; i < predictor_order; i++)
|
||||
decoded[i] = stream.read(bps, true);
|
||||
|
||||
this.decode_residuals(channel, predictor_order);
|
||||
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
|
||||
if (predictor_order > 0)
|
||||
a = decoded[predictor_order - 1];
|
||||
|
||||
if (predictor_order > 1)
|
||||
b = a - decoded[predictor_order - 2];
|
||||
|
||||
if (predictor_order > 2)
|
||||
c = b - decoded[predictor_order - 2] + decoded[predictor_order - 3];
|
||||
|
||||
if (predictor_order > 3)
|
||||
d = c - decoded[predictor_order - 2] + 2 * decoded[predictor_order - 3] - decoded[predictor_order - 4];
|
||||
|
||||
switch (predictor_order) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
var abcd = new Int32Array([a, b, c, d]),
|
||||
blockSize = this.blockSize;
|
||||
|
||||
for (var i = predictor_order; i < blockSize; i++) {
|
||||
abcd[predictor_order - 1] += decoded[i];
|
||||
|
||||
for (var j = predictor_order - 2; j >= 0; j--) {
|
||||
abcd[j] += abcd[j + 1];
|
||||
}
|
||||
|
||||
decoded[i] = abcd[0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid Predictor Order " + predictor_order);
|
||||
}
|
||||
};
|
||||
|
||||
this.prototype.decode_subframe_lpc = function(channel, predictor_order) {
|
||||
var stream = this.bitstream,
|
||||
decoded = this.decoded[channel],
|
||||
bps = this.curr_bps,
|
||||
blockSize = this.blockSize;
|
||||
|
||||
// warm up samples
|
||||
for (var i = 0; i < predictor_order; i++) {
|
||||
decoded[i] = stream.read(bps, true);
|
||||
}
|
||||
|
||||
var coeff_prec = stream.read(4) + 1;
|
||||
if (coeff_prec === 16)
|
||||
throw new Error("Invalid coefficient precision");
|
||||
|
||||
var qlevel = stream.read(5, true);
|
||||
if (qlevel < 0)
|
||||
throw new Error("Negative qlevel, maybe buggy stream");
|
||||
|
||||
var coeffs = new Int32Array(32);
|
||||
for (var i = 0; i < predictor_order; i++) {
|
||||
coeffs[i] = stream.read(coeff_prec, true);
|
||||
}
|
||||
|
||||
this.decode_residuals(channel, predictor_order);
|
||||
|
||||
if (this.bps <= 16) {
|
||||
for (var i = predictor_order; i < blockSize - 1; i += 2) {
|
||||
var d = decoded[i - predictor_order],
|
||||
s0 = 0, s1 = 0, c = 0;
|
||||
|
||||
for (var j = predictor_order - 1; j > 0; j--) {
|
||||
c = coeffs[j];
|
||||
s0 += c * d;
|
||||
d = decoded[i - j];
|
||||
s1 += c * d;
|
||||
}
|
||||
|
||||
c = coeffs[0];
|
||||
s0 += c * d;
|
||||
d = decoded[i] += (s0 >> qlevel);
|
||||
s1 += c * d;
|
||||
decoded[i + 1] += (s1 >> qlevel);
|
||||
}
|
||||
|
||||
if (i < blockSize) {
|
||||
var sum = 0;
|
||||
for (var j = 0; j < predictor_order; j++)
|
||||
sum += coeffs[j] * decoded[i - j - 1];
|
||||
|
||||
decoded[i] += (sum >> qlevel);
|
||||
}
|
||||
} else {
|
||||
// simulate 64 bit integer using an array of two 32 bit ints
|
||||
var total = this.lpc_total;
|
||||
for (var i = predictor_order; i < blockSize; i++) {
|
||||
// reset total to 0
|
||||
total[0] = 0;
|
||||
total[1] = 0;
|
||||
|
||||
for (j = 0; j < predictor_order; j++) {
|
||||
// simulate `total += coeffs[j] * decoded[i - j - 1]`
|
||||
multiply_add(total, coeffs[j], decoded[i - j - 1]);
|
||||
}
|
||||
|
||||
// simulate `decoded[i] += total >> qlevel`
|
||||
// we know that qlevel < 32 since it is a 5 bit field (see above)
|
||||
decoded[i] += (total[0] >>> qlevel) | (total[1] << (32 - qlevel));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const TWO_PWR_32_DBL = Math.pow(2, 32);
|
||||
|
||||
// performs `total += a * b` on a simulated 64 bit int
|
||||
// total is an Int32Array(2)
|
||||
// a and b are JS numbers (32 bit ints)
|
||||
function multiply_add(total, a, b) {
|
||||
// multiply a * b (we can use normal JS multiplication for this)
|
||||
var r = a * b;
|
||||
var n = r < 0;
|
||||
if (n)
|
||||
r = -r;
|
||||
|
||||
var r_low = (r % TWO_PWR_32_DBL) | 0;
|
||||
var r_high = (r / TWO_PWR_32_DBL) | 0;
|
||||
if (n) {
|
||||
r_low = ~r_low + 1;
|
||||
r_high = ~r_high;
|
||||
}
|
||||
|
||||
// add result to total
|
||||
var a48 = total[1] >>> 16;
|
||||
var a32 = total[1] & 0xFFFF;
|
||||
var a16 = total[0] >>> 16;
|
||||
var a00 = total[0] & 0xFFFF;
|
||||
|
||||
var b48 = r_high >>> 16;
|
||||
var b32 = r_high & 0xFFFF;
|
||||
var b16 = r_low >>> 16;
|
||||
var b00 = r_low & 0xFFFF;
|
||||
|
||||
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
|
||||
c00 += a00 + b00;
|
||||
c16 += c00 >>> 16;
|
||||
c00 &= 0xFFFF;
|
||||
c16 += a16 + b16;
|
||||
c32 += c16 >>> 16;
|
||||
c16 &= 0xFFFF;
|
||||
c32 += a32 + b32;
|
||||
c48 += c32 >>> 16;
|
||||
c32 &= 0xFFFF;
|
||||
c48 += a48 + b48;
|
||||
c48 &= 0xFFFF;
|
||||
|
||||
// store result back in total
|
||||
total[0] = (c16 << 16) | c00;
|
||||
total[1] = (c48 << 16) | c32;
|
||||
}
|
||||
|
||||
const INT_MAX = 32767;
|
||||
|
||||
this.prototype.decode_residuals = function(channel, predictor_order) {
|
||||
var stream = this.bitstream,
|
||||
method_type = stream.read(2);
|
||||
|
||||
if (method_type > 1)
|
||||
throw new Error('Illegal residual coding method ' + method_type);
|
||||
|
||||
var rice_order = stream.read(4),
|
||||
samples = (this.blockSize >>> rice_order);
|
||||
|
||||
if (predictor_order > samples)
|
||||
throw new Error('Invalid predictor order ' + predictor_order + ' > ' + samples);
|
||||
|
||||
var decoded = this.decoded[channel],
|
||||
sample = predictor_order,
|
||||
i = predictor_order;
|
||||
|
||||
for (var partition = 0; partition < (1 << rice_order); partition++) {
|
||||
var tmp = stream.read(method_type === 0 ? 4 : 5);
|
||||
|
||||
if (tmp === (method_type === 0 ? 15 : 31)) {
|
||||
tmp = stream.read(5);
|
||||
for (; i < samples; i++)
|
||||
decoded[sample++] = stream.read(tmp, true);
|
||||
|
||||
} else {
|
||||
for (; i < samples; i++)
|
||||
decoded[sample++] = this.golomb(tmp, INT_MAX, 0);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
};
|
||||
|
||||
const MIN_CACHE_BITS = 25;
|
||||
|
||||
this.prototype.golomb = function(k, limit, esc_len) {
|
||||
var data = this.bitstream,
|
||||
offset = data.bitPosition,
|
||||
buf = data.peek(32 - offset) << offset,
|
||||
v = 0;
|
||||
|
||||
var log = 31 - clz(buf | 1); // log2(buf)
|
||||
|
||||
if (log - k >= 32 - MIN_CACHE_BITS && 32 - log < limit) {
|
||||
buf >>>= log - k;
|
||||
buf += (30 - log) << k;
|
||||
|
||||
data.advance(32 + k - log);
|
||||
v = buf;
|
||||
|
||||
} else {
|
||||
for (var i = 0; data.read(1) === 0; i++)
|
||||
buf = data.peek(32 - offset) << offset;
|
||||
|
||||
if (i < limit - 1) {
|
||||
if (k)
|
||||
buf = data.read(k);
|
||||
else
|
||||
buf = 0;
|
||||
|
||||
v = buf + (i << k);
|
||||
|
||||
} else if (i === limit - 1) {
|
||||
buf = data.read(esc_len);
|
||||
v = buf + 1;
|
||||
|
||||
} else {
|
||||
v = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return (v >> 1) ^ -(v & 1);
|
||||
};
|
||||
|
||||
// Should be in the damned standard library...
|
||||
function clz(input) {
|
||||
var output = 0,
|
||||
curbyte = 0;
|
||||
|
||||
while(true) { // emulate goto in JS using the break statement :D
|
||||
curbyte = input >>> 24;
|
||||
if (curbyte) break;
|
||||
output += 8;
|
||||
|
||||
curbyte = input >>> 16;
|
||||
if (curbyte & 0xff) break;
|
||||
output += 8;
|
||||
|
||||
curbyte = input >>> 8;
|
||||
if (curbyte & 0xff) break;
|
||||
output += 8;
|
||||
|
||||
curbyte = input;
|
||||
if (curbyte & 0xff) break;
|
||||
output += 8;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
if (!(curbyte & 0xf0))
|
||||
output += 4;
|
||||
else
|
||||
curbyte >>>= 4;
|
||||
|
||||
if (curbyte & 0x8)
|
||||
return output;
|
||||
|
||||
if (curbyte & 0x4)
|
||||
return output + 1;
|
||||
|
||||
if (curbyte & 0x2)
|
||||
return output + 2;
|
||||
|
||||
if (curbyte & 0x1)
|
||||
return output + 3;
|
||||
|
||||
// shouldn't get here
|
||||
return output + 4;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FLACDecoder;
|
||||
|
||||
},{}],3:[function(require,module,exports){
|
||||
/*
|
||||
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
|
||||
* By Devon Govett and Jens Nockert of Official.fm Labs
|
||||
*
|
||||
* FLAC.js is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FLAC.js is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
var AV = (window.AV);
|
||||
|
||||
var FLACDemuxer = AV.Demuxer.extend(function() {
|
||||
AV.Demuxer.register(this);
|
||||
|
||||
this.probe = function(buffer) {
|
||||
return buffer.peekString(0, 4) === 'fLaC';
|
||||
}
|
||||
|
||||
const STREAMINFO = 0,
|
||||
PADDING = 1,
|
||||
APPLICATION = 2,
|
||||
SEEKTABLE = 3,
|
||||
VORBIS_COMMENT = 4,
|
||||
CUESHEET = 5,
|
||||
PICTURE = 6,
|
||||
INVALID = 127,
|
||||
STREAMINFO_SIZE = 34;
|
||||
|
||||
this.prototype.readChunk = function() {
|
||||
var stream = this.stream;
|
||||
|
||||
if (!this.readHeader && stream.available(4)) {
|
||||
if (stream.readString(4) !== 'fLaC')
|
||||
return this.emit('error', 'Invalid FLAC file.');
|
||||
|
||||
this.readHeader = true;
|
||||
}
|
||||
|
||||
while (stream.available(1) && !this.last) {
|
||||
if (!this.readBlockHeaders) {
|
||||
var tmp = stream.readUInt8();
|
||||
this.last = (tmp & 0x80) === 0x80,
|
||||
this.type = tmp & 0x7F,
|
||||
this.size = stream.readUInt24();
|
||||
}
|
||||
|
||||
if (!this.foundStreamInfo && this.type !== STREAMINFO)
|
||||
return this.emit('error', 'STREAMINFO must be the first block');
|
||||
|
||||
if (!stream.available(this.size))
|
||||
return;
|
||||
|
||||
switch (this.type) {
|
||||
case STREAMINFO:
|
||||
if (this.foundStreamInfo)
|
||||
return this.emit('error', 'STREAMINFO can only occur once.');
|
||||
|
||||
if (this.size !== STREAMINFO_SIZE)
|
||||
return this.emit('error', 'STREAMINFO size is wrong.');
|
||||
|
||||
this.foundStreamInfo = true;
|
||||
var bitstream = new AV.Bitstream(stream);
|
||||
|
||||
var cookie = {
|
||||
minBlockSize: bitstream.read(16),
|
||||
maxBlockSize: bitstream.read(16),
|
||||
minFrameSize: bitstream.read(24),
|
||||
maxFrameSize: bitstream.read(24)
|
||||
};
|
||||
|
||||
this.format = {
|
||||
formatID: 'flac',
|
||||
sampleRate: bitstream.read(20),
|
||||
channelsPerFrame: bitstream.read(3) + 1,
|
||||
bitsPerChannel: bitstream.read(5) + 1
|
||||
};
|
||||
|
||||
this.emit('format', this.format);
|
||||
this.emit('cookie', cookie);
|
||||
|
||||
var sampleCount = bitstream.read(36);
|
||||
this.emit('duration', sampleCount / this.format.sampleRate * 1000 | 0);
|
||||
|
||||
stream.advance(16); // skip MD5 hashes
|
||||
this.readBlockHeaders = false;
|
||||
break;
|
||||
|
||||
/*
|
||||
I am only looking at the least significant 32 bits of sample number and offset data
|
||||
This is more than sufficient for the longest flac file I have (~50 mins 2-channel 16-bit 44.1k which uses about 7.5% of the UInt32 space for the largest offset)
|
||||
Can certainly be improved by storing sample numbers and offests as doubles, but would require additional overriding of the searchTimestamp and seek functions (possibly more?)
|
||||
Also the flac faq suggests it would be possible to find frame lengths and thus create seek points on the fly via decoding but I assume this would be slow
|
||||
I may look into these thigns though as my project progresses
|
||||
*/
|
||||
case SEEKTABLE:
|
||||
for(var s=0; s<this.size/18; s++)
|
||||
{
|
||||
if(stream.peekUInt32(0) == 0xFFFFFFFF && stream.peekUInt32(1) == 0xFFFFFFFF)
|
||||
{
|
||||
//placeholder, ignore
|
||||
stream.advance(18);
|
||||
} else {
|
||||
if(stream.readUInt32() > 0)
|
||||
{
|
||||
this.emit('error', 'Seek points with sample number >UInt32 not supported');
|
||||
}
|
||||
var samplenum = stream.readUInt32();
|
||||
if(stream.readUInt32() > 0)
|
||||
{
|
||||
this.emit('error', 'Seek points with stream offset >UInt32 not supported');
|
||||
}
|
||||
var offset = stream.readUInt32();
|
||||
|
||||
stream.advance(2);
|
||||
|
||||
this.addSeekPoint(offset, samplenum);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VORBIS_COMMENT:
|
||||
// see http://www.xiph.org/vorbis/doc/v-comment.html
|
||||
this.metadata || (this.metadata = {});
|
||||
var len = stream.readUInt32(true);
|
||||
|
||||
this.metadata.vendor = stream.readString(len);
|
||||
var length = stream.readUInt32(true);
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
len = stream.readUInt32(true);
|
||||
var str = stream.readString(len, 'utf8'),
|
||||
idx = str.indexOf('=');
|
||||
|
||||
this.metadata[str.slice(0, idx).toLowerCase()] = str.slice(idx + 1);
|
||||
}
|
||||
|
||||
// TODO: standardize field names across formats
|
||||
break;
|
||||
|
||||
case PICTURE:
|
||||
var type = stream.readUInt32();
|
||||
if (type !== 3) { // make sure this is album art (type 3)
|
||||
stream.advance(this.size - 4);
|
||||
} else {
|
||||
var mimeLen = stream.readUInt32(),
|
||||
mime = stream.readString(mimeLen),
|
||||
descLen = stream.readUInt32(),
|
||||
description = stream.readString(descLen),
|
||||
width = stream.readUInt32(),
|
||||
height = stream.readUInt32(),
|
||||
depth = stream.readUInt32(),
|
||||
colors = stream.readUInt32(),
|
||||
length = stream.readUInt32(),
|
||||
picture = stream.readBuffer(length);
|
||||
|
||||
this.metadata || (this.metadata = {});
|
||||
this.metadata.coverArt = picture;
|
||||
}
|
||||
|
||||
// does anyone want the rest of the info?
|
||||
break;
|
||||
|
||||
default:
|
||||
stream.advance(this.size);
|
||||
this.readBlockHeaders = false;
|
||||
}
|
||||
|
||||
if (this.last && this.metadata)
|
||||
this.emit('metadata', this.metadata);
|
||||
}
|
||||
|
||||
while (stream.available(1) && this.last) {
|
||||
var buffer = stream.readSingleBuffer(stream.remainingBytes());
|
||||
this.emit('data', buffer);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.exports = FLACDemuxer;
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
var AV = (window.AV);
|
||||
|
||||
// if ogg.js exists, register a plugin
|
||||
try {
|
||||
var OggDemuxer = (window.AV.OggDemuxer);
|
||||
} catch (e) {};
|
||||
if (!OggDemuxer) return;
|
||||
|
||||
OggDemuxer.plugins.push({
|
||||
magic: "\177FLAC",
|
||||
|
||||
init: function() {
|
||||
this.list = new AV.BufferList();
|
||||
this.stream = new AV.Stream(this.list);
|
||||
},
|
||||
|
||||
readHeaders: function(packet) {
|
||||
var stream = this.stream;
|
||||
this.list.append(new AV.Buffer(packet));
|
||||
|
||||
stream.advance(5); // magic
|
||||
if (stream.readUInt8() != 1)
|
||||
throw new Error('Unsupported FLAC version');
|
||||
|
||||
stream.advance(3);
|
||||
if (stream.peekString(0, 4) != 'fLaC')
|
||||
throw new Error('Not flac');
|
||||
|
||||
this.flac = AV.Demuxer.find(stream.peekSingleBuffer(0, stream.remainingBytes()));
|
||||
if (!this.flac)
|
||||
throw new Error('Flac demuxer not found');
|
||||
|
||||
this.flac.prototype.readChunk.call(this);
|
||||
return true;
|
||||
},
|
||||
|
||||
readPacket: function(packet) {
|
||||
this.list.append(new AV.Buffer(packet));
|
||||
this.flac.prototype.readChunk.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
},{}]},{},[1])
|
||||
|
||||
|
||||
//# sourceMappingURL=flac.js.map
|
7706
modules/aurora.js/mp3.js
Normal file
7706
modules/aurora.js/mp3.js
Normal file
File diff suppressed because it is too large
Load diff
100
modules/aurora.js/ogg.js
Normal file
100
modules/aurora.js/ogg.js
Normal file
File diff suppressed because one or more lines are too long
139
modules/aurora.js/opus.js
Normal file
139
modules/aurora.js/opus.js
Normal file
File diff suppressed because one or more lines are too long
161
modules/aurora.js/vorbis.js
Normal file
161
modules/aurora.js/vorbis.js
Normal file
File diff suppressed because one or more lines are too long
108
modules/jquery-jplayer/jquery.jplayer.min.js
vendored
108
modules/jquery-jplayer/jquery.jplayer.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -443,7 +443,7 @@ if ($transcode) {
|
|||
}
|
||||
}
|
||||
|
||||
$transcoder = Stream::start_transcode($media, $transcode_to, $troptions);
|
||||
$transcoder = Stream::start_transcode($media, $transcode_to, null, $troptions);
|
||||
$fp = $transcoder['handle'];
|
||||
$media_name = $media->f_artist_full . " - " . $media->title . "." . $transcoder['format'];
|
||||
} else if ($cpaction) {
|
||||
|
|
|
@ -47,9 +47,16 @@ if (AmpConfig::get('webplayer_html5')) {
|
|||
if (AmpConfig::get('webplayer_flash')) {
|
||||
$solutions[] = 'flash';
|
||||
}
|
||||
if (AmpConfig::get('webplayer_aurora')) {
|
||||
$solutions[] = 'aurora';
|
||||
}
|
||||
echo implode(',', $solutions);
|
||||
|
||||
$supplied = WebPlayer::get_supplied_types($playlist);
|
||||
?>",
|
||||
supplied: "<?php echo implode(", ", WebPlayer::get_supplied_types($playlist)); ?>",
|
||||
nativeSupport:true,
|
||||
oggSupport: false,
|
||||
supplied: "<?php echo implode(", ", $supplied); ?>",
|
||||
volume: jp_volume,
|
||||
<?php if (!$is_share) { ?>
|
||||
size: {
|
||||
|
@ -242,6 +249,35 @@ if (AmpConfig::get('song_page_title') && !$is_share) {
|
|||
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
// Load Aurora.js scripts
|
||||
if (AmpConfig::get('webplayer_aurora')) {
|
||||
$atypes = array();
|
||||
foreach ($supplied as $stype) {
|
||||
if ($stype == 'ogg') {
|
||||
// Ogg could requires vorbis/opus codecs
|
||||
if (!in_array('ogg', $atypes)) $atypes[] = 'ogg';
|
||||
if (!in_array('vorbis', $atypes)) $atypes[] = 'vorbis';
|
||||
if (!in_array('opus', $atypes)) $atypes[] = 'opus';
|
||||
} else if ($stype == 'm4a') {
|
||||
// m4a could requires aac / alac codecs
|
||||
if (!in_array('aac', $atypes)) $atypes[] = 'aac';
|
||||
if (!in_array('alac', $atypes)) $atypes[] = 'alac';
|
||||
} else {
|
||||
// We support that other filetypes requires a codec name matching the filetype
|
||||
if (!in_array($stype, $atypes)) $atypes[] = $stype;
|
||||
}
|
||||
}
|
||||
|
||||
// Load only existing codec scripts
|
||||
foreach ($atypes as $atype) {
|
||||
$spath = '/modules/aurora.js/' . $atype . '.js';
|
||||
if (Core::is_readable(AmpConfig::get('prefix') . $spath)) {
|
||||
echo '<script src="' . AmpConfig::get('web_path') . $spath . '" language="javascript" type="text/javascript"></script>' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
|
|
|
@ -31,6 +31,9 @@ function update_action()
|
|||
}
|
||||
?>
|
||||
<link href="<?php echo AmpConfig::get('web_path'); ?>/modules/UberViz/style.css" rel="stylesheet" type="text/css">
|
||||
<?php if (AmpConfig::get('webplayer_aurora')) { ?>
|
||||
<script src="<?php echo AmpConfig::get('web_path'); ?>/modules/aurora.js/aurora.js" language="javascript" type="text/javascript"></script>
|
||||
<?php } ?>
|
||||
<script src="<?php echo AmpConfig::get('web_path'); ?>/modules/jquery-jplayer/jquery.jplayer.min.js" language="javascript" type="text/javascript"></script>
|
||||
<script src="<?php echo AmpConfig::get('web_path'); ?>/modules/jquery-jplayer/add-on/jplayer.playlist.min.js" language="javascript" type="text/javascript"></script>
|
||||
<script src="<?php echo AmpConfig::get('web_path'); ?>/lib/javascript/jplayer.playlist.ext.js" language="javascript" type="text/javascript"></script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue