diff --git a/dist/extension.zip b/dist/extension.zip index fa3a0ec..34c527e 100644 Binary files a/dist/extension.zip and b/dist/extension.zip differ diff --git a/dist/extension/birb.js b/dist/extension/birb.js index bf9aa73..2286db9 100644 --- a/dist/extension/birb.js +++ b/dist/extension/birb.js @@ -857,6 +857,49 @@ } } + // @ts-check + + class Birdsong { + + /** + * @type {AudioContext} + */ + audioContext; + + chirp() { + if (!this.audioContext) { + this.audioContext = new AudioContext(); + } + + const TIMES = [0, 0.06, 0.16]; + const FREQUENCIES = [2200, + 3500 + Math.random() * 700, + 1600 + Math.random() * 400]; + const VOLUMES = [0.0001, 0.3, 0.0001]; + + const oscillator = this.audioContext.createOscillator(); + oscillator.type = "sine"; + const gain = this.audioContext.createGain(); + oscillator.connect(gain); + gain.connect(this.audioContext.destination); + + const now = this.audioContext.currentTime; + for (let i = 0; i < TIMES.length; i++) { + const time = TIMES[i] + now; + if (i === 0) { + oscillator.frequency.setValueAtTime(FREQUENCIES[i], time); + gain.gain.setValueAtTime(VOLUMES[i], time); + } else { + oscillator.frequency.exponentialRampToValueAtTime(FREQUENCIES[i], time); + gain.gain.exponentialRampToValueAtTime(VOLUMES[i], time); + } + } + + oscillator.start(now); + oscillator.stop(now + TIMES[TIMES.length - 1]); + } + } + const SAVE_KEY = "birbSaveData"; /** @@ -1819,7 +1862,7 @@ insertModal(`${birdBirb()} Mode`, message); }), new Separator(), - new MenuItem("2026.1.1", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.1"); }, false), + new MenuItem("2026.1.4", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.4"); }, false), ]; const styleElement = document.createElement("style"); @@ -1833,6 +1876,8 @@ FLYING: "flying", }; + const birdsong = new Birdsong(); + let frozen = false; let stateStart = Date.now(); let currentState = States.IDLE; @@ -2513,6 +2558,7 @@ function pet() { if (currentState === States.IDLE && birb.getCurrentAnimation() !== Animations.HEART) { + birdsong.chirp(); birb.setAnimation(Animations.HEART); lastPetTimestamp = Date.now(); } diff --git a/dist/extension/manifest.json b/dist/extension/manifest.json index d87779c..4584664 100644 --- a/dist/extension/manifest.json +++ b/dist/extension/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "Pocket Bird", "description": "It's a pet bird in your browser, what more could you want?", - "version": "2026.1.1", + "version": "2026.1.4", "homepage_url": "https://idreesinc.com", "icons": { "48": "images/icons/transparent/48x48x1.png", diff --git a/dist/obsidian/main.js b/dist/obsidian/main.js index 21af991..981ab11 100644 --- a/dist/obsidian/main.js +++ b/dist/obsidian/main.js @@ -1,7 +1,7 @@ const { Plugin, Notice } = require('obsidian'); module.exports = class PocketBird extends Plugin { onload() { - console.log("Loading Pocket Bird version 2026.1.1..."); + console.log("Loading Pocket Bird version 2026.1.4..."); const OBSIDIAN_PLUGIN = this; (function () { 'use strict'; @@ -862,6 +862,49 @@ module.exports = class PocketBird extends Plugin { } } + // @ts-check + + class Birdsong { + + /** + * @type {AudioContext} + */ + audioContext; + + chirp() { + if (!this.audioContext) { + this.audioContext = new AudioContext(); + } + + const TIMES = [0, 0.06, 0.16]; + const FREQUENCIES = [2200, + 3500 + Math.random() * 700, + 1600 + Math.random() * 400]; + const VOLUMES = [0.0001, 0.3, 0.0001]; + + const oscillator = this.audioContext.createOscillator(); + oscillator.type = "sine"; + const gain = this.audioContext.createGain(); + oscillator.connect(gain); + gain.connect(this.audioContext.destination); + + const now = this.audioContext.currentTime; + for (let i = 0; i < TIMES.length; i++) { + const time = TIMES[i] + now; + if (i === 0) { + oscillator.frequency.setValueAtTime(FREQUENCIES[i], time); + gain.gain.setValueAtTime(VOLUMES[i], time); + } else { + oscillator.frequency.exponentialRampToValueAtTime(FREQUENCIES[i], time); + gain.gain.exponentialRampToValueAtTime(VOLUMES[i], time); + } + } + + oscillator.start(now); + oscillator.stop(now + TIMES[TIMES.length - 1]); + } + } + const ROOT_PATH = ""; /** @@ -1862,7 +1905,7 @@ module.exports = class PocketBird extends Plugin { insertModal(`${birdBirb()} Mode`, message); }), new Separator(), - new MenuItem("2026.1.1", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.1"); }, false), + new MenuItem("2026.1.4", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.4"); }, false), ]; const styleElement = document.createElement("style"); @@ -1876,6 +1919,8 @@ module.exports = class PocketBird extends Plugin { FLYING: "flying", }; + const birdsong = new Birdsong(); + let frozen = false; let stateStart = Date.now(); let currentState = States.IDLE; @@ -2556,6 +2601,7 @@ module.exports = class PocketBird extends Plugin { function pet() { if (currentState === States.IDLE && birb.getCurrentAnimation() !== Animations.HEART) { + birdsong.chirp(); birb.setAnimation(Animations.HEART); lastPetTimestamp = Date.now(); } diff --git a/dist/obsidian/manifest.json b/dist/obsidian/manifest.json index 69435cc..4aae7d6 100644 --- a/dist/obsidian/manifest.json +++ b/dist/obsidian/manifest.json @@ -1,7 +1,7 @@ { "id": "pocket-bird", "name": "Pocket Bird", - "version": "2026.1.1", + "version": "2026.1.4", "minAppVersion": "0.15.0", "description": "Add a pet bird to fly around your notes and keep you company!", "author": "Idrees Hassan", diff --git a/dist/userscript/birb.user.js b/dist/userscript/birb.user.js index fe59ca8..e4bf5d7 100644 --- a/dist/userscript/birb.user.js +++ b/dist/userscript/birb.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Pocket Bird // @namespace https://idreesinc.com -// @version 2026.1.1 +// @version 2026.1.4 // @description It's a pet bird in your browser, what more could you want? // @author Idrees // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/userscript/birb.user.js @@ -871,6 +871,49 @@ } } + // @ts-check + + class Birdsong { + + /** + * @type {AudioContext} + */ + audioContext; + + chirp() { + if (!this.audioContext) { + this.audioContext = new AudioContext(); + } + + const TIMES = [0, 0.06, 0.16]; + const FREQUENCIES = [2200, + 3500 + Math.random() * 700, + 1600 + Math.random() * 400]; + const VOLUMES = [0.0001, 0.3, 0.0001]; + + const oscillator = this.audioContext.createOscillator(); + oscillator.type = "sine"; + const gain = this.audioContext.createGain(); + oscillator.connect(gain); + gain.connect(this.audioContext.destination); + + const now = this.audioContext.currentTime; + for (let i = 0; i < TIMES.length; i++) { + const time = TIMES[i] + now; + if (i === 0) { + oscillator.frequency.setValueAtTime(FREQUENCIES[i], time); + gain.gain.setValueAtTime(VOLUMES[i], time); + } else { + oscillator.frequency.exponentialRampToValueAtTime(FREQUENCIES[i], time); + gain.gain.exponentialRampToValueAtTime(VOLUMES[i], time); + } + } + + oscillator.start(now); + oscillator.stop(now + TIMES[TIMES.length - 1]); + } + } + const SAVE_KEY = "birbSaveData"; /** @@ -1824,7 +1867,7 @@ insertModal(`${birdBirb()} Mode`, message); }), new Separator(), - new MenuItem("2026.1.1", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.1"); }, false), + new MenuItem("2026.1.4", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.4"); }, false), ]; const styleElement = document.createElement("style"); @@ -1838,6 +1881,8 @@ FLYING: "flying", }; + const birdsong = new Birdsong(); + let frozen = false; let stateStart = Date.now(); let currentState = States.IDLE; @@ -2518,6 +2563,7 @@ function pet() { if (currentState === States.IDLE && birb.getCurrentAnimation() !== Animations.HEART) { + birdsong.chirp(); birb.setAnimation(Animations.HEART); lastPetTimestamp = Date.now(); } diff --git a/dist/web/birb.embed.js b/dist/web/birb.embed.js index 1c0c2f9..f068ab6 100644 --- a/dist/web/birb.embed.js +++ b/dist/web/birb.embed.js @@ -857,6 +857,49 @@ } } + // @ts-check + + class Birdsong { + + /** + * @type {AudioContext} + */ + audioContext; + + chirp() { + if (!this.audioContext) { + this.audioContext = new AudioContext(); + } + + const TIMES = [0, 0.06, 0.16]; + const FREQUENCIES = [2200, + 3500 + Math.random() * 700, + 1600 + Math.random() * 400]; + const VOLUMES = [0.0001, 0.3, 0.0001]; + + const oscillator = this.audioContext.createOscillator(); + oscillator.type = "sine"; + const gain = this.audioContext.createGain(); + oscillator.connect(gain); + gain.connect(this.audioContext.destination); + + const now = this.audioContext.currentTime; + for (let i = 0; i < TIMES.length; i++) { + const time = TIMES[i] + now; + if (i === 0) { + oscillator.frequency.setValueAtTime(FREQUENCIES[i], time); + gain.gain.setValueAtTime(VOLUMES[i], time); + } else { + oscillator.frequency.exponentialRampToValueAtTime(FREQUENCIES[i], time); + gain.gain.exponentialRampToValueAtTime(VOLUMES[i], time); + } + } + + oscillator.start(now); + oscillator.stop(now + TIMES[TIMES.length - 1]); + } + } + const SAVE_KEY = "birbSaveData"; /** @@ -1804,7 +1847,7 @@ insertModal(`${birdBirb()} Mode`, message); }), new Separator(), - new MenuItem("2026.1.1", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.1"); }, false), + new MenuItem("2026.1.4", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.4"); }, false), ]; const styleElement = document.createElement("style"); @@ -1818,6 +1861,8 @@ FLYING: "flying", }; + const birdsong = new Birdsong(); + let frozen = false; let stateStart = Date.now(); let currentState = States.IDLE; @@ -2498,6 +2543,7 @@ function pet() { if (currentState === States.IDLE && birb.getCurrentAnimation() !== Animations.HEART) { + birdsong.chirp(); birb.setAnimation(Animations.HEART); lastPetTimestamp = Date.now(); } diff --git a/dist/web/birb.js b/dist/web/birb.js index 1c0c2f9..f068ab6 100644 --- a/dist/web/birb.js +++ b/dist/web/birb.js @@ -857,6 +857,49 @@ } } + // @ts-check + + class Birdsong { + + /** + * @type {AudioContext} + */ + audioContext; + + chirp() { + if (!this.audioContext) { + this.audioContext = new AudioContext(); + } + + const TIMES = [0, 0.06, 0.16]; + const FREQUENCIES = [2200, + 3500 + Math.random() * 700, + 1600 + Math.random() * 400]; + const VOLUMES = [0.0001, 0.3, 0.0001]; + + const oscillator = this.audioContext.createOscillator(); + oscillator.type = "sine"; + const gain = this.audioContext.createGain(); + oscillator.connect(gain); + gain.connect(this.audioContext.destination); + + const now = this.audioContext.currentTime; + for (let i = 0; i < TIMES.length; i++) { + const time = TIMES[i] + now; + if (i === 0) { + oscillator.frequency.setValueAtTime(FREQUENCIES[i], time); + gain.gain.setValueAtTime(VOLUMES[i], time); + } else { + oscillator.frequency.exponentialRampToValueAtTime(FREQUENCIES[i], time); + gain.gain.exponentialRampToValueAtTime(VOLUMES[i], time); + } + } + + oscillator.start(now); + oscillator.stop(now + TIMES[TIMES.length - 1]); + } + } + const SAVE_KEY = "birbSaveData"; /** @@ -1804,7 +1847,7 @@ insertModal(`${birdBirb()} Mode`, message); }), new Separator(), - new MenuItem("2026.1.1", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.1"); }, false), + new MenuItem("2026.1.4", () => { alert("Thank you for using Pocket Bird! You are on version: 2026.1.4"); }, false), ]; const styleElement = document.createElement("style"); @@ -1818,6 +1861,8 @@ FLYING: "flying", }; + const birdsong = new Birdsong(); + let frozen = false; let stateStart = Date.now(); let currentState = States.IDLE; @@ -2498,6 +2543,7 @@ function pet() { if (currentState === States.IDLE && birb.getCurrentAnimation() !== Animations.HEART) { + birdsong.chirp(); birb.setAnimation(Animations.HEART); lastPetTimestamp = Date.now(); } diff --git a/src/application.js b/src/application.js index c194c03..40263ba 100644 --- a/src/application.js +++ b/src/application.js @@ -2,6 +2,7 @@ import Frame from './frame.js'; import Layer from './layer.js'; import Anim from './anim.js'; import { Birb, Animations } from './birb.js'; +import { Birdsong } from './sound.js'; import { Context, ObsidianContext } from './context.js'; import { @@ -203,6 +204,8 @@ function startApplication(birbPixels, featherPixels) { FLYING: "flying", }; + const birdsong = new Birdsong(); + let frozen = false; let stateStart = Date.now(); let currentState = States.IDLE; @@ -897,6 +900,7 @@ function startApplication(birbPixels, featherPixels) { function pet() { if (currentState === States.IDLE && birb.getCurrentAnimation() !== Animations.HEART) { + birdsong.chirp(); birb.setAnimation(Animations.HEART); lastPetTimestamp = Date.now(); } diff --git a/src/sound.js b/src/sound.js new file mode 100644 index 0000000..834043b --- /dev/null +++ b/src/sound.js @@ -0,0 +1,42 @@ +// @ts-check + +export class Birdsong { + + /** + * @type {AudioContext} + */ + audioContext; + + chirp() { + if (!this.audioContext) { + this.audioContext = new AudioContext(); + } + + const TIMES = [0, 0.06, 0.16]; + const FREQUENCIES = [2200, + 3500 + Math.random() * 700, + 1600 + Math.random() * 400]; + const VOLUMES = [0.0001, 0.3, 0.0001]; + + const oscillator = this.audioContext.createOscillator(); + oscillator.type = "sine"; + const gain = this.audioContext.createGain(); + oscillator.connect(gain); + gain.connect(this.audioContext.destination); + + const now = this.audioContext.currentTime; + for (let i = 0; i < TIMES.length; i++) { + const time = TIMES[i] + now; + if (i === 0) { + oscillator.frequency.setValueAtTime(FREQUENCIES[i], time); + gain.gain.setValueAtTime(VOLUMES[i], time); + } else { + oscillator.frequency.exponentialRampToValueAtTime(FREQUENCIES[i], time); + gain.gain.exponentialRampToValueAtTime(VOLUMES[i], time); + } + } + + oscillator.start(now); + oscillator.stop(now + TIMES[TIMES.length - 1]); + } +} \ No newline at end of file