From ec4e1b20cb6aaea842985b94598be1623a047fc3 Mon Sep 17 00:00:00 2001 From: Samuel ORTION Date: Wed, 7 Apr 2021 12:35:25 +0200 Subject: [PATCH] Added Larynx v1.0 --- public/about/index.php | 22 +- public/database/TAXREF14.0_CHIRO.csv | 0 public/explore/spectrograms/index.php | 6 +- public/larynx/index.php | 85 +++++ public/larynx/scripts/create_colormap.js | 9 + public/larynx/scripts/hot-colormap.js | 258 +++++++++++++++ public/larynx/scripts/p5.fft.js | 47 +++ public/larynx/scripts/script.js | 11 + public/larynx/scripts/sketch.js | 34 ++ public/larynx/scripts/spectro.borismus.js | 259 +++++++++++++++ public/larynx/scripts/spectro.js | 63 ++++ public/larynx/scripts/spectro.larynx.js | 78 +++++ public/larynx/scripts/webaudio.spectro.js | 6 + public/styles/style.css | 372 +++++++++++++--------- 14 files changed, 1090 insertions(+), 160 deletions(-) mode change 100755 => 100644 public/database/TAXREF14.0_CHIRO.csv create mode 100644 public/larynx/index.php create mode 100644 public/larynx/scripts/create_colormap.js create mode 100644 public/larynx/scripts/hot-colormap.js create mode 100644 public/larynx/scripts/p5.fft.js create mode 100644 public/larynx/scripts/script.js create mode 100644 public/larynx/scripts/sketch.js create mode 100644 public/larynx/scripts/spectro.borismus.js create mode 100644 public/larynx/scripts/spectro.js create mode 100644 public/larynx/scripts/spectro.larynx.js create mode 100644 public/larynx/scripts/webaudio.spectro.js diff --git a/public/about/index.php b/public/about/index.php index 06791f9..390c33d 100644 --- a/public/about/index.php +++ b/public/about/index.php @@ -4,7 +4,27 @@ ini_set('display_startup_errors', 1); error_reporting(E_ALL); session_start(); $root = realpath($_SERVER["DOCUMENT_ROOT"]); - +require "$root/database/credentials.php"; +// Connect the database +try { + $db = new PDO("mysql:host=$host;dbname=$database;charset=utf8", + $user, + $password, + array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + )); +} catch (Exception $e) { + die("Error : ".$e->getMessage()); +} +if (isset($_GET['record'])) { + $req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` WHERE id=:id'); + $req->execute(array( + "id"=>$_GET['record'] + )); +} else { + $req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` ORDER BY date DESC, time DESC LIMIT 1'); + $req->execute(); +} +$data = $req->fetch(); ?> diff --git a/public/database/TAXREF14.0_CHIRO.csv b/public/database/TAXREF14.0_CHIRO.csv old mode 100755 new mode 100644 diff --git a/public/explore/spectrograms/index.php b/public/explore/spectrograms/index.php index 96b5855..7ead7c7 100644 --- a/public/explore/spectrograms/index.php +++ b/public/explore/spectrograms/index.php @@ -58,8 +58,10 @@ include("$root/analytics/matomo.php"); -
-
+ + +
diff --git a/public/larynx/index.php b/public/larynx/index.php new file mode 100644 index 0000000..a969589 --- /dev/null +++ b/public/larynx/index.php @@ -0,0 +1,85 @@ + PDO::ERRMODE_EXCEPTION + )); +} catch (Exception $e) { + die("Error : ".$e->getMessage()); +} +if (isset($_GET['record'])) { + $req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` WHERE id=:id'); + $req->execute(array( + "id"=>$_GET['record'] + )); +} else { + $req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` ORDER BY date DESC, time DESC LIMIT 1'); + $req->execute(); +} +$data = $req->fetch(); +?> + + + + + + + Larynx | Chiro - Canto + + + + + + + +
+

Larynx

+

A web tool for bat sound analysis

+
+
+

Larynx v

+ + +
+
+
+ +
+
+
+ T: + + ms F: + + kHz +
+
+ + + +
+
+

+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/public/larynx/scripts/create_colormap.js b/public/larynx/scripts/create_colormap.js new file mode 100644 index 0000000..29aae2f --- /dev/null +++ b/public/larynx/scripts/create_colormap.js @@ -0,0 +1,9 @@ +const colormap = require('colormap'); +const colors = colormap({ + colormap: 'hot', + nshades: 256, + format: 'float' +}); +// const fs = require('fs'); +// fs.writeFile('hot-colormap.json', JSON.stringify(colors)); +console.log(JSON.stringify(colors)); \ No newline at end of file diff --git a/public/larynx/scripts/hot-colormap.js b/public/larynx/scripts/hot-colormap.js new file mode 100644 index 0000000..152b903 --- /dev/null +++ b/public/larynx/scripts/hot-colormap.js @@ -0,0 +1,258 @@ +[ + [0, 0, 0, 1], + [0.011764705882352941, 0, 0, 1], + [0.023529411764705882, 0, 0, 1], + [0.03529411764705882, 0, 0, 1], + [0.047058823529411764, 0, 0, 1], + [0.058823529411764705, 0, 0, 1], + [0.07058823529411765, 0, 0, 1], + [0.08235294117647059, 0, 0, 1], + [0.09411764705882353, 0, 0, 1], + [0.10588235294117647, 0, 0, 1], + [0.11764705882352941, 0, 0, 1], + [0.12941176470588237, 0, 0, 1], + [0.1411764705882353, 0, 0, 1], + [0.15294117647058825, 0, 0, 1], + [0.16470588235294117, 0, 0, 1], + [0.17647058823529413, 0, 0, 1], + [0.18823529411764706, 0, 0, 1], + [0.2, 0, 0, 1], + [0.21176470588235294, 0, 0, 1], + [0.2235294117647059, 0, 0, 1], + [0.23529411764705882, 0, 0, 1], + [0.24705882352941178, 0, 0, 1], + [0.25882352941176473, 0, 0, 1], + [0.27058823529411763, 0, 0, 1], + [0.2823529411764706, 0, 0, 1], + [0.29411764705882354, 0, 0, 1], + [0.3058823529411765, 0, 0, 1], + [0.3176470588235294, 0, 0, 1], + [0.32941176470588235, 0, 0, 1], + [0.3411764705882353, 0, 0, 1], + [0.35294117647058826, 0, 0, 1], + [0.36470588235294116, 0, 0, 1], + [0.3764705882352941, 0, 0, 1], + [0.38823529411764707, 0, 0, 1], + [0.4, 0, 0, 1], + [0.4117647058823529, 0, 0, 1], + [0.4235294117647059, 0, 0, 1], + [0.43529411764705883, 0, 0, 1], + [0.4470588235294118, 0, 0, 1], + [0.4549019607843137, 0, 0, 1], + [0.4666666666666667, 0, 0, 1], + [0.47843137254901963, 0, 0, 1], + [0.49019607843137253, 0, 0, 1], + [0.5019607843137255, 0, 0, 1], + [0.5137254901960784, 0, 0, 1], + [0.5254901960784314, 0, 0, 1], + [0.5372549019607843, 0, 0, 1], + [0.5490196078431373, 0, 0, 1], + [0.5607843137254902, 0, 0, 1], + [0.5725490196078431, 0, 0, 1], + [0.5843137254901961, 0, 0, 1], + [0.596078431372549, 0, 0, 1], + [0.6078431372549019, 0, 0, 1], + [0.6196078431372549, 0, 0, 1], + [0.6313725490196078, 0, 0, 1], + [0.6431372549019608, 0, 0, 1], + [0.6549019607843137, 0, 0, 1], + [0.6666666666666666, 0, 0, 1], + [0.6784313725490196, 0, 0, 1], + [0.6901960784313725, 0, 0, 1], + [0.7019607843137254, 0, 0, 1], + [0.7137254901960784, 0, 0, 1], + [0.7254901960784313, 0, 0, 1], + [0.7372549019607844, 0, 0, 1], + [0.7490196078431373, 0, 0, 1], + [0.7607843137254902, 0, 0, 1], + [0.7725490196078432, 0, 0, 1], + [0.7843137254901961, 0, 0, 1], + [0.796078431372549, 0, 0, 1], + [0.807843137254902, 0, 0, 1], + [0.8196078431372549, 0, 0, 1], + [0.8313725490196079, 0, 0, 1], + [0.8431372549019608, 0, 0, 1], + [0.8549019607843137, 0, 0, 1], + [0.8666666666666667, 0, 0, 1], + [0.8784313725490196, 0, 0, 1], + [0.8901960784313725, 0, 0, 1], + [0.9019607843137255, 0, 0, 1], + [0.9019607843137255, 0.011764705882352941, 0, 1], + [0.9058823529411765, 0.023529411764705882, 0, 1], + [0.9058823529411765, 0.03137254901960784, 0, 1], + [0.9058823529411765, 0.043137254901960784, 0, 1], + [0.9098039215686274, 0.054901960784313725, 0, 1], + [0.9098039215686274, 0.06666666666666667, 0, 1], + [0.9098039215686274, 0.07450980392156863, 0, 1], + [0.9137254901960784, 0.08627450980392157, 0, 1], + [0.9137254901960784, 0.09803921568627451, 0, 1], + [0.9137254901960784, 0.10980392156862745, 0, 1], + [0.9176470588235294, 0.11764705882352941, 0, 1], + [0.9176470588235294, 0.12941176470588237, 0, 1], + [0.9176470588235294, 0.1411764705882353, 0, 1], + [0.9215686274509803, 0.15294117647058825, 0, 1], + [0.9215686274509803, 0.1607843137254902, 0, 1], + [0.9215686274509803, 0.17254901960784313, 0, 1], + [0.9254901960784314, 0.1843137254901961, 0, 1], + [0.9254901960784314, 0.19607843137254902, 0, 1], + [0.9254901960784314, 0.20784313725490197, 0, 1], + [0.9294117647058824, 0.21568627450980393, 0, 1], + [0.9294117647058824, 0.22745098039215686, 0, 1], + [0.9294117647058824, 0.23921568627450981, 0, 1], + [0.9333333333333333, 0.25098039215686274, 0, 1], + [0.9333333333333333, 0.25882352941176473, 0, 1], + [0.9333333333333333, 0.27058823529411763, 0, 1], + [0.9372549019607843, 0.2823529411764706, 0, 1], + [0.9372549019607843, 0.29411764705882354, 0, 1], + [0.9372549019607843, 0.30196078431372547, 0, 1], + [0.9411764705882353, 0.3137254901960784, 0, 1], + [0.9411764705882353, 0.3254901960784314, 0, 1], + [0.9411764705882353, 0.33725490196078434, 0, 1], + [0.9450980392156862, 0.34509803921568627, 0, 1], + [0.9450980392156862, 0.3568627450980392, 0, 1], + [0.9450980392156862, 0.3686274509803922, 0, 1], + [0.9490196078431372, 0.3803921568627451, 0, 1], + [0.9490196078431372, 0.38823529411764707, 0, 1], + [0.9490196078431372, 0.4, 0, 1], + [0.9529411764705882, 0.4117647058823529, 0, 1], + [0.9529411764705882, 0.4235294117647059, 0, 1], + [0.9529411764705882, 0.43529411764705883, 0, 1], + [0.9529411764705882, 0.44313725490196076, 0, 1], + [0.9568627450980393, 0.4549019607843137, 0, 1], + [0.9568627450980393, 0.4666666666666667, 0, 1], + [0.9568627450980393, 0.47843137254901963, 0, 1], + [0.9607843137254902, 0.48627450980392156, 0, 1], + [0.9607843137254902, 0.4980392156862745, 0, 1], + [0.9607843137254902, 0.5098039215686274, 0, 1], + [0.9647058823529412, 0.5215686274509804, 0, 1], + [0.9647058823529412, 0.5294117647058824, 0, 1], + [0.9647058823529412, 0.5411764705882353, 0, 1], + [0.9686274509803922, 0.5529411764705883, 0, 1], + [0.9686274509803922, 0.5647058823529412, 0, 1], + [0.9686274509803922, 0.5725490196078431, 0, 1], + [0.9725490196078431, 0.5843137254901961, 0, 1], + [0.9725490196078431, 0.596078431372549, 0, 1], + [0.9725490196078431, 0.6078431372549019, 0, 1], + [0.9764705882352941, 0.6196078431372549, 0, 1], + [0.9764705882352941, 0.6274509803921569, 0, 1], + [0.9764705882352941, 0.6392156862745098, 0, 1], + [0.9803921568627451, 0.6509803921568628, 0, 1], + [0.9803921568627451, 0.6627450980392157, 0, 1], + [0.9803921568627451, 0.6705882352941176, 0, 1], + [0.984313725490196, 0.6823529411764706, 0, 1], + [0.984313725490196, 0.6941176470588235, 0, 1], + [0.984313725490196, 0.7058823529411765, 0, 1], + [0.9882352941176471, 0.7137254901960784, 0, 1], + [0.9882352941176471, 0.7254901960784313, 0, 1], + [0.9882352941176471, 0.7372549019607844, 0, 1], + [0.9921568627450981, 0.7490196078431373, 0, 1], + [0.9921568627450981, 0.7568627450980392, 0, 1], + [0.9921568627450981, 0.7686274509803922, 0, 1], + [0.996078431372549, 0.7803921568627451, 0, 1], + [0.996078431372549, 0.792156862745098, 0, 1], + [0.996078431372549, 0.8, 0, 1], + [1, 0.8117647058823529, 0, 1], + [1, 0.8235294117647058, 0, 1], + [1, 0.8235294117647058, 0.011764705882352941, 1], + [1, 0.8274509803921568, 0.0196078431372549, 1], + [1, 0.8274509803921568, 0.03137254901960784, 1], + [1, 0.8313725490196079, 0.0392156862745098, 1], + [1, 0.8313725490196079, 0.050980392156862744, 1], + [1, 0.8352941176470589, 0.058823529411764705, 1], + [1, 0.8352941176470589, 0.07058823529411765, 1], + [1, 0.8392156862745098, 0.0784313725490196, 1], + [1, 0.8392156862745098, 0.09019607843137255, 1], + [1, 0.8392156862745098, 0.09803921568627451, 1], + [1, 0.8431372549019608, 0.10980392156862745, 1], + [1, 0.8431372549019608, 0.11764705882352941, 1], + [1, 0.8470588235294118, 0.12941176470588237, 1], + [1, 0.8470588235294118, 0.13725490196078433, 1], + [1, 0.8509803921568627, 0.14901960784313725, 1], + [1, 0.8509803921568627, 0.1568627450980392, 1], + [1, 0.8549019607843137, 0.16862745098039217, 1], + [1, 0.8549019607843137, 0.17647058823529413, 1], + [1, 0.8549019607843137, 0.18823529411764706, 1], + [1, 0.8588235294117647, 0.19607843137254902, 1], + [1, 0.8588235294117647, 0.20784313725490197, 1], + [1, 0.8627450980392157, 0.21568627450980393, 1], + [1, 0.8627450980392157, 0.22745098039215686, 1], + [1, 0.8666666666666667, 0.23529411764705882, 1], + [1, 0.8666666666666667, 0.24705882352941178, 1], + [1, 0.8666666666666667, 0.2549019607843137, 1], + [1, 0.8705882352941177, 0.26666666666666666, 1], + [1, 0.8705882352941177, 0.27450980392156865, 1], + [1, 0.8745098039215686, 0.28627450980392155, 1], + [1, 0.8745098039215686, 0.29411764705882354, 1], + [1, 0.8784313725490196, 0.3058823529411765, 1], + [1, 0.8784313725490196, 0.3137254901960784, 1], + [1, 0.8823529411764706, 0.3254901960784314, 1], + [1, 0.8823529411764706, 0.3333333333333333, 1], + [1, 0.8823529411764706, 0.34509803921568627, 1], + [1, 0.8862745098039215, 0.35294117647058826, 1], + [1, 0.8862745098039215, 0.36470588235294116, 1], + [1, 0.8901960784313725, 0.37254901960784315, 1], + [1, 0.8901960784313725, 0.3843137254901961, 1], + [1, 0.8941176470588236, 0.39215686274509803, 1], + [1, 0.8941176470588236, 0.403921568627451, 1], + [1, 0.8980392156862745, 0.4117647058823529, 1], + [1, 0.8980392156862745, 0.4235294117647059, 1], + [1, 0.8980392156862745, 0.43137254901960786, 1], + [1, 0.9019607843137255, 0.44313725490196076, 1], + [1, 0.9019607843137255, 0.45098039215686275, 1], + [1, 0.9058823529411765, 0.4627450980392157, 1], + [1, 0.9058823529411765, 0.47058823529411764, 1], + [1, 0.9098039215686274, 0.4823529411764706, 1], + [1, 0.9098039215686274, 0.49019607843137253, 1], + [1, 0.9137254901960784, 0.5019607843137255, 1], + [1, 0.9137254901960784, 0.5098039215686274, 1], + [1, 0.9137254901960784, 0.5215686274509804, 1], + [1, 0.9176470588235294, 0.5294117647058824, 1], + [1, 0.9176470588235294, 0.5411764705882353, 1], + [1, 0.9215686274509803, 0.5490196078431373, 1], + [1, 0.9215686274509803, 0.5607843137254902, 1], + [1, 0.9254901960784314, 0.5686274509803921, 1], + [1, 0.9254901960784314, 0.5803921568627451, 1], + [1, 0.9254901960784314, 0.5882352941176471, 1], + [1, 0.9294117647058824, 0.6, 1], + [1, 0.9294117647058824, 0.6078431372549019, 1], + [1, 0.9333333333333333, 0.6196078431372549, 1], + [1, 0.9333333333333333, 0.6274509803921569, 1], + [1, 0.9372549019607843, 0.6392156862745098, 1], + [1, 0.9372549019607843, 0.6470588235294118, 1], + [1, 0.9411764705882353, 0.6588235294117647, 1], + [1, 0.9411764705882353, 0.6666666666666666, 1], + [1, 0.9411764705882353, 0.6784313725490196, 1], + [1, 0.9450980392156862, 0.6862745098039216, 1], + [1, 0.9450980392156862, 0.6980392156862745, 1], + [1, 0.9490196078431372, 0.7058823529411765, 1], + [1, 0.9490196078431372, 0.7176470588235294, 1], + [1, 0.9529411764705882, 0.7254901960784313, 1], + [1, 0.9529411764705882, 0.7372549019607844, 1], + [1, 0.9568627450980393, 0.7450980392156863, 1], + [1, 0.9568627450980393, 0.7568627450980392, 1], + [1, 0.9568627450980393, 0.7647058823529411, 1], + [1, 0.9607843137254902, 0.7764705882352941, 1], + [1, 0.9607843137254902, 0.7843137254901961, 1], + [1, 0.9647058823529412, 0.796078431372549, 1], + [1, 0.9647058823529412, 0.803921568627451, 1], + [1, 0.9686274509803922, 0.8156862745098039, 1], + [1, 0.9686274509803922, 0.8235294117647058, 1], + [1, 0.9725490196078431, 0.8352941176470589, 1], + [1, 0.9725490196078431, 0.8431372549019608, 1], + [1, 0.9725490196078431, 0.8549019607843137, 1], + [1, 0.9764705882352941, 0.8627450980392157, 1], + [1, 0.9764705882352941, 0.8745098039215686, 1], + [1, 0.9803921568627451, 0.8823529411764706, 1], + [1, 0.9803921568627451, 0.8941176470588236, 1], + [1, 0.984313725490196, 0.9019607843137255, 1], + [1, 0.984313725490196, 0.9137254901960784, 1], + [1, 0.984313725490196, 0.9215686274509803, 1], + [1, 0.9882352941176471, 0.9333333333333333, 1], + [1, 0.9882352941176471, 0.9411764705882353, 1], + [1, 0.9921568627450981, 0.9529411764705882, 1], + [1, 0.9921568627450981, 0.9607843137254902, 1], + [1, 0.996078431372549, 0.9725490196078431, 1], + [1, 0.996078431372549, 0.9803921568627451, 1], + [1, 1, 0.9921568627450981, 1], + [1, 1, 1, 1] +] \ No newline at end of file diff --git a/public/larynx/scripts/p5.fft.js b/public/larynx/scripts/p5.fft.js new file mode 100644 index 0000000..d09250c --- /dev/null +++ b/public/larynx/scripts/p5.fft.js @@ -0,0 +1,47 @@ +let audio = document.getElementById('audio'); +let file_path = audio.src; + +function preload(){ + sound = loadSound(audio.src); +} + +function setup(){ + let cnv = createCanvas(100,100); + cnv.mouseClicked(togglePlay); + fft = new p5.FFT(); + sound.amp(0.2); +} + +function draw(){ + background(220); + + let spectrum = fft.analyze(); + noStroke(); + fill(255, 0, 255); + for (let i = 0; i< spectrum.length; i++){ + let x = map(i, 0, spectrum.length, 0, width); + let h = -height + map(spectrum[i], 0, 255, height, 0); + rect(x, height, width / spectrum.length, h ) + } + + let waveform = fft.waveform(); + noFill(); + beginShape(); + stroke(20); + for (let i = 0; i < waveform.length; i++){ + let x = map(i, 0, waveform.length, 0, width); + let y = map( waveform[i], -1, 1, 0, height); + vertex(x,y); + } + endShape(); + + text('tap to play', 20, 20); +} + +function togglePlay() { + if (sound.isPlaying()) { + sound.pause(); + } else { + sound.loop(); + } +} \ No newline at end of file diff --git a/public/larynx/scripts/script.js b/public/larynx/scripts/script.js new file mode 100644 index 0000000..db8992f --- /dev/null +++ b/public/larynx/scripts/script.js @@ -0,0 +1,11 @@ +var wavesurfer = WaveSurfer.create({ + container: '#waveform', + waveColor: 'violet', + progressColor: 'purple' +}); +wavesurfer.on('ready', function () { + wavesurfer.play(); +}); +let audio = document.getElementById('audio'); +let file_path = audio.src; +wavesurfer.load(file_path); \ No newline at end of file diff --git a/public/larynx/scripts/sketch.js b/public/larynx/scripts/sketch.js new file mode 100644 index 0000000..4195c0d --- /dev/null +++ b/public/larynx/scripts/sketch.js @@ -0,0 +1,34 @@ +let audio = document.getElementById('audio'); +let file_path = audio.src; + +var sound; +var fft; + +function preload() { + sound = loadSound(audio.src); +} + +function setup() { + createCanvas(500, 500); + colorMode(HSB); + angleMode(DEGREES); + sound.play(); + fft = new p5.FFT(0.9, 128); +} + +function draw() { + background(0); + var spectrum = fft.analyze(); + noStroke(); + translate(width / 2, height / 2); + + for (var i = 0; i < spectrum.length; i++) { + var angle = map(i, 0, spectrum.length, 0, 360); + var amp = spectrum[i]; + var r = map(amp, 0, 256, 20, 100); + var x = r * cos(angle); + var y = r * sin(angle); + stroke(i, 255, 255); + line(0, 0, x, y); + } +} diff --git a/public/larynx/scripts/spectro.borismus.js b/public/larynx/scripts/spectro.borismus.js new file mode 100644 index 0000000..4fbab88 --- /dev/null +++ b/public/larynx/scripts/spectro.borismus.js @@ -0,0 +1,259 @@ +// Assumes context is an AudioContext defined outside of this class. + +Polymer('g-spectrogram', { + // Show the controls UI. + controls: false, + // Log mode. + log: false, + // Show axis labels, and how many ticks. + labels: false, + ticks: 5, + speed: 2, + // FFT bin size, + fftsize: 2048, + oscillator: false, + color: false, + + attachedCallback: function() { + this.tempCanvas = document.createElement('canvas'), + console.log('Created spectrogram'); + // Get input from the microphone. + if (navigator.mozGetUserMedia) { + navigator.mozGetUserMedia({audio: true}, + this.onStream.bind(this), + this.onStreamError.bind(this)); + } else if (navigator.webkitGetUserMedia) { + navigator.webkitGetUserMedia({audio: true}, + this.onStream.bind(this), + this.onStreamError.bind(this)); + } + this.ctx = this.$.canvas.getContext('2d'); + }, + + render: function() { + //console.log('Render'); + this.width = window.innerWidth; + this.height = window.innerHeight; + + var didResize = false; + // Ensure dimensions are accurate. + if (this.$.canvas.width != this.width) { + this.$.canvas.width = this.width; + this.$.labels.width = this.width; + didResize = true; + } + if (this.$.canvas.height != this.height) { + this.$.canvas.height = this.height; + this.$.labels.height = this.height; + didResize = true; + } + + //this.renderTimeDomain(); + this.renderFreqDomain(); + + if (this.labels && didResize) { + this.renderAxesLabels(); + } + + requestAnimationFrame(this.render.bind(this)); + + var now = new Date(); + if (this.lastRenderTime_) { + this.instantaneousFPS = now - this.lastRenderTime_; + } + this.lastRenderTime_ = now; + }, + + renderTimeDomain: function() { + var times = new Uint8Array(this.analyser.frequencyBinCount); + this.analyser.getByteTimeDomainData(times); + + for (var i = 0; i < times.length; i++) { + var value = times[i]; + var percent = value / 256; + var barHeight = this.height * percent; + var offset = this.height - barHeight - 1; + var barWidth = this.width/times.length; + this.ctx.fillStyle = 'black'; + this.ctx.fillRect(i * barWidth, offset, 1, 1); + } + }, + + renderFreqDomain: function() { + var freq = new Uint8Array(this.analyser.frequencyBinCount); + this.analyser.getByteFrequencyData(freq); + + var ctx = this.ctx; + // Copy the current canvas onto the temp canvas. + this.tempCanvas.width = this.width; + this.tempCanvas.height = this.height; + //console.log(this.$.canvas.height, this.tempCanvas.height); + var tempCtx = this.tempCanvas.getContext('2d'); + tempCtx.drawImage(this.$.canvas, 0, 0, this.width, this.height); + + // Iterate over the frequencies. + for (var i = 0; i < freq.length; i++) { + var value; + // Draw each pixel with the specific color. + if (this.log) { + logIndex = this.logScale(i, freq.length); + value = freq[logIndex]; + } else { + value = freq[i]; + } + + ctx.fillStyle = (this.color ? this.getFullColor(value) : this.getGrayColor(value)); + + var percent = i / freq.length; + var y = Math.round(percent * this.height); + + // draw the line at the right side of the canvas + ctx.fillRect(this.width - this.speed, this.height - y, + this.speed, this.speed); + } + + // Translate the canvas. + ctx.translate(-this.speed, 0); + // Draw the copied image. + ctx.drawImage(this.tempCanvas, 0, 0, this.width, this.height, + 0, 0, this.width, this.height); + + // Reset the transformation matrix. + ctx.setTransform(1, 0, 0, 1, 0, 0); + }, + + /** + * Given an index and the total number of entries, return the + * log-scaled value. + */ + logScale: function(index, total, opt_base) { + var base = opt_base || 2; + var logmax = this.logBase(total + 1, base); + var exp = logmax * index / total; + return Math.round(Math.pow(base, exp) - 1); + }, + + logBase: function(val, base) { + return Math.log(val) / Math.log(base); + }, + + renderAxesLabels: function() { + var canvas = this.$.labels; + canvas.width = this.width; + canvas.height = this.height; + var ctx = canvas.getContext('2d'); + var startFreq = 440; + var nyquist = context.sampleRate/2; + var endFreq = nyquist - startFreq; + var step = (endFreq - startFreq) / this.ticks; + var yLabelOffset = 5; + // Render the vertical frequency axis. + for (var i = 0; i <= this.ticks; i++) { + var freq = startFreq + (step * i); + // Get the y coordinate from the current label. + var index = this.freqToIndex(freq); + var percent = index / this.getFFTBinCount(); + var y = (1-percent) * this.height; + var x = this.width - 60; + // Get the value for the current y coordinate. + var label; + if (this.log) { + // Handle a logarithmic scale. + var logIndex = this.logScale(index, this.getFFTBinCount()); + // Never show 0 Hz. + freq = Math.max(1, this.indexToFreq(logIndex)); + } + var label = this.formatFreq(freq); + var units = this.formatUnits(freq); + ctx.font = '16px Inconsolata'; + // Draw the value. + ctx.textAlign = 'right'; + ctx.fillText(label, x, y + yLabelOffset); + // Draw the units. + ctx.textAlign = 'left'; + ctx.fillText(units, x + 10, y + yLabelOffset); + // Draw a tick mark. + ctx.fillRect(x + 40, y, 30, 2); + } + }, + + clearAxesLabels: function() { + var canvas = this.$.labels; + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, this.width, this.height); + }, + + formatFreq: function(freq) { + return (freq >= 1000 ? (freq/1000).toFixed(1) : Math.round(freq)); + }, + + formatUnits: function(freq) { + return (freq >= 1000 ? 'KHz' : 'Hz'); + }, + + indexToFreq: function(index) { + var nyquist = context.sampleRate/2; + return nyquist/this.getFFTBinCount() * index; + }, + + freqToIndex: function(frequency) { + var nyquist = context.sampleRate/2; + return Math.round(frequency/nyquist * this.getFFTBinCount()); + }, + + getFFTBinCount: function() { + return this.fftsize / 2; + }, + + onStream: function(stream) { + var input = context.createMediaStreamSource(stream); + var analyser = context.createAnalyser(); + analyser.smoothingTimeConstant = 0; + analyser.fftSize = this.fftsize; + + // Connect graph. + input.connect(analyser); + + this.analyser = analyser; + // Setup a timer to visualize some stuff. + this.render(); + }, + + onStreamError: function(e) { + console.error(e); + }, + + getGrayColor: function(value) { + return 'rgb(V, V, V)'.replace(/V/g, 255 - value); + }, + + getFullColor: function(value) { + var fromH = 62; + var toH = 0; + var percent = value / 255; + var delta = percent * (toH - fromH); + var hue = fromH + delta; + return 'hsl(H, 100%, 50%)'.replace(/H/g, hue); + }, + + logChanged: function() { + if (this.labels) { + this.renderAxesLabels(); + } + }, + + ticksChanged: function() { + if (this.labels) { + this.renderAxesLabels(); + } + }, + + labelsChanged: function() { + if (this.labels) { + this.renderAxesLabels(); + } else { + this.clearAxesLabels(); + } + } + }); + \ No newline at end of file diff --git a/public/larynx/scripts/spectro.js b/public/larynx/scripts/spectro.js new file mode 100644 index 0000000..c4e14cc --- /dev/null +++ b/public/larynx/scripts/spectro.js @@ -0,0 +1,63 @@ +'use strict'; +let audio = document.getElementById('audio'); +let file_path = audio.src; + +var wavesurfer; + +// Init & load +document.addEventListener('DOMContentLoaded', function() { + // Create an instance + var options = { + container: '#waveform', + waveColor: 'white', + progressColor: 'purple', + loaderColor: 'purple', + cursorColor: 'navy', + plugins: [ + WaveSurfer.spectrogram.create({ + wavesurfer: wavesurfer, + container: "#wave-spectrogram", + labels: true + }) + ] + }; + + wavesurfer = WaveSurfer.create(options); + wavesurfer.load(file_path); +}); + +let play = document.getElementById('play').addEventListener('click', function() { + wavesurfer.play(); +}); +let pause = document.getElementById('pause').addEventListener('click', function() { + wavesurfer.pause(); +}); +let restart = document.getElementById('restart').addEventListener('click', function() { + wavesurfer.play(0); +}); + +function getCursorXY(e) { + return [e.pageX, e.pageY]; +} + +function measure(x, y) { + let spectro = document.getElementById('wave-spectrogram'); + let spectro_x = spectro.offsetLeft; + let spectro_y = spectro.offsetTop; + let spectro_height = spectro.clientHeight; + let spectro_width = spectro.clientWidth; + x = (x - spectro_x); + y = -(-(-y + spectro_y) - spectro_height); + let t = (x / spectro_width) * audio.duration; + let f = (y / spectro_height) * 24; + document.getElementById('cursor-time').innerHTML = Math.round(t); + document.getElementById('cursor-frequency').innerHTML = Math.round(f); +} + +let spectro = document.getElementById("wave-spectrogram"); +console.log(spectro); +spectro.addEventListener('mousemove', function() { + let coords = getCursorXY(window.event); + measure(coords[0], coords[1]); + // console.log(getCursorXY(window.event)); +}); \ No newline at end of file diff --git a/public/larynx/scripts/spectro.larynx.js b/public/larynx/scripts/spectro.larynx.js new file mode 100644 index 0000000..b280a49 --- /dev/null +++ b/public/larynx/scripts/spectro.larynx.js @@ -0,0 +1,78 @@ +let audio = document.getElementById('audio'); +let file_path = audio.src; + +fftsize = 2048; +color = true; + +cnv = document.getElementById("spectrogram"); +width = window.innerWidth; +height = window.innerHeight; + +if (cnv.width != width) { + cnv.width = width; +} + +if (cnv.height != height) { + cnv.height = height; +} + +let now = new Date(); + +if (lastRenderTime) { + instantaneousFPS = now - lastRenderTime; +} + +function renderTimeDomain() { + let times = new Uint8Array(analyser.frequencyBinCount); + analyser.getByteTimeDomainData(times); + + for (var i = 0; i < times.length; i++) { + var value = times[i]; + var percent = value / 256; + var barHeight = height * percent; + var offset = height - barHeight - 1; + var barWidth = width / times.length; + ctx.fillStyle = 'black'; + ctx.fillRect(i * barWidth, offset, 1, 1); + + } +} + +function renderFreqDomain() { + var freq = new Uint8Array(analyser.frequencyBinCount); + analyser.getByteFrequencyData(freq); + cnv.width = width; + cnv.height = height; + var tempCtx = cnv.getContext('2d'); + tempCtx.drawImage(cnv, 0, 0, width, height); + + for (var i = 0; i < freq.length; i++) { + var value; + if (log) { + logIndex = logScale(i, freq.length); + value = freq[logIndex]; + } else { + value = freq[i]; + } + ctx.fillStyle = (color ? getFullColor(value): getGrayColor(value)); + var percent = i / freq.length; + var y = Math.round(percent * height); + + ctx.fillRect(width - speed, height - y, speed, speed); + } + ctx.translate(-speed, 0); + ctx.drawImage(cnv, 0, 0, width, height, 0, 0, width, height); + + ctx.setTransform(1, 0, 0, 1, 0, 0); +} + +function logScale(index, total, opt_base) { + var base = opt_base || 2; + var logmax = logBase(total + 1, base); + var exp = logmax * index / total; + return Math.round(Math.pow(base, exp) - 1); +} + +function renderAxesLabels() { + +} \ No newline at end of file diff --git a/public/larynx/scripts/webaudio.spectro.js b/public/larynx/scripts/webaudio.spectro.js new file mode 100644 index 0000000..5d66785 --- /dev/null +++ b/public/larynx/scripts/webaudio.spectro.js @@ -0,0 +1,6 @@ +let audio = document.getElementById('audio'); +let file_path = audio.src; + +function specgram() { + const analyser = +} \ No newline at end of file diff --git a/public/styles/style.css b/public/styles/style.css index f89c75e..60df212 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -1,325 +1,383 @@ * { - align-self: baseline; + align-self: baseline; } -body{ - margin: 0; - background-color: white; +body { + margin: 0; + background-color: white; } header { - text-align: right; - padding: 0; - margin-top: 1em; + text-align: right; + padding: 0; + margin-top: 1em; } header a { - text-decoration: none; - color: black; + text-decoration: none; + color: black; } -nav, nav .container{ - padding: 0; - margin: 0; +nav, +nav .container { + padding: 0; + margin: 0; } -header .container, nav .container { - display: flex; - justify-content: space-between; +header .container, +nav .container { + display: flex; + justify-content: space-between; } header h1 { - font-size: 4em; - padding: 0; - margin: 0; + font-size: 4em; + padding: 0; + margin: 0; } img.banner { - width: 100%; - height: auto; + width: 100%; + height: auto; } -section{ - padding: 1em; - min-height: 100vh; - width: 50vw; - text-align: left; - margin: auto; +section { + padding: 1em; + min-height: 100vh; + width: 50vw; + text-align: left; + margin: auto; } article { - width: 50vw; - text-align: left; - margin: auto; + width: 50vw; + text-align: left; + margin: auto; } -article h1 { - font-size: 5em; +article h1 { + font-size: 5em; } footer { - color: white; - text-align: center; - background-color: rgba(0,0,0, 0.8); - padding: 1em; + color: white; + text-align: center; + background-color: rgba(0, 0, 0, 0.9); + padding: 1em; } footer a { - text-decoration: none; + text-decoration: none; } footer img { - height: 1em; + height: 1em; } label { - width: 40em; - display: inline; + width: 40em; + display: inline; } + label span.info { - display: none; + display: none; } label:hover span.info { - display: inline-block; - position: relative; - background: yellow; - padding: 0.5em; - border-radius: 5px; + display: inline-block; + position: relative; + background: yellow; + padding: 0.5em; + border-radius: 5px; } input { - margin: 0.1em; + margin: 0.1em; } /* Style inputs with type="text", select elements and textareas */ -input[type=text], input[type=email], input[type=url], input[type=password], input[type=number], input[type=date], input[type=time], select, textarea { - width: 100%; /* Full width */ - padding: 12px; /* Some padding */ - border: 1px solid #ccc; /* Gray border */ - border-radius: 4px; /* Rounded borders */ - box-sizing: border-box; /* Make sure that padding and width stays in place */ - margin-top: 6px; /* Add a top margin */ - margin-bottom: 16px; /* Bottom margin */ - resize: vertical /* Allow the user to vertically resize the textarea (not horizontally) */ +input[type=text], +input[type=email], +input[type=url], +input[type=password], +input[type=number], +input[type=date], +input[type=time], +select, +textarea { + width: 100%; + /* Full width */ + padding: 12px; + /* Some padding */ + border: 1px solid #ccc; + /* Gray border */ + border-radius: 4px; + /* Rounded borders */ + box-sizing: border-box; + /* Make sure that padding and width stays in place */ + margin-top: 6px; + /* Add a top margin */ + margin-bottom: 16px; + /* Bottom margin */ + resize: vertical + /* Allow the user to vertically resize the textarea (not horizontally) */ } /* Style the submit button with a specific background color etc */ input[type=submit] { - background-color: #4CAF50; - color: white; - padding: 12px 20px; - border: none; - border-radius: 4px; - cursor: pointer; + background-color: #4CAF50; + color: white; + padding: 12px 20px; + border: none; + border-radius: 4px; + cursor: pointer; } input[type=reset] { - background-color: #af4c4c; - color: white; - padding: 12px 20px; - border: none; - border-radius: 4px; - cursor: pointer; + background-color: #af4c4c; + color: white; + padding: 12px 20px; + border: none; + border-radius: 4px; + cursor: pointer; } /* When moving the mouse over the submit button, add a darker green color */ input[type=submit]:hover { - background-color: #45a049; + background-color: #45a049; } /* When moving the mouse over the reset button, add a darker red color */ input[type=reset]:hover { - background-color: #923737; + background-color: #923737; } /* Add a background color and some padding around the form */ .container { - border-radius: 5px; - background-color: #f2f2f2; - padding: 1em; + border-radius: 5px; + background-color: #f2f2f2; + padding: 1em; } nav { - border-bottom: 1px solid black; - position: flex; - display: block; - width: 100%; - background-color: white; - z-index: 10; + border-bottom: 1px solid black; + position: flex; + display: block; + width: 100%; + background-color: white; + z-index: 10; } nav div { - padding: 1em; + padding: 1em; } nav.dark-theme { - background: black; + background: black; } nav.dark-theme ul li { - color: white; + color: white; } nav ul { - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; } nav ul a { - text-decoration: none; + text-decoration: none; } nav ul li { - list-style: none; - color: black; - margin: 0.5em; + list-style: none; + color: black; + margin: 0.5em; } iframe::parent { - padding: 0; - margin: 0; + padding: 0; + margin: 0; } iframe { - width: 100%; - height: 750px; + width: 100%; + height: 750px; } #previewFrame { - width: 300px; - height: 200px; - resize: both; - zoom: 0.2; - -moz-transform: scale(0.5); - -moz-transform-origin: 0 0; - -o-transform: scale(0.5); - -o-transform-origin: 0 0; - -webkit-transform: scale(0.5); - -webkit-transform-origin: 0 0; + width: 300px; + height: 200px; + resize: both; + zoom: 0.2; + -moz-transform: scale(0.5); + -moz-transform-origin: 0 0; + -o-transform: scale(0.5); + -o-transform-origin: 0 0; + -webkit-transform: scale(0.5); + -webkit-transform-origin: 0 0; } -iframe html body { - font-size: 0.1em; + +iframe html body { + font-size: 0.1em; } .container { - display: flex; - padding-top: 0; - padding-bottom: 0; + display: flex; + padding-top: 0; + padding-bottom: 0; } .container.row { - flex-direction: row; + flex-direction: row; } .container.column { - flex-direction: column; + flex-direction: column; } -h3, p{ - padding: 1em; - width: auto; +h3, +p { + padding: 1em; + width: auto; } p.large { - width: auto; + width: auto; } table { - border-radius: 5px; - width: 100%; - border-collapse: collapse; + border-radius: 5px; + width: 100%; + border-collapse: collapse; } -td, th { - border: 1px solid #dddddd; - text-align: left; - padding: 0.5em; + +td, +th { + border: 1px solid #dddddd; + text-align: left; + padding: 0.5em; } tr:nth-child(even) { - background-color: #dddddd; + background-color: #dddddd; } table a { - text-decoration: none; - color: black; + text-decoration: none; + color: black; } table a:hover { - font-style: italic; + font-style: italic; } #searchbar { - display: flex; - flex-direction: row; - align-self: right; + display: flex; + flex-direction: row; + align-self: right; } ul.breadcrumb { - display: flex; - flex-basis: row; + display: flex; + flex-basis: row; } ul.breadcrumb li { - list-style: url("/media/icons/arrow.svg"); - margin-left: 2em; + list-style: url("/media/icons/arrow.svg"); + margin-left: 2em; } ul.breadcrumb li.active { - font-weight: bold; + font-weight: bold; } div.coordinates { - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; } div.coordinates input[type="text"] { - width: 25em; + width: 25em; } .sci-name { - font-style: italic; + font-style: italic; } .error { - border: solid red; - border-radius: 5px; - padding: 0.5em; - background-color: rgba(255, 0, 0, 0.4) + border: solid red; + border-radius: 5px; + padding: 0.5em; + background-color: rgba(255, 0, 0, 0.4) } .sound { - background-color: rgba(0, 255, 0, 0.5); - padding: 1em; - border-radius: 5px; - overflow: auto; + background-color: rgba(0, 255, 0, 0.5); + padding: 1em; + border-radius: 5px; + overflow: auto; } #spectrogram { - width: 100%; - max-width: 75vh; + width: 100%; + max-width: 75vh; } button img { - max-height: 1em; + max-height: 1em; } form#url-form { - display: none; - position: fixed; - top: 50%; - left: 35%; - width: 15em; - z-index: 1; - background-color: white; - padding: 1em; - border-radius: 5px; - border: 1px solid #ccc; + display: none; + position: fixed; + top: 50%; + left: 35%; + width: 15em; + z-index: 1; + background-color: white; + padding: 1em; + border-radius: 5px; + border: 1px solid #ccc; } table#replies td { - background-color: white; + background-color: white; } + + +#larynx { + background-color: rgba(0, 0, 0, 0.8); + width: auto; + min-height: 100vh; + margin: 0; + + color: white; + padding: 1em; +} + +#larynx h2 { + text-align: center; +} + +#larynx { + text-align: center; +} + +#larynx .license { + text-align: left; +} + +#larynx canvas { + width: 100vw; + position: relative; + left: 0; +} + +#larynx .container { + background: transparent; + display: flex; + justify-content: space-between; +} \ No newline at end of file