note-listener 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +116 -0
  2. package/package.json +1 -1
  3. package/src/index.js +10 -3
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # note-listener
2
+
3
+ A lightweight JavaScript/TypeScript library for detecting pitch from any audio input. Works in the browser and supports selecting specific microphones or sound cards. Perfect for guitar practice, music apps, or live pitch detection.
4
+
5
+ ---
6
+
7
+ ## Features
8
+
9
+ * Detect musical notes (C0–B8) in real-time.
10
+ * Smooths out note jitter with a configurable buffer.
11
+ * Supports multiple audio inputs and allows choosing a specific device.
12
+ * Pure browser-based — no backend required.
13
+ * Minimal dependencies (`pitchy`).
14
+
15
+ ---
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install note-listener
21
+ # or
22
+ yarn add note-listener
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Plain JavaScript
28
+
29
+ ```js
30
+ import { listAudioInputs, createPitchListener } from "note-listener";
31
+
32
+ // List all available audio inputs
33
+ const inputs = await listAudioInputs();
34
+ console.log(inputs); // [{ deviceId, label }, ...]
35
+
36
+ const listener = await createPitchListener({
37
+ deviceId: inputs[0].deviceId, // select a device
38
+ minEnergy: 0.03,
39
+ onNote: (note, freq, clarity) => {
40
+ console.log(`Detected note: ${note} (${freq.toFixed(2)} Hz), clarity: ${clarity.toFixed(2)}`);
41
+ },
42
+ });
43
+
44
+ listener.start();
45
+
46
+ // Stop when needed
47
+ // listener.stop();
48
+ ```
49
+
50
+ ### Vue 3 Example
51
+
52
+ ```vue
53
+ <template>
54
+ <div>
55
+ <h2>Select audio input</h2>
56
+ <select v-model="selectedId">
57
+ <option disabled value="">-- choose input --</option>
58
+ <option v-for="d in inputs" :key="d.deviceId" :value="d.deviceId">
59
+ {{ d.label }}
60
+ </option>
61
+ </select>
62
+
63
+ <button @click="start" :disabled="!selectedId">Start listening</button>
64
+ <p>Last note: {{ lastNote || "—" }}</p>
65
+ </div>
66
+ </template>
67
+
68
+ <script setup>
69
+ import { ref, onMounted, onBeforeUnmount } from "vue"
70
+ import { listAudioInputs, createPitchListener } from "note-listener"
71
+
72
+ const inputs = ref([])
73
+ const selectedId = ref("")
74
+ const lastNote = ref(null)
75
+ let listener = null
76
+
77
+ onMounted(async () => {
78
+ await navigator.mediaDevices.getUserMedia({ audio: true })
79
+ inputs.value = await listAudioInputs()
80
+ })
81
+
82
+ async function start() {
83
+ if (listener) listener.stop()
84
+
85
+ listener = await createPitchListener({
86
+ deviceId: selectedId.value,
87
+ minEnergy: 0.03,
88
+ onNote(note) {
89
+ lastNote.value = note
90
+ console.log("🎵", note)
91
+ },
92
+ })
93
+
94
+ listener.start()
95
+ }
96
+
97
+ onBeforeUnmount(() => {
98
+ if (listener) listener.stop()
99
+ })
100
+ </script>
101
+ ```
102
+
103
+ ## Options
104
+
105
+ | Option | Type | Default | Description |
106
+ | ---------------- | -------- | --------- | ------------------------------------------------------------------- |
107
+ | deviceId | string | undefined | Audio input device ID to use. |
108
+ | minEnergy | number | 0.03 | Minimum RMS energy to consider a signal as valid. |
109
+ | clarityThreshold | number | 0.88 | Minimum clarity from pitchy to register a note. |
110
+ | minFreq | number | 82 | Minimum frequency to detect (Hz). |
111
+ | maxFreq | number | 1319 | Maximum frequency to detect (Hz). |
112
+ | onNote | function | required | Callback when a new note is detected: `(note, freq, clarity) => {}` |
113
+
114
+ ## License
115
+
116
+ MIT © Donald Edwin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "note-listener",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "simple pitch detection library for instruments using pitchy",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import { PitchDetector } from "pitchy";
2
2
 
3
- /* ====== Utilities (unchanged) ====== */
3
+ export async function listAudioInputs() {
4
+ const devices = await navigator.mediaDevices.enumerateDevices();
5
+ return devices.filter((d) => d.kind === "audioinput");
6
+ }
7
+
4
8
 
9
+ /* ====== Utilities ====== */
5
10
  function freqToNote(freq) {
6
11
  const A4 = 440;
7
12
  const notes = [
@@ -34,9 +39,10 @@ function rms(buffer) {
34
39
  return Math.sqrt(sum / buffer.length);
35
40
  }
36
41
 
37
- function getGuitar() {
42
+ function getGuitar(deviceId) {
38
43
  return navigator.mediaDevices.getUserMedia({
39
44
  audio: {
45
+ ...(deviceId ? { deviceId: { exact: deviceId } } : {}),
40
46
  echoCancellation: false,
41
47
  autoGainControl: false,
42
48
  noiseSuppression: false,
@@ -49,6 +55,7 @@ function getGuitar() {
49
55
 
50
56
  export async function createPitchListener({
51
57
  onNote,
58
+ deviceId,
52
59
  minEnergy = 0.03,
53
60
  clarityThreshold = 0.88,
54
61
  minFreq = 82,
@@ -64,7 +71,7 @@ export async function createPitchListener({
64
71
  const context = new AudioContext();
65
72
  let guitar;
66
73
  try {
67
- guitar = await getGuitar();
74
+ guitar = await getGuitar(deviceId);
68
75
  } catch (e) {
69
76
  console.warn("Microphone access not granted:", e.message);
70
77
  return {