-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |