-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
make new analyze service to analyze and validate audio
- Loading branch information
Showing
2 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
if (FEAT.TCF_DEMO) { | ||
describe('prx.analyze-audio', function () { | ||
|
||
describe('MimeType', function() { | ||
var MimeType; | ||
|
||
beforeEach(module('prx.analyze-audio')); | ||
|
||
beforeEach(inject(function (_MimeType_) { | ||
MimeType = _MimeType_; | ||
})); | ||
|
||
it("will return the preset file type on lookup", function() { | ||
var md = MimeType.lookup({name: 'test.foo', type: 'bar/foo'}); | ||
expect(md.major()).toEqual('bar'); | ||
expect(md.minor()).toEqual('foo'); | ||
}); | ||
|
||
it("will lookup type from file extension", function() { | ||
var md = MimeType.lookup({name: 'test.mp2', type: null}); | ||
expect(md.major()).toEqual('audio'); | ||
expect(md.minor()).toEqual('mpeg'); | ||
}); | ||
|
||
it("will use default when extension unknown and type null", function() { | ||
var md = MimeType.lookup({name: 'test.mp2014', type: null}); | ||
expect(md.major()).toEqual('application'); | ||
expect(md.minor()).toEqual('octet-stream'); | ||
}); | ||
|
||
it("will use provided default when extension unknown and type null", function() { | ||
var md = MimeType.lookup({name: 'test.mp2014', type: null}, 'bar/foo'); | ||
expect(md.major()).toEqual('bar'); | ||
expect(md.minor()).toEqual('foo'); | ||
}); | ||
|
||
}); | ||
|
||
describe('AnalyzeAudio', function() { | ||
var AnalyzeAudio, $q, $rs, mv, pr, MockAuroraService, MockId3Service; | ||
|
||
beforeEach(module('angular-aurora', function ($provide) { | ||
mv = {}; | ||
MockAuroraService = {}; | ||
MockAuroraService.mock = function (md, val) { mv[md] = val; return MockAuroraService; }; | ||
MockAuroraService.format = function(file) { pr = mv['format']; return MockAuroraService; }; | ||
MockAuroraService.duration = function(file) { pr = mv['duration']; return MockAuroraService; }; | ||
MockAuroraService.metadata = function(file) { pr = mv['metadata']; return MockAuroraService; }; | ||
MockAuroraService.then = function(f) { return f(pr); }; | ||
$provide.value('AuroraService', MockAuroraService); | ||
})); | ||
|
||
beforeEach(module('angular-id3', function ($provide) { | ||
MockId3Service = {}; | ||
MockId3Service.analyze = function(file) { return MockId3Service; }; | ||
MockId3Service.then = function(f) { return f({album: 'mister bar'}); }; | ||
$provide.value('Id3Service', MockId3Service); | ||
})); | ||
|
||
beforeEach(module('prx.analyze-audio')); | ||
|
||
beforeEach(inject(function (_AnalyzeAudio_, _$rootScope_) { | ||
AnalyzeAudio = _AnalyzeAudio_; | ||
$rs = _$rootScope_; | ||
|
||
MockAuroraService | ||
.mock('format', {bitRate: 128}) | ||
.mock('duration', 600) | ||
.mock('metadata', {artist: 'miss foo'}); | ||
|
||
})); | ||
|
||
it("can analyze an audio file", function() { | ||
var mockFile = {name: 'test.mp2', type: null}; | ||
|
||
AnalyzeAudio.analyze(mockFile).then( function (file) { | ||
expect(file.mimeType.major()).toEqual('audio'); | ||
expect(file.tags.album).toEqual('mister bar'); | ||
expect(file.format.bitRate).toEqual(128); | ||
expect(file.duration).toEqual(600); | ||
expect(file.metadata.artist).toEqual('miss foo'); | ||
}); | ||
|
||
$rs.$apply(); | ||
}); | ||
|
||
}); | ||
|
||
describe('ValidateAudio', function () { | ||
var _fn, _file; | ||
|
||
beforeEach(module('prx.analyze-audio', function ($provide) { | ||
MockAnalyzeAudio = {}; | ||
MockAnalyzeAudio.mock = function(fun) { _fn = fun; return MockAnalyzeAudio; }; | ||
MockAnalyzeAudio.analyze = function(file) { _file = file; _fn(_file); return MockAnalyzeAudio; }; | ||
MockAnalyzeAudio.then = function(f) { return f(_file); }; | ||
$provide.value('AnalyzeAudio', MockAnalyzeAudio); | ||
})); | ||
|
||
it('validates the format', inject(function (AnalyzeAudio, ValidateAudio, MimeDefinition) { | ||
AnalyzeAudio.mock( function(file) { | ||
file.mimeType = new MimeDefinition('foo/bar'); | ||
}); | ||
|
||
var mockFile = {}; | ||
AnalyzeAudio.analyze(mockFile).then( function(file){ | ||
expect(file.mimeType.major()).toEqual('foo'); | ||
ValidateAudio.validateType(mockFile); | ||
expect(mockFile.validationResults).toBeDefined(); | ||
expect(mockFile.validationResults.results.length).toBe(1); | ||
expect(mockFile.validationResults.messages('mimeType').length).toBe(1); | ||
}); | ||
|
||
})); | ||
|
||
}); | ||
|
||
|
||
}); | ||
|
||
} // FEAT.TCF_DEMO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/* istanbul ignore next */ | ||
if (FEAT.TCF_DEMO) { | ||
angular.module('prx.analyze-audio', ['angular-id3', 'angular-aurora']) | ||
.factory('MimeDefinition', function() { | ||
|
||
function MimeDefinition (type) { | ||
this.type = type; | ||
} | ||
|
||
MimeDefinition.prototype = { | ||
major: function () { return this.type.split('/')[0]; }, | ||
minor: function () { return this.type.split('/')[1]; }, | ||
full: function () { return this.type; } | ||
}; | ||
|
||
return MimeDefinition; | ||
}) | ||
.service('MimeType', function MimeTypeService(MimeDefinition) { | ||
|
||
var expectedMimeTypes = { | ||
"aif": "audio\/x-aiff", | ||
"aifc": "audio\/x-aiff", | ||
"aiff": "audio\/x-aiff", | ||
"caf": "audio\/x-caf", | ||
"flac": "audio\/x-flac", | ||
"m2a": "audio\/mpeg", | ||
"m3a": "audio\/mpeg", | ||
"m4a": "audio\/mp4", | ||
"mp2": "audio\/mpeg", | ||
"mp2a": "audio\/mpeg", | ||
"mp3": "audio\/mpeg", | ||
"mp4": "video\/mp4", | ||
"mp4a": "audio\/mp4", | ||
"mpga": "audio\/mpeg", | ||
"oga": "audio\/ogg", | ||
"ogg": "audio\/ogg", | ||
"spx": "audio\/ogg", | ||
"wav": "audio\/x-wav", | ||
"weba": "audio\/webm", | ||
"gif": "image\/gif", | ||
"jpe": "image\/jpeg", | ||
"jpeg": "image\/jpeg", | ||
"jpg": "image\/jpeg", | ||
"png": "image\/png", | ||
"svg": "image\/svg+xml", | ||
"svgz": "image\/svg+xml", | ||
"webp": "image\/webp" | ||
}; | ||
|
||
this.lookup = function(file, defaultType) { | ||
defaultType = defaultType || "application\/octet-stream"; | ||
|
||
var type = file.type; | ||
if (typeof type === 'undefined' || type === null || type === '') { | ||
var ext = file.name.split('.').pop(); | ||
type = expectedMimeTypes[ext]; | ||
} | ||
return new MimeDefinition(type || defaultType); | ||
}; | ||
|
||
}) | ||
.service('AnalyzeAudio', function AnalyzeAudio($q, MimeType, Id3Service, AuroraService) { | ||
|
||
this.analyze = function (file) { | ||
|
||
// try to figure out the mime type | ||
file.mimeType = MimeType.lookup(file); | ||
|
||
var analysis = []; | ||
|
||
// metadata may be redundant with tags, getting it anyway | ||
analysis.push( AuroraService.metadata(file).then( function (m) { file.metadata = m; return m; }) ); | ||
analysis.push( AuroraService.format(file).then( function (f) { file.format = f; return f; }) ); | ||
analysis.push( AuroraService.duration(file).then( function (d) { file.duration = d; return d; }) ); | ||
analysis.push( Id3Service.analyze(file).then( function (t) { file.tags = t; return t; }) ); | ||
|
||
// return just the file back in the collected promise | ||
return $q.all(analysis).then(function (data) { return file; }); | ||
}; | ||
}) | ||
.service('ValidateAudio', function ValidateAudio(AnalyzeAudio) { | ||
|
||
function ValidationResult(msgType, message, attribute) { | ||
this.type = msgType; | ||
this.message = message; | ||
this.attribute = attribute || '_global_'; | ||
} | ||
|
||
function ValidationResults() { | ||
this.results = []; | ||
this.byType = {}; | ||
this.byAttr = {}; | ||
} | ||
|
||
ValidationResults.prototype = { | ||
addMessage: function (mtype, msg, attr) { | ||
var vr = new ValidationResult(mtype, msg, attr); | ||
this.results.push(vr); | ||
|
||
if (!angular.isDefined(this.byType[mtype])) { this.byType[mtype] = []; } | ||
this.byType[mtype].push(vr); | ||
|
||
if (!angular.isDefined(this.byAttr[attr])) { this.byAttr[attr] = []; } | ||
this.byAttr[attr].push(vr); | ||
}, | ||
error: function(msg, attr) { | ||
return this.addMessage('error', msg, attr); | ||
}, | ||
warning: function(msg, attr) { | ||
return this.addMessage('warning', msg, attr); | ||
}, | ||
messages: function(forAttr) { | ||
var a = forAttr || '_global_'; | ||
return this.byAttr[a]; | ||
} | ||
}; | ||
|
||
this.validateType = function(file) { | ||
file.validationResults = file.validationResults || new ValidationResults(); | ||
|
||
var t = file.mimeType.major(); | ||
if (t == 'video') { | ||
file.validationResults.warning('audio track will be extracted from the video.', 'mimeType'); | ||
} else if (t == 'audio') { | ||
if (!file.mimeType.minor().match(/(wav|flac|aiff|alac|raw|pcm)/)) { | ||
file.validationResults.warning('encoded in a lossy format, transcoding or altering will affect audio quality.', 'mimeType'); | ||
} | ||
} else { | ||
file.validationResults.error('must contain audio, but type is "'+t+'".', 'mimeType'); | ||
} | ||
return this; | ||
}; | ||
|
||
// need to figure out what warnings we want per type, and per channel count | ||
// for an mp2, bitrate should be 256 for stereo, and 128 for mono | ||
this.validateBitRate = function(file) { | ||
file.validationResults = file.validationResults || new ValidationResults(); | ||
if (file.mimeType.major() != 'audio') { return this; } | ||
return this; | ||
}; | ||
|
||
// need to figure out what warnings we want per type | ||
// for an mp2, sample rate should by 44100 | ||
this.validateSampleRate = function(file) { | ||
file.validationResults = file.validationResults || new ValidationResults(); | ||
if (file.mimeType.major() != 'audio') { return this; } | ||
return this; | ||
}; | ||
|
||
this.validate = function (file) { | ||
return AnalyzeAudio.analyze(file).then( function () { | ||
file.validationResults = new ValidationResults(); | ||
this.validateType(file); | ||
this.validateBitRate(file); | ||
this.validateSampleRate(file); | ||
}); | ||
|
||
}; | ||
|
||
}); | ||
} |