+
+### Features
+
+* ✅ Uses your devices microphone to detect the pitch of stringed instruments
+* ✅ Filters noise from low and high frequencies outside the range of stringed instruments
+* ✅ Does not try to detect when the volume is too low
+
+## Installation
+
+```console
+npm install @chordbook/tuner
+```
+
+## Usage
+
+```js
+import { createTuner } from '@chordbook/tuner'
+
+const tuner = createTuner({
+ // The callback to call when a note is detected.
+ onNote: note => {
+ console.log('Note:', note)
+ },
+
+ //// Here are some other settings you can fiddle with
+ //// (let us know if you find values that work better).
+
+ //// The frequency of middle A. Defaults to 440Hz.
+ // a4: 440,
+
+ //// The minimum clarity threshold. Anything below this will be ignored
+ // clarityThreshold: 0.95,
+
+ // The minimum volume threshold. -100 means 1/100th the volume of the loudest sound.
+ // minVolumeDecibels: -100,
+
+ //// The minimum and maximum frequencies to detect. To reduce noise, everything else is filtered
+ //// out using a lowpass and highpass filter.
+ // minFrequency: 73.42, // D2, drop D
+ // maxFrequency: 1084.0, // C6, highest note on the guitar in front of me
+
+ // bufferSize: 2048,
+ // smoothingTimeConstant: 0.9,
+})
+
+// Request access to the microphone and begin pitch detection
+tuner.start()
+
+// Stop listening
+tuner.stop()
+```
+
+## Contributing
+
+Contributions are welcome!
+
+1. Clone this repository: `git clone https://github.com/chordbook/tuner.git`
+2. Install dependencies: `npm install`
+3. Start the development server: `npm run dev`
+4. Open [http://localhost:5173/](http://localhost:5173/) in your browser
+
+## License
+
+This project is licensed under the [GPLv3.0](./LICENSE) license.
diff --git a/demo.ts b/demo.ts
new file mode 100644
index 0000000..8bfb46b
--- /dev/null
+++ b/demo.ts
@@ -0,0 +1,51 @@
+import { createTuner, Note } from "./src/"
+
+const canvasEl = document.getElementById("visualizer")! as HTMLCanvasElement;
+const noteEl = document.getElementById("note")! as HTMLDivElement;
+
+const tuner = createTuner({
+ onNote(note: Note) {
+ noteEl.innerText = JSON.stringify(note, null, 2);
+ }
+})
+
+document.getElementById("start")?.addEventListener("click", () => tuner.start())
+document.getElementById("stop")?.addEventListener("click", () => tuner.stop())
+
+const bufferLength = tuner.analyser.frequencyBinCount;
+const dataArray = new Uint8Array(bufferLength);
+// Just get the low end of the spectrum
+const displayLength = Math.sqrt(bufferLength) * 2;
+
+const ctx = canvasEl.getContext("2d")!;
+canvasEl.width = canvasEl.offsetWidth
+canvasEl.height = canvasEl.offsetHeight
+
+function visualize() {
+ tuner.analyser.getByteFrequencyData(dataArray);
+
+ ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
+
+ const barWidth = (canvasEl.width / displayLength);
+ let x = 0;
+
+ for (let i = 0; i < displayLength; i++) {
+ const barHeight = canvasEl.height * (dataArray[i] / 255);
+
+ ctx.fillStyle = `rgb(60 20 ${barHeight + 50})`;
+ ctx.fillRect(x, canvasEl.height - barHeight, barWidth, barHeight);
+
+ x += barWidth;
+ }
+ requestAnimationFrame(visualize);
+}
+
+visualize();
+
+
+// const oscillator = tuner.context.createOscillator();
+
+// oscillator.type = "square";
+// oscillator.frequency.setValueAtTime(440, tuner.context.currentTime); // value in hertz
+// oscillator.connect(tuner.context.destination);
+// oscillator.start();
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..56cca6d
--- /dev/null
+++ b/index.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+ @chordbook/tuner
+
+
+
+
+
+