Skip to content

Commit

Permalink
Added unit conversion. Still a few things left to fix but it works now!
Browse files Browse the repository at this point in the history
  • Loading branch information
atruskie committed Jan 22, 2013
1 parent 44f6779 commit 13c43ad
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 73 deletions.
24 changes: 3 additions & 21 deletions app/assets/javascripts/angular/controllers/annotation_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,11 @@ function AnnotationViewerCtrl($scope, $element, $attrs, $transclude) {
}
};

$scope.converters = (function () {
// TODO: these are stubs and will need to be refactored

// constants go here
var me = $scope;

return {
pixelsToSeconds: function pixelsToSeconds(pixels) {
var seconds = pixels * pixelsPerSecond;
return seconds;
},
pixelsToHertz: function pixelsToHertz(pixels) {
var hertz = hetz * pixelsPerHertz;
return value * 10;
},
secondsToPixels: function secondsToPixels(value) {
return value / 10;
},
hertzToPixels: function hertzToPixels(value) {
return value / 10;
}
};
})();

// updated in directive
$scope.converters = {};


}
Expand Down
4 changes: 3 additions & 1 deletion app/assets/javascripts/angular/controllers/listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) {

var recordingId = $scope.recordingId = $routeParams.recordingId;

$scope.model = {};
$scope.model = {
audioElement: {}
};

var formatPaths = function () {
if ($scope.model.media && $scope.model.media.hasOwnProperty('recordingId')) {
Expand Down
188 changes: 140 additions & 48 deletions app/assets/javascripts/angular/directives/directives.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@
require: 'ngModel',
link: function (scope, element, attr, ngModel) {

function catchParseErrors(viewValue){
try{
function catchParseErrors(viewValue) {
try {
var result = angular.fromJson(viewValue);
}catch(e){
ngModel.$setValidity('bawJsonBinding',false);
} catch (e) {
ngModel.$setValidity('bawJsonBinding', false);
return '';
}
ngModel.$setValidity('bawJsonBinding',true);
ngModel.$setValidity('bawJsonBinding', true);
return result;
}

Expand All @@ -62,12 +62,12 @@
});

// ensures formatters are run on input blur
bawds.directive('renderOnBlur', function() {
bawds.directive('renderOnBlur', function () {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, elm, attrs, ctrl) {
elm.bind('blur', function() {
link: function (scope, elm, attrs, ctrl) {
elm.bind('blur', function () {
var viewValue = ctrl.$modelValue;
for (var i in ctrl.$formatters) {
viewValue = ctrl.$formatters[i](viewValue);
Expand Down Expand Up @@ -115,11 +115,11 @@

// implements infinite scrolling
// http://jsfiddle.net/vojtajina/U7Bz9/
bawds.directive('whenScrolled', function() {
return function(scope, elm, attr) {
bawds.directive('whenScrolled', function () {
return function (scope, elm, attr) {
var raw = elm[0];

elm.bind('scroll', function() {
elm.bind('scroll', function () {
console.log('scrolled');
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
Expand All @@ -131,6 +131,65 @@

bawds.directive('bawAnnotationViewer', function () {

function variance(x, y) {
var fraction = x / y;
return Math.abs(fraction - 1);
}

function unitConversions(sampleRate, window, imageWidth, imageHeight) {
if (sampleRate === undefined || window === undefined || !imageWidth || !imageHeight) {
return { pixelsPerSecond: NaN, pixelsPerHertz: NaN};
}

// based on meta data only
var nyquistFrequency = (sampleRate / 2.0),
idealPps = sampleRate / window,
idealPph = (window / 2.0) / nyquistFrequency;

// intentionally use width to ensure the image is correct
var spectrogramBasedAudioLength = (imageWidth * window) / sampleRate;
var spectrogramPps = imageWidth / spectrogramBasedAudioLength;

// intentionally use width to ensure image is correct
// SEE https://github.com/QutBioacousticsResearchGroup/bioacoustic-workbench/issues/86
imageHeight = (imageHeight % 2) === 1 ? imageHeight - 1 : imageHeight;
var imagePph = imageHeight / nyquistFrequency;

// do consistency check (tolerance = 2%)
if (variance(idealPph, imagePph) > 0.02) {
console.warn("the image height does not conform well with the meta data");
}
if (variance(idealPps, spectrogramPps) > 0.02) {
console.warn("the image width does not conform well with the meta data");
}

return { pixelsPerSecond: spectrogramPps, pixelsPerHertz: imagePph};
}

function updateUnitConversions(scope, imageWidth, imageHeight) {
var conversions = unitConversions(scope.model.media.sampleRate, scope.model.media.window, imageWidth, imageHeight)

return {
conversions : conversions,
pixelsToSeconds: function pixelsToSeconds(pixels) {
var seconds = pixels / conversions.pixelsPerSecond;
return seconds;
},
pixelsToHertz: function pixelsToHertz(pixels) {
var hertz = pixels / conversions.pixelsPerHertz;
return hertz;
},
secondsToPixels: function secondsToPixels(seconds) {
var pixels = seconds * conversions.pixelsPerSecond;
return pixels;
},
hertzToPixels: function hertzToPixels(hertz) {
var pixels = hertz * conversions.pixelsPerHertz;
return pixels;
}
};
}

/**
*
* @param audioEvent
Expand All @@ -140,18 +199,19 @@
function resizeOrMove(audioEvent, box, scope) {

if (audioEvent.__temporaryId__ === box.id) {
audioEvent.startTimeSeconds = scope.converters.pixelsToSeconds(box.left || 0);
audioEvent.highFrequencyHertz = scope.converters.pixelsToHertz(box.top || 0);
audioEvent.startTimeSeconds = scope.model.converters.pixelsToSeconds(box.left || 0);
audioEvent.highFrequencyHertz = scope.model.converters.pixelsToHertz(box.top || 0);

audioEvent.endTimeSeconds = audioEvent.startTimeSeconds + scope.converters.pixelsToSeconds(box.width || 0);
audioEvent.lowFrequencyHertz = audioEvent.highFrequencyHertz + scope.converters.pixelsToHertz(box.height || 0);
audioEvent.endTimeSeconds = audioEvent.startTimeSeconds + scope.model.converters.pixelsToSeconds(box.width || 0);
audioEvent.lowFrequencyHertz = audioEvent.highFrequencyHertz + scope.model.converters.pixelsToHertz(box.height || 0);
}
else {
console.error("Box ids do not match on resizing or move event", audioEvent.__temporaryId__ , box.id);
console.error("Box ids do not match on resizing or move event", audioEvent.__temporaryId__, box.id);
}
}

function resizeOrMoveWithApply(scope, audioEvent, box) {
scope.$apply(function() {
scope.$apply(function () {
scope.__lastDrawABoxEditId__ = audioEvent.__temporaryId__;
resizeOrMove(audioEvent, box, scope);

Expand Down Expand Up @@ -185,26 +245,26 @@
function registerWatcher(scope, array, index, drawaboxInstance) {

// create the watcher
var watcherFunc = function() {
var watcherFunc = function () {
return array[index];
};

// create the listener - the actual callback
var listenerFunc = function(value) {
var listenerFunc = function (value) {

if (value) {
if ( scope.__lastDrawABoxEditId__ === value.__temporaryId__){
if (scope.__lastDrawABoxEditId__ === value.__temporaryId__) {
scope.__lastDrawABoxEditId__ = undefined;
return;
}

console.log("audioEvent watcher fired");

// TODO: SET UP CONVERSIONS HERE
var top = scope.converters.hertzToPixels(value.highFrequencyHertz),
left = scope.converters.secondsToPixels(value.startTimeSeconds),
width = scope.converters.secondsToPixels(value.endTimeSeconds - value.startTimeSeconds),
height = scope.converters.hertzToPixels(value.highFrequencyHertz - value.lowFrequencyHertz);
var top = scope.model.converters.hertzToPixels(value.highFrequencyHertz),
left = scope.model.converters.secondsToPixels(value.startTimeSeconds),
width = scope.model.converters.secondsToPixels(value.endTimeSeconds - value.startTimeSeconds),
height = scope.model.converters.hertzToPixels(value.highFrequencyHertz - value.lowFrequencyHertz);

drawaboxInstance.drawabox('setBox', value.__temporaryId__, top, left, height, width, undefined);
}
Expand Down Expand Up @@ -233,14 +293,19 @@
// compile: function(element, attributes, transclude) {
// // transform DOM
// },
link: function (scope, element, attributes, controller) {

var $element = $(element);
link: function (scope, $element, attributes, controller) {

// assign a unique id to scope
scope.id = Number.Unique();

scope.$canvas = $element.find(".annotation-viewer img + div").first();
scope.$image = $element.find("img");


// init unit conversion
function updateConverters() {scope.model.converters = updateUnitConversions(scope, scope.$image.width(), scope.$image.height());}
scope.$watch(function() {return scope.model.media.imageUrl}, updateConverters);
scope.$image[0].addEventListener('load', updateConverters, false);

// init drawabox
scope.model.audioEvents = scope.model.audioEvents || [];
Expand All @@ -253,11 +318,11 @@
var newAudioEvent = create(newBox, "a dummy id!", scope);


scope.$apply(function() {
scope.model.audioEvents.push( newAudioEvent);
scope.$apply(function () {
scope.model.audioEvents.push(newAudioEvent);

var annotationViewerIndex = scope.model.audioEvents.length - 1;
element[0].annotationViewerIndex = annotationViewerIndex;
element[0].annotationViewerIndex = annotationViewerIndex;

// register for reverse binding
registerWatcher(scope, scope.model.audioEvents, annotationViewerIndex, scope.$canvas);
Expand All @@ -269,7 +334,7 @@
console.log("boxSelected", selectedBox);

// support for multiple selections - remove the clear
scope.$apply(function() {
scope.$apply(function () {
scope.model.selectedAudioEvents.length = 0;
scope.model.selectedAudioEvents.push(scope.model.audioEvents[element[0].annotationViewerIndex]);
});
Expand All @@ -294,7 +359,7 @@
"boxDeleted": function (element, deletedBox) {
console.log("boxDeleted");

scope.$apply(function(){
scope.$apply(function () {
// TODO: i'm not sure how I should handle 'deleted' items yet
var itemToDelete = scope.model.audioEvents[element[0].annotationViewerIndex];
itemToDelete.deletedAt = (new Date());
Expand All @@ -313,22 +378,49 @@
}
});

/**
* A directive for binding the model to data off an audio element.
* Most things are oneway bindings
*/
bawds.directive('ngAudio', function () {
return {
restrict: 'A',
link: function (scope, elements, attributes, controller) {
var element = elements[0];
if (element.nodeName !== "AUDIO") {
throw "Cannot put ngAudio element on an element that is not a <audio />";
}

var events = {'abort': undefined, 'canplay': undefined, 'canplaythrough': undefined,
'durationchange': function (event) {
scope.$safeApply2(function () {
if (attributes.ngAudio) {
var target = scope.$eval(attributes.ngAudio)
if (target) {
target.duration = element.duration;
return;
}
}

})();
scope.duration = element.duration;
});

//bawApp.directive('nsDsFade', function() {
// return function(scope, element, attrs) {
// element.css('display', 'none');
// scope.$watch(attrs.ngDsFade, function(value) {
// if (value) {
// element.fadeIn(200);
// }
// else {
// element.fadeOut(100);
// }
// });
// }
//
//});
//
},
'emptied': undefined, 'ended': undefined,
'error': undefined, 'loadeddata': undefined, 'loadedmetadata': undefined, 'loadstart': undefined, 'mozaudioavailable': undefined,
'pause': undefined, 'play': undefined, 'playing': undefined, 'progress': undefined, 'ratechange': undefined, 'seeked': undefined, 'seeking': undefined,
'suspend': undefined, 'timeupdate': undefined, 'volumechange': undefined, 'waiting': undefined};

angular.forEach(events, function (value, key) {
if (value) {
element.addEventListener(key, value, false);

// initialise first time
value();
}
});
}
}
});
})();

2 changes: 1 addition & 1 deletion app/assets/templates/listen.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h3>Spectrogram</h3>
<baw-annotation-viewer model="model"></baw-annotation-viewer>

<h3>Audio Controls</h3>
<audio id="main-player" controls>
<audio id="main-player" ng-audio="model.audioElement" controls>
<source ng-repeat="url in model.media.audioUrls" ng-src="{{url.url}}" src="" type="{{url.mime}}" >
Your browser does not support the audio element.
</audio>
Expand Down
4 changes: 2 additions & 2 deletions lib/assets/javascripts/jquery.drawabox.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@
var wasMouseDownSet = dataMouseUp.mousedown;
var currentBoxId = dataMouseUp.currentMouseDragBoxId;

// mousedown must be true
if (!wasMouseDownSet) {
// mousedown must be true, we must actually have a currentId
if (!wasMouseDownSet || !currentBoxId) {
return;
}

Expand Down

0 comments on commit 13c43ad

Please sign in to comment.