Skip to content

Commit

Permalink
Add demo
Browse files Browse the repository at this point in the history
  • Loading branch information
bkeepers committed Mar 24, 2024
1 parent 5f73e86 commit 4c4b8e2
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Deploy Demo

on:
push:
branches: ['main']

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: 'pages'
cancel-in-progress: true

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build -- --base=/tuner/
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './demo'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# @chordbook/tuner

A web-based library for pitch detection of stringed instruments. It uses the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) to capture audio from the microphone, and uses the [pitchy](https://github.com/ianprime0509/pitchy) to detect the predominant pitch.

<h3 align="center">🎸 <a href="https://chordbook.github.io/tuner">View Demo</a> 🪕</h3>

### 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.
51 changes: 51 additions & 0 deletions demo.ts
Original file line number Diff line number Diff line change
@@ -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();
92 changes: 92 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@chordbook/tuner</title>
<style>
body {
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

main {
display: flex;
flex-direction: column;
height: 100vh;
}

header {
color: white;
background: darkslateblue;
padding: 1rem 2rem;
display: flex;
}

header a {
color: inherit;
}

h1 {
font-size: 1.5rem;
margin: 0;
}

#note {
padding: 1rem;
background-color: #eee;
color: #666;
border: 1px solid #ccc;
border-radius: 0.25rem;
height: 200px;
}

#tuner {
flex: 1;
position: relative;
color: white;

display: flex;
align-items: center;
justify-content: center;
}

#visualizer {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: 100%;
height: 100%;
}

button {
font-size: 1rem;
}
</style>
</head>
<body>
<main>
<header>
<h1><a href="https://github.com/chordbook/tuner">@chordbook/tuner</a></h1>
<a href="https://github.com/chordpro/tuner" style="margin-left: auto;">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
</svg>
</a>
</header>
<div id="tuner">
<canvas id="visualizer"></canvas>
<div style="position: relative; width: 400px;">
<div style="text-align: center">
<button id="start">Start</button>
<button id="stop">Stop</button>
</div>
<pre id="note" style="height: 200px;">Press start to begin tuning</pre>
</div>
</div>
</main>
<script type="module" src="./demo.ts"></script>
</body>
</html>

0 comments on commit 4c4b8e2

Please sign in to comment.