diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..bd55037 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..2dc6b81 --- /dev/null +++ b/README.md @@ -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. + +

🎸 View Demo 🪕

+ +### 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 + + + +
+
+

@chordbook/tuner

+ + + + + +
+
+ +
+
+ + +
+
Press start to begin tuning
+
+
+
+ + +