From ca1959a672bd680818adfb97f75948b5c445fc17 Mon Sep 17 00:00:00 2001 From: Samuel ORTION Date: Sun, 18 Apr 2021 16:59:01 +0200 Subject: [PATCH] Added API doc --- public/api/index.php | 5 + public/api/v1/doc/index.php | 26 +++ public/api/v1/index.php | 30 +++ public/discussion/index.php | 88 +++++++++ public/discussion/messages/index.php | 42 ++-- public/discussion/notification.php | 35 ++++ public/explore/guano/index.php | 46 ++--- public/explore/record/index.php | 42 ++-- public/explore/search/searchrecord.php | 23 +-- public/header.php | 3 +- public/larynx/g-spectrogram.html | 34 ++++ public/larynx/index.php | 65 +++--- public/larynx/scripts/g-spectro.js | 261 ++++++++++++++++++++++++ public/larynx/v/2.0/index.php | 101 ++++++++++ public/larynx/v/2.0/scripts/script.js | 263 +++++++++++++++++++++++++ public/larynx/v/2.0/scripts/spectro.js | 67 +++++++ public/menu.php | 7 + public/search/searchbar.php | 4 - public/styles/style.css | 26 ++- 19 files changed, 1062 insertions(+), 106 deletions(-) create mode 100644 public/api/index.php mode change 100755 => 100644 public/api/v1/index.php create mode 100644 public/discussion/index.php create mode 100644 public/discussion/notification.php create mode 100644 public/larynx/g-spectrogram.html create mode 100644 public/larynx/scripts/g-spectro.js create mode 100644 public/larynx/v/2.0/index.php create mode 100644 public/larynx/v/2.0/scripts/script.js create mode 100644 public/larynx/v/2.0/scripts/spectro.js diff --git a/public/api/index.php b/public/api/index.php new file mode 100644 index 0000000..6f13ce5 --- /dev/null +++ b/public/api/index.php @@ -0,0 +1,5 @@ + @@ -21,6 +26,27 @@ $root = realpath($_SERVER["DOCUMENT_ROOT"]); ?>

Documentation API v1

+ text($md)); + } else { + $doc_paths = glob("$root/api/v1/doc/md/*.md"); + ?> + +
+ + + + + + + API v1 Documentation | Chiro - Canto + + + + + +
+

API v1

+ Documentation +
+ + + \ No newline at end of file diff --git a/public/discussion/index.php b/public/discussion/index.php new file mode 100644 index 0000000..cbd4ff4 --- /dev/null +++ b/public/discussion/index.php @@ -0,0 +1,88 @@ + PDO::ERRMODE_EXCEPTION + )); +} catch (Exception $e) { + die("Error : ".$e->getMessage()); +} + +$req = $db->prepare('SELECT id FROM `authors` WHERE username=:username'); +$req->execute(array( + "username"=>$_SESSION['username'] +)); +$data = $req->fetch(); +$user_id = $data['id']; + +$req = $db->prepare('SELECT message_by, COUNT(message_by) AS n_msg FROM `messages` WHERE message_to=:user_id AND message_read=0'); +$req->execute(array( + "user_id"=>$user_id +)); +$result = $req->fetchAll(); + +?> + + + + + + + Discussion | Chiro - Canto + + + + + + +
+

Discussion

+

New messages

+ +

Send a message

+
+ + + +
+
+ + + + \ No newline at end of file diff --git a/public/discussion/messages/index.php b/public/discussion/messages/index.php index 632ef52..d515ca9 100644 --- a/public/discussion/messages/index.php +++ b/public/discussion/messages/index.php @@ -22,29 +22,38 @@ if (isset($_SESSION['username'])) { "username"=>$_SESSION['username'] )); if ($data = $req->fetch()) { - $user_id = $data['id']; + $sender_id = $data['id']; } } else { $_SESSION['error_msg'] = "You must be logged in to receive an send message."; header('Location: /auth/login'); } if (isset($_GET['author'])) { - $req = $db->prepare('SELECT * FROM `messages` WHERE message_by=:user_id AND message_to=:author_id OR message_by=:author_id AND message_to=:user_id ORDER BY message_datetime ASC'); + if (!is_numeric($_GET['author'])) { + $req = $db->prepare('SELECT id FROM `authors` WHERE username=:username'); + $req->execute(array( + "username"=>$_GET['author'] + )); + if ($data = $req->fetch()) { + $user_id = $data['id']; + } else { + $user_id = $_GET['author']; + } + $req = $db->prepare('SELECT username FROM `authors` WHERE id=:id'); + $req->execute(array( + "id"=>$sender_id + )); + if ($data = $req->fetch()) { + $addressee = $data['username']; + } + } + $req = $db->prepare('SELECT * FROM `messages` WHERE message_by=:sender_id AND message_to=:user_id OR message_by=:sender_id AND message_to=:user_id ORDER BY message_datetime ASC'); $req->execute(array( - "author_id"=>$user_id, - "user_id"=>$_GET['author'] + "sender_id"=>$sender_id, + "user_id"=>$user_id )); $result = $req->fetchAll(); } - -$req = $db->prepare('SELECT username FROM `authors` WHERE id=:id'); -$req->execute(array( - "id"=>$_GET['author'] -)); -if ($data = $req->fetch()) { - $destinator = $data['username']; -} - ?> @@ -66,10 +75,15 @@ include("$root/analytics/matomo.php");

Discussion

- +
prepare('UPDATE `messages` SET message_read=1 WHERE id=:id'); + $req->execute(array( + "id"=>$message_id + )); if ($message['message_by'] == $_SESSION['username']) { $class = "right"; } else { diff --git a/public/discussion/notification.php b/public/discussion/notification.php new file mode 100644 index 0000000..69c48cd --- /dev/null +++ b/public/discussion/notification.php @@ -0,0 +1,35 @@ + PDO::ERRMODE_EXCEPTION + )); +} catch (Exception $e) { + die("Error : ".$e->getMessage()); +} + +if (isset($_SESSION['username'])) { + $req = $db->prepare('SELECT id FROM `authors` WHERE username=:username'); + $req->execute(array( + "username"=>$_SESSION['username'] + )); + $data = $req->fetch(); + $user_id = $data['id']; + $req = $db->prepare('SELECT COUNT(*) AS counter FROM `messages` WHERE message_to=:user_id AND message_read=0'); + $req->execute(array( + "user_id"=>$user_id + )); + $data = $req->fetch(); + $new_msg_number = $data['counter']; + if ($new_msg_number != 0) { + ?> + + + + + + + + + GUANO | Chiro-Canto + + + + + + +
+

GUANO v1.0

+ '.$_SESSION['error_msg'].'
' : "";?> + about GUANO +
+ fetch()) { } else { $_SESSION['error_msg'] = "Can't fetch data."; } -?> - - - - - - - GUANO | Chiro-Canto - - - - - - -
-

GUANO v1.0

- '.$_SESSION['error_msg'].'' : "";?> - about GUANO -
+ ?> GUANO|Version: 1.0

Original Filename:
diff --git a/public/explore/record/index.php b/public/explore/record/index.php index 0092b43..267059d 100644 --- a/public/explore/record/index.php +++ b/public/explore/record/index.php @@ -4,27 +4,6 @@ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); $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['id'])) { - $req = $db->prepare('SELECT * FROM `records` WHERE id=:id'); - $req->execute(array( - "id"=>$_GET['id'] - )); -} else { - $req = $db->prepare('SELECT * FROM `records` ORDER BY date'); - $req->execute(); -} ?> @@ -45,6 +24,27 @@ include("$root/analytics/owa.php");

Record

PDO::ERRMODE_EXCEPTION + )); + } catch (Exception $e) { + die("Error : ".$e->getMessage()); + } + + if (isset($_GET['id'])) { + $req = $db->prepare('SELECT * FROM `records` WHERE id=:id'); + $req->execute(array( + "id"=>$_GET['id'] + )); + } else { + $req = $db->prepare('SELECT * FROM `records` ORDER BY date'); + $req->execute(); + } if ($data = $req->fetch()) { ?>
diff --git a/public/explore/search/searchrecord.php b/public/explore/search/searchrecord.php index ef72f56..c238350 100755 --- a/public/explore/search/searchrecord.php +++ b/public/explore/search/searchrecord.php @@ -56,21 +56,21 @@ if ($_SESSION['error_msg'] == "") { if ($and) { $sql .= " AND "; } - $sql .= ' species="'.$_SESSION['query']['species'].'"'; + $sql .= ' species LIKE "%'.$_SESSION['query']['species'].'%"'; $and = True; } if (isset($_SESSION['query']['subspecies']) and $_SESSION['query']['subspecies'] != "") { if ($and) { $sql .= " AND "; } - $sql .= ' subspecies="'.$_SESSION['query']['subspecies'].'"'; + $sql .= ' subspecies LIKE "%'.$_SESSION['query']['subspecies'].'%"'; $and = True; } if (isset($_SESSION['query']['recordist']) and $_SESSION['query']['recordist'] != "") { if ($and) { $sql .= " AND "; } - $sql .= ' recordist_name="'.$_SESSION['query']['recordist'].'"'; + $sql .= ' recordist_name LIKE "%'.$_SESSION['query']['recordist'].'%"'; $and = True; } if (isset($_SESSION['query']['date-after']) and $_SESSION['query']['date-after'] != "") { @@ -96,10 +96,6 @@ if ($_SESSION['error_msg'] == "") { $and = True; } } - // echo $sql; - $req = $db->prepare($sql); - $req->execute(); - $result = $req->fetchAll(); } else { $_SESSION['error_msg'] .= "You did not enter any query.\n"; header("Location: /explore/search"); @@ -108,9 +104,7 @@ if ($_SESSION['error_msg'] == "") { } else { header("Location: /explore/search"); } - -if (isset($result)) { - ?> +?> @@ -128,6 +122,11 @@ if (isset($result)) {

Explore

Search Results

prepare($sql); + $req->execute(); + $result = $req->fetchAll(); + // print_r($sql); if (empty($result)) { echo "No result for this query, please try again.\n"; } else { @@ -163,6 +162,4 @@ if (isset($result)) { - - \ No newline at end of file diff --git a/public/header.php b/public/header.php index c3977cc..7790b59 100755 --- a/public/header.php +++ b/public/header.php @@ -5,7 +5,6 @@

Chiro - Canto

Bat sound sharing tools

-
- + \ No newline at end of file diff --git a/public/larynx/g-spectrogram.html b/public/larynx/g-spectrogram.html new file mode 100644 index 0000000..d7c01ea --- /dev/null +++ b/public/larynx/g-spectrogram.html @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/public/larynx/index.php b/public/larynx/index.php index 693b1e0..cd07437 100755 --- a/public/larynx/index.php +++ b/public/larynx/index.php @@ -5,27 +5,6 @@ error_reporting(E_ALL); session_start(); $root = realpath($_SERVER["DOCUMENT_ROOT"]); $version = "1.0"; -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(); ?> @@ -35,14 +14,15 @@ $data = $req->fetch(); Larynx | Chiro - Canto + + + + - @@ -55,8 +35,31 @@ include("$root/analytics/matomo.php");

Larynx v

- - + 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(); + } + if ($data = $req->fetch()) { + // print_r($data); + ?> +
@@ -90,7 +93,13 @@ include("$root/analytics/matomo.php"); - + - \ No newline at end of file + + \ No newline at end of file diff --git a/public/larynx/scripts/g-spectro.js b/public/larynx/scripts/g-spectro.js new file mode 100644 index 0000000..245b3c3 --- /dev/null +++ b/public/larynx/scripts/g-spectro.js @@ -0,0 +1,261 @@ +var AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext; +context = new AudioContext(); + +// 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/v/2.0/index.php b/public/larynx/v/2.0/index.php new file mode 100644 index 0000000..3d09435 --- /dev/null +++ b/public/larynx/v/2.0/index.php @@ -0,0 +1,101 @@ + + + + + + + + Larynx | Chiro - Canto + + + + + + +
+

Larynx

+

A web tool for bat sound analysis

+ +
+
+
+
+

Larynx v

+ 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(); + } + if ($data = $req->fetch()) { + // print_r($data); + ?> + +
+
+
+ +
+
+
+ T: + + s F: + + kHz +
+
+ + + +
+
+

+
+ + +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/public/larynx/v/2.0/scripts/script.js b/public/larynx/v/2.0/scripts/script.js new file mode 100644 index 0000000..41f7924 --- /dev/null +++ b/public/larynx/v/2.0/scripts/script.js @@ -0,0 +1,263 @@ +let analyser; +let context; +let fftsize = 2048; +let oscillator = true; +let log; +let percent; +let times; +let speed = 0.1; +let bufferLength; +let labels; +let color = true; +let width = window.innerwidth; +let height = window.innerheight; + + +let canvas = document.getElementById('spectrogram-canvas'); +let ctx = canvas.getContext('2d'); + +let lastRenderTime = new Date(); +let instantaneousFPS = 0; + +let audio = document.getElementById('audio'); + +document.getElementById("launch-larynx").addEventListener('click', function () { + loadSound(audio.src) + startLarynx(); +}); + +function loadSound(url) { + let AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext; + context = new AudioContext(); + let source = context.createMediaElementSource(audio); +} + +function startLarynx() { + analyser = context.createAnalyser(); + render(); +} + + +function render() { + let didResize = false; + + analyser = context.createAnalyser(); + + // Ensure dimensions are accurate. + if (canvas.width != width) { + canvas.width = width; + didResize = true; + } + if (canvas.height != height) { + canvas.height = height; + didResize = true; + } + + renderTimeDomain(); + renderFreqDomain(); + + if (labels && didResize) { + renderAxesLabels(); + } + + let now = new Date(); + if (lastRenderTime) { + instantaneousFPS = now - lastRenderTime; + } + lastRenderTime = now; +} + +function draw() { + drawVisual = requestAnimationFrame(draw); + analyser.getByteTimeDomainData(times); + + ctx.fillStyle = 'rgb(200, 200, 200)'; + ctx.fillRect(0, 0, width, height); + + ctx.linewidth = 2; + ctx.strokeStyle = 'rgb(0, 0, 0)'; + + ctx.beginPath(); + + var slicewidth = width * 1.0 / bufferLength; + var x = 0; + + for (var i = 0; i < bufferLength; i++) { + + var v = times[i] / 128.0; + var y = v * height / 2; + + if (i === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + + x += slicewidth; + } + + ctx.lineTo(canvas.width, canvas.height / 2); + ctx.stroke(); +} + +function renderTimeDomain() { + bufferLength = analyser.frequencyBinCount; + times = new Uint8Array(bufferLength); + draw(); +} + +function renderFreqDomain() { + let freq = new Uint8Array(analyser.frequencyBinCount); + analyser.getByteFrequencyData(freq); + + for (let i = 0; i < freq.length; i++) { + let value; + // Draw each pixel with the specific color. + if (log) { + logIndex = logScale(i, freq.length); + value = freq[logIndex]; + } else { + value = freq[i]; + } + ctx.fillStyle = (color ? getFullColor(value) : getGrayColor(value)); + + let perceint = i / freq.length; + let y = Math.round(percent * height); + + // Draw the line at the right side of the canvas. + ctx.fillRect(width - speed, height - y, speed, speed); + } + ctx.translate(-speed, 0); + // Reset the transformation matrix + ctx.setTransform(1, 0, 0, 1, 0, 0); +} + +function logScale(index, total, opt_base) { + let base = opt_base || 2; + let logmax = logBase(total + 1, base); + let exp = logmax * index / total; + return Math.round(Math.pow(base, exp) - 1); +} + +function logBase(val, base) { + return Math.log(val) / Math.log(base); +} + + +function renderAxesLabels() { + canvas.width = width; + canvas.height = height; + let ctx = canvas.getContext('2d'); + let startFreq = 440; + let nyquist = context.sampleRate / 2; + let endFreq = nyquist - startFreq; + let step = (endFreq - startFreq) / ticks; + let yLabelOffset = 5; + // Render the vertical frequency axis. + for (let i = 0; i <= ticks; i++) { + let freq = startFreq + (step * i); + // Get the y coordinate from the current label. + let index = freqToIndex(freq); + let percent = index / getFFTBinCount(); + let y = (1 - percent) * height; + let x = width - 60; + // Get the value for the current y coordinate. + let label; + if (log) { + // Handle a logarithmic scale. + let logIndex = logScale(index, getFFTBinCount()); + // Never show 0 Hz. + freq = Math.max(1, indexToFreq(logIndex)); + } + label = formatFreq(freq); + let units = 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); + } +} + +function clearAxesLabels() { + let canvas = labels; + let ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, width, height); +} + +function formatFreq(freq) { + return (freq >= 1000 ? (freq / 1000).toFixed(1) : Math.round(freq)); +} + +function formatUnits(freq) { + return (freq >= 1000 ? 'KHz' : 'Hz'); +} + +function indexToFreq(index) { + let nyquist = context.sampleRate / 2; + return nyquist / getFFTBinCount() * index; +} + +function freqToIndex(frequency) { + let nyquist = context.sampleRate / 2; + return Math.round(frequency / nyquist * getFFTBinCount()); +} + +function getFFTBinCount() { + return fftsize / 2; +} + +// function onStream(stream) { +// let input = context.createMediaStreamSource(stream); +// let analyser = context.createAnalyser(); +// analyser.smoothingTimeConstant = 0; +// analyser.fftSize = fftsize; + +// // Connect graph. +// input.connect(analyser); + +// // Setup a timer to visualize some stuff. +// render(); +// } + +function onStreamError(e) { + console.error(e); +} + +function getGrayColor(value) { + return 'rgb(V, V, V)'.replace(/V/g, 255 - value); +} + +function getFullColor(value) { + let fromH = 62; + let toH = 0; + let percent = value / 255; + let delta = percent * (toH - fromH); + let hue = fromH + delta; + return 'hsl(H, 100%, 50%)'.replace(/H/g, hue); +} + +function logChanged() { + if (labels) { + renderAxesLabels(); + } +} + +function ticksChanged() { + if (labels) { + renderAxesLabels(); + } +} + +function labelsChanged() { + if (labels) { + renderAxesLabels(); + } else { + clearAxesLabels(); + } +} + diff --git a/public/larynx/v/2.0/scripts/spectro.js b/public/larynx/v/2.0/scripts/spectro.js new file mode 100644 index 0000000..cff6d41 --- /dev/null +++ b/public/larynx/v/2.0/scripts/spectro.js @@ -0,0 +1,67 @@ +var audio = document.getElementById('audio'); + +let button = document.getElementById('launch-larynx').addEventListener('click', function(){ + loadSound(audio.src); + render(); +}); + +let audioCtx; +let source; +let analyser; +let canvasCtx = document.getElementById('spectrogram-canvas'); + +let WIDTH = canvasCtx.width; +let HEIGHT = canvasCtx.height; + +function loadSound(url) { + audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + source = audioCtx.createMediaElementSource(audio); +} + +function render() { + analyser = audioCtx.createAnalyser(); + + analyser.fftSize = 2048; + var bufferLength = analyser.frequencyBinCount; + var dataArray = new Uint8Array(bufferLength); + analyser.getByteTimeDomainData(dataArray); + + // draw an oscilloscope of the current audio source + + function draw() { + + drawVisual = requestAnimationFrame(draw); + + analyser.getByteTimeDomainData(dataArray); + + canvasCtx.fillStyle = 'rgb(200, 200, 200)'; + canvasCtx.fillRect(0, 0, WIDTH, HEIGHT); + + canvasCtx.lineWidth = 2; + canvasCtx.strokeStyle = 'rgb(0, 0, 0)'; + + canvasCtx.beginPath(); + + var sliceWidth = WIDTH * 1.0 / bufferLength; + var x = 0; + + for (var i = 0; i < bufferLength; i++) { + + var v = dataArray[i] / 128.0; + var y = v * HEIGHT / 2; + + if (i === 0) { + canvasCtx.moveTo(x, y); + } else { + canvasCtx.lineTo(x, y); + } + + x += sliceWidth; + } + + canvasCtx.lineTo(canvas.width, canvas.height / 2); + canvasCtx.stroke(); + }; + + draw(); +} \ No newline at end of file diff --git a/public/menu.php b/public/menu.php index 59205e6..f330c92 100755 --- a/public/menu.php +++ b/public/menu.php @@ -45,9 +45,16 @@
  • larynx
  • + +
  • API
  • +
  • forum
  • + +
  • messages
  • + +
  • mysteries
  • diff --git a/public/search/searchbar.php b/public/search/searchbar.php index 1269808..e69de29 100755 --- a/public/search/searchbar.php +++ b/public/search/searchbar.php @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/public/styles/style.css b/public/styles/style.css index fb5632f..6a3176a 100755 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -1,5 +1,6 @@ * { align-self: baseline; + overflow: auto; } body { @@ -278,9 +279,8 @@ table a:hover { } #searchbar { - display: flex; + display: inline-flex; flex-direction: row; - align-self: right; } ul.breadcrumb { @@ -631,3 +631,25 @@ i, .fa { background-color: lightgreen; border-radius: 8px 8px 0px 8px; } + +.notification { + text-decoration: none; + position: relative; + display: inline-block; +} + +.notification span { + padding: 1em; +} + +.notification .badge { + position: absolute; + font-size: 0.5em; + font-weight: bold; + top: -0.5em; + right: -0.5em; + padding: 5px; + border-radius: 50%; + background: red; + color: white; +}