Skip to content

Commit

Permalink
Added ability to combine local and mocturnal birds for BirdNET
Browse files Browse the repository at this point in the history
renamed 'migrants' to nocturnal
added a gain node

moved more 'change' events to the document change handler

beginnings of a normaliser node, using a worklet
  • Loading branch information
Mattk70 committed Feb 11, 2024
1 parent 6ea5b23 commit c37de38
Show file tree
Hide file tree
Showing 9 changed files with 807 additions and 733 deletions.
1 change: 0 additions & 1 deletion css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,6 @@ input[type="range"].vertical {
line-height: 20px;
float: right;
text-decoration: none; /* Remove underline from <a> tags */
display: inline-block;
transition: transform 0.2s; /* Add transition for smooth scaling */
}

Expand Down
File renamed without changes
1,107 changes: 562 additions & 545 deletions index.html

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions js/audio_normalizer_processor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// This needs some refinement!

class AudioNormalizerProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{ name: 'peakLoudness', defaultValue: -3 }];
}

process(inputs, outputs, parameters) {
const input = inputs[0];
const output = outputs[0];

const peakLoudness = parameters.peakLoudness[0];

for (let channel = 0; channel < input.length; ++channel) {
const inputData = input[channel];
const outputData = output[channel];

// Increase buffer size
const bufferSize = 1024; // Adjust as needed

for (let sample = 0; sample < inputData.length; sample += bufferSize) {
let maxAbsValue = 0;
const endSample = Math.min(sample + bufferSize, inputData.length);

for (let s = sample; s < endSample; ++s) {
const absValue = Math.abs(inputData[s]);
if (absValue > maxAbsValue) {
maxAbsValue = absValue;
}
}

const desiredPeak = Math.pow(10, peakLoudness / 20); // Convert dB to linear scale
const scaleFactor = desiredPeak / (maxAbsValue + 1e-5); // Handle division by zero

for (let s = sample; s < endSample; ++s) {
outputData[s] = inputData[s] * scaleFactor;
}
}
}

return true;
}
}

registerProcessor('audio-normalizer-processor', AudioNormalizerProcessor);

40 changes: 12 additions & 28 deletions js/listWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ let BACKEND;
//GLOBALS
let listModel;
let NOT_BIRDS;
const MIGRANTS = new Set(["Pluvialis dominica_American Golden Plover", "Acanthis hornemanni_Arctic Redpoll", "Sterna paradisaea_Arctic Tern", "Recurvirostra avosetta_Avocet", "Porzana pusilla_Baillon's Crake", "Limosa lapponica_Bar-tailed Godwit", "Tyto alba_Barn Owl", "Branta leucopsis_Barnacle Goose", "Cygnus columbianus_Bewick's Swan", "Botaurus stellaris_Bittern (call)", "Chroicocephalus ridibundus_Black-headed Gull", "Podiceps nigricollis_Black-necked Grebe", "Limosa limosa_Black-tailed Godwit", "Turdus merula_Blackbird (flight call)", "Sylvia atricapilla_Blackcap (call)", "Fringilla montifringilla_Brambling", "Branta bernicla_Brent Goose", "Branta canadensis_Canada Goose", "Larus cachinnans_Caspian Gull", "Phylloscopus collybita_Chiffchaff (call)", "Loxia curvirostra_Common Crossbill", "Larus canus_Common Gull", "Acanthis flammea_Common Redpoll", "Actitis hypoleucos_Common Sandpiper", "Melanitta nigra_Common Scoter", "Sterna hirundo_Common Tern", "Fulica atra_Coot", "Emberize calandre_Corn Bunting (call)", "Crex crex_Corncrake", "Cuculus canorus_Cuckoo (call)", "Calidris ferruginea_Curlew Sandpiper", "Numenius arquata_Curlew", "Charadrius morinellus_Dotterel", "Calidris alpina_Dunlin", "Prunella modularis_Dunnock (call)", "Alopochen aegyptiaca_Egyptian Goose", "Turdus pilaris_Fieldfare (call)", "Mareca strepera_Gadwall", "Sylvia borin_Garden Warbler (call)", "Spatula querquedula_Garganey", "Regulus regulus_Goldcrest (call)", "Regulus ignicapilla_Firecrest (call)", "Pluvialis apricaria_Golden Plover", "Bucephala clangula_Goldeneye", "Mergus merganser_Goosander", "Locustella naevia_Grasshopper Warbler (call)", "Larus marinus_Great Black-backed Gull", "Podiceps cristatus_Great Crested Grebe", "Tringa ochropus_Green Sandpiper", "Tringa nebularia_Greenshank", "Ardea cinerea_Grey Heron", "Perdix perdix_Grey Partridge", "Phalaropus fulicarius_Grey", "Pluvialis squatarola_Grey Plover", "Motacilla cinerea_Grey Wagtail ", "Anser anser_Greylag Goose", "Delichon urbicum_House Martin", "Coccothraustes coccothraustes_Hawfinch (call)", "Larus argentatus_Herring Gull", "Lymnocryptes minimus_Jack Snipe", "Alcedo atthis_Kingfisher", "Calidris canutus_Knot", "Calcarius lapponicus_Lapland Bunting (call)", "Larus fuscus_Lesser Black-backed Gull", "Acanthis cabaret_Lesser Redpoll ", "Curraca curruca_Lesser Whitethroat (call)", "Linaria cannabina_Linnet", "Ixobrychus minutus_Little Bittern (call)", "Egretta garzetta_Little Egret", "Tachybaptus ruficollis_Little Grebe", "Hydrocoloeus minutus_Little Gull", "Athene noctua_Little Owl", "Charadrius dubius_Little Ringed Plover", "Calidris minuta_Little Stint ", "Sternula albifrons_Little Tern", "Asio otus_Long-eared Owl", "Clangula hyemalis_Long-tailed Duck", "Anas platyrhynchos_Mallard", "Aix galericulata_Mandarin Duck", "Anthus pratensis_Meadow Pipit (call)", "Ichthyaetus melanocephalus_Mediterranean Gull", "Turdus viscivorus_Mistle Thrush (call)", "Gallinula chloropus_Moorhen", "Nycticorax nycticorax_Night Heron", "Luscinia megarhynchos_Nightingale (call)", "Luscinia megarhynchos_Nightingale (song)", "Caprimulgus europaeus_Nightjar (call)", "Anthus hodgsoni_Olive-backed Pipit (call)", "Emberiza hortulana_Ortolan Bunting (call)", "Emberiza pusilla_Little Bunting (call)", "Haematopus ostralegus_Oystercatcher", "Ficedula hypoleuca_Pied Flycatcher (call)", "Motacilla alba_Pied Wagtail", "Anser brachyrhynchus_Pink-footed Goose", "Anas acuta_Pintail", "Aythya ferina_Pochard", "Calidris maritima_Purple Sandpiper", "Coturnix coturnix_Quail (call)", "Coturnix coturnix_Quail (song)", "Mergus serrator_Red-breasted Merganser", "Netta rufina_Red-crested Pochard", "Alectoris rufa_Red-legged Partridge", "Tringa totanus_Redshank", "Phoenicurus phoenicurus_Redstart (call)", "Turdus iliacus_Redwing (call)", "Emberiza schoeniclus_Reed Bunting (call)", "Acrocephalus scirpaceus_Reed Warbler (call)", "Anthus richardi_Richard's Pipit (call)", "Turdus torquatus_Ring Ouzel (call)", "Charadrius hiaticula_Ringed Plover", "Erithacus rubecula_Robin (flight call)", "Anthus petrosus_Rock Pipit", "Sterna dougallii_Roseate Tern", "Calidris pugnax_Ruff", "Riparia riparia_Sand Martin", "Calidris alba_Sanderling", "Thalasseus sandvicensis_Sandwich Tern", "Aythya marila_Scaup", "Loxia scotica_Scottish Crossbill", "Acrocephalus schoenobaenus_Sedge Warbler", "Tadorna tadorna_Shelduck", "Asio flammeus_Short-eared Owl", "Spatula clypeata_Shoveler", "Spinus spinus_Siskin", "Alauda arvensis_Skylark (call)", "Gallinago gallinago_Snipe", "Plectrophenax nivalis_Snow Bunting", "Turdus philomelos_Song Thrush (call)", "Porzana porzana_Spotted Crake", "Muscicapa striata_Spotted Flycatcher", "Tringa erythropus_Spotted Redshank (call)", "Burhinus oedicnemus_Stone-curlew", "Saxicola rubicola_Stonechat", "Hirundo rustica_Swallow", "Apus apus_Swift", "Anser fabalis_Taiga Bean Goose", "Strix aluco_Tawny Owl", "Anas crecca_Teal", "Anthus trivialis_Tree Pipit (call)", "Aythya fuligula_Tufted Duck", "Anser serrirostris_Tundra Bean Goose", "Arenaria interpres_Turnstone", "Anthus spinoletta_Water Pipit", "Rallus aquaticus_Water Rail", "Numenius phaeopus_Whimbrel", "Anser albifrons_White-fronted Goose", "Sylvia communis_Whitethroat (call)", "Cygnus cygnus_Whooper Swan", "Mareca penelope_Wigeon", "Phylloscopus trochilus_Willow Warbler (call)", "Tringa glareola_Wood Sandpiper", "Scolopax rusticola_Woodcock", "Lullula arborea_Woodlark (call)", "Larus michahellis_Yellow-legged Gull", "Motacilla flava_Yellow Wagtail", "Emberiza citrinella_Yellowhammer (call)"]);
const NOCTURNAL = new Set(["Pluvialis dominica_American Golden Plover", "Acanthis hornemanni_Arctic Redpoll", "Sterna paradisaea_Arctic Tern", "Recurvirostra avosetta_Avocet", "Porzana pusilla_Baillon's Crake", "Limosa lapponica_Bar-tailed Godwit", "Tyto alba_Barn Owl", "Branta leucopsis_Barnacle Goose", "Cygnus columbianus_Bewick's Swan", "Botaurus stellaris_Bittern (call)", "Chroicocephalus ridibundus_Black-headed Gull", "Podiceps nigricollis_Black-necked Grebe", "Limosa limosa_Black-tailed Godwit", "Turdus merula_Blackbird (flight call)", "Sylvia atricapilla_Blackcap (call)", "Fringilla montifringilla_Brambling", "Branta bernicla_Brent Goose", "Branta canadensis_Canada Goose", "Larus cachinnans_Caspian Gull", "Phylloscopus collybita_Chiffchaff (call)", "Loxia curvirostra_Common Crossbill", "Larus canus_Common Gull", "Acanthis flammea_Common Redpoll", "Actitis hypoleucos_Common Sandpiper", "Melanitta nigra_Common Scoter", "Sterna hirundo_Common Tern", "Fulica atra_Coot", "Emberize calandre_Corn Bunting (call)", "Crex crex_Corncrake", "Cuculus canorus_Cuckoo (call)", "Calidris ferruginea_Curlew Sandpiper", "Numenius arquata_Curlew", "Charadrius morinellus_Dotterel", "Calidris alpina_Dunlin", "Prunella modularis_Dunnock (call)", "Alopochen aegyptiaca_Egyptian Goose", "Turdus pilaris_Fieldfare (call)", "Mareca strepera_Gadwall", "Sylvia borin_Garden Warbler (call)", "Spatula querquedula_Garganey", "Regulus regulus_Goldcrest (call)", "Regulus ignicapilla_Firecrest (call)", "Pluvialis apricaria_Golden Plover", "Bucephala clangula_Goldeneye", "Mergus merganser_Goosander", "Locustella naevia_Grasshopper Warbler (call)", "Larus marinus_Great Black-backed Gull", "Podiceps cristatus_Great Crested Grebe", "Tringa ochropus_Green Sandpiper", "Tringa nebularia_Greenshank", "Ardea cinerea_Grey Heron", "Perdix perdix_Grey Partridge", "Phalaropus fulicarius_Grey", "Pluvialis squatarola_Grey Plover", "Motacilla cinerea_Grey Wagtail ", "Anser anser_Greylag Goose", "Delichon urbicum_House Martin", "Coccothraustes coccothraustes_Hawfinch (call)", "Larus argentatus_Herring Gull", "Lymnocryptes minimus_Jack Snipe", "Alcedo atthis_Kingfisher", "Calidris canutus_Knot", "Calcarius lapponicus_Lapland Bunting (call)", "Larus fuscus_Lesser Black-backed Gull", "Acanthis cabaret_Lesser Redpoll ", "Curraca curruca_Lesser Whitethroat (call)", "Linaria cannabina_Linnet", "Ixobrychus minutus_Little Bittern (call)", "Egretta garzetta_Little Egret", "Tachybaptus ruficollis_Little Grebe", "Hydrocoloeus minutus_Little Gull", "Athene noctua_Little Owl", "Charadrius dubius_Little Ringed Plover", "Calidris minuta_Little Stint ", "Sternula albifrons_Little Tern", "Asio otus_Long-eared Owl", "Clangula hyemalis_Long-tailed Duck", "Anas platyrhynchos_Mallard", "Aix galericulata_Mandarin Duck", "Anthus pratensis_Meadow Pipit (call)", "Ichthyaetus melanocephalus_Mediterranean Gull", "Turdus viscivorus_Mistle Thrush (call)", "Gallinula chloropus_Moorhen", "Nycticorax nycticorax_Night Heron", "Luscinia megarhynchos_Nightingale (call)", "Luscinia megarhynchos_Nightingale (song)", "Caprimulgus europaeus_Nightjar (call)", "Anthus hodgsoni_Olive-backed Pipit (call)", "Emberiza hortulana_Ortolan Bunting (call)", "Emberiza pusilla_Little Bunting (call)", "Haematopus ostralegus_Oystercatcher", "Ficedula hypoleuca_Pied Flycatcher (call)", "Motacilla alba_Pied Wagtail", "Anser brachyrhynchus_Pink-footed Goose", "Anas acuta_Pintail", "Aythya ferina_Pochard", "Calidris maritima_Purple Sandpiper", "Coturnix coturnix_Quail (call)", "Coturnix coturnix_Quail (song)", "Mergus serrator_Red-breasted Merganser", "Netta rufina_Red-crested Pochard", "Alectoris rufa_Red-legged Partridge", "Tringa totanus_Redshank", "Phoenicurus phoenicurus_Redstart (call)", "Turdus iliacus_Redwing (call)", "Emberiza schoeniclus_Reed Bunting (call)", "Acrocephalus scirpaceus_Reed Warbler (call)", "Anthus richardi_Richard's Pipit (call)", "Turdus torquatus_Ring Ouzel (call)", "Charadrius hiaticula_Ringed Plover", "Erithacus rubecula_Robin (flight call)", "Anthus petrosus_Rock Pipit", "Sterna dougallii_Roseate Tern", "Calidris pugnax_Ruff", "Riparia riparia_Sand Martin", "Calidris alba_Sanderling", "Thalasseus sandvicensis_Sandwich Tern", "Aythya marila_Scaup", "Loxia scotica_Scottish Crossbill", "Acrocephalus schoenobaenus_Sedge Warbler", "Tadorna tadorna_Shelduck", "Asio flammeus_Short-eared Owl", "Spatula clypeata_Shoveler", "Spinus spinus_Siskin", "Alauda arvensis_Skylark (call)", "Gallinago gallinago_Snipe", "Plectrophenax nivalis_Snow Bunting", "Turdus philomelos_Song Thrush (call)", "Porzana porzana_Spotted Crake", "Muscicapa striata_Spotted Flycatcher", "Tringa erythropus_Spotted Redshank (call)", "Burhinus oedicnemus_Stone-curlew", "Saxicola rubicola_Stonechat", "Hirundo rustica_Swallow", "Apus apus_Swift", "Anser fabalis_Taiga Bean Goose", "Strix aluco_Tawny Owl", "Anas crecca_Teal", "Anthus trivialis_Tree Pipit (call)", "Aythya fuligula_Tufted Duck", "Anser serrirostris_Tundra Bean Goose", "Arenaria interpres_Turnstone", "Anthus spinoletta_Water Pipit", "Rallus aquaticus_Water Rail", "Numenius phaeopus_Whimbrel", "Anser albifrons_White-fronted Goose", "Sylvia communis_Whitethroat (call)", "Cygnus cygnus_Whooper Swan", "Mareca penelope_Wigeon", "Phylloscopus trochilus_Willow Warbler (call)", "Tringa glareola_Wood Sandpiper", "Scolopax rusticola_Woodcock", "Lullula arborea_Woodlark (call)", "Larus michahellis_Yellow-legged Gull", "Motacilla flava_Yellow Wagtail", "Emberiza citrinella_Yellowhammer (call)"]);
const CHIRPITY_NOT_BIRDS = ['Ambient Noise_Ambient Noise', 'Animal_Animal', 'Cat_Cat', 'Church Bells_Church Bells', 'Cough_Cough', 'Dog_Dog', 'Human_Human', 'Laugh_Laugh', 'No call_No call', 'Rain_Rain', 'Red Fox_Red Fox', 'Sneeze_Sneeze', 'Snoring_Snoring', 'Thunder_Thunder', 'Vehicle_Vehicle', 'Water Drops_Water Drops', 'Waves_Waves', 'Wind_Wind'];
const BIRDNET_NOT_BIRDS = [
'Dog_Dog',
Expand Down Expand Up @@ -121,8 +121,9 @@ onmessage = async (e) => {
let lon = parseFloat(e.data.lon);
let week = parseInt(e.data.week);
let threshold = parseFloat(e.data.threshold);
let localBirdsOnly = e.data.localBirdsOnly;
DEBUG && console.log(`Setting list to ${listType}`);
const includedIDs = await listModel.setList({lat, lon, week, listType, useWeek, threshold});
const includedIDs = await listModel.setList({lat, lon, week, listType, useWeek, threshold, localBirdsOnly});
postMessage({
message: "your-list-sir",
result: includedIDs,
Expand Down Expand Up @@ -154,7 +155,7 @@ class Model {
}
}

async setList({lat, lon, week, listType, useWeek, threshold}) {
async setList({lat, lon, week, listType, useWeek, threshold, localBirdsOnly}) {
let includedIDs = [];
week = useWeek ? week : -1;
if (listType === "everything") {
Expand Down Expand Up @@ -203,43 +204,26 @@ class Model {
DEBUG && console.log('Total species considered at this location: ', count)
// return an object
includedIDs = {week: week, lat: lat, lon:lon, included: includedIDs}
} else if (listType === 'migrants') {
} else if (listType === 'nocturnal') {
if (this.model === 'chirpity') {
for (let i = 0; i < this.labels.length; i++) {
const item = this.labels[i];
if (MIGRANTS.has(item) || MYSTERIES.includes(item)) includedIDs.push(i);
if (NOCTURNAL.has(item)) includedIDs.push(i);
}
} else {
// BirdNET nocturnal bird filter
const additionalIDs = [];
// Get list of IDs of birds that call through the might or all the time. Exclude non-avian classes
for (let i = 0; i < this.labels.length; i++) {
const item = this.labels[i];
if (ACTIVITY_INDEX[item] !== 1 && BIRDNET_NOT_BIRDS.indexOf(item) < 0) includedIDs.push(i);
}
this.mdata_input = tf.tensor([lat, lon, week]).expandDims(0);
const mdata_prediction = this.metadata_model.predict(this.mdata_input);
const mdata_probs = await mdata_prediction.data();
for (let i = 0; i < mdata_probs.length; i++) {
const index = i; // mdata_probs.indexOf(mdata_probs_sorted[i]);
if (mdata_probs[index] < threshold) {
DEBUG && console.log('Excluding:', this.mdata_labels[index] + ': ' + mdata_probs[index]);
} else {
const latin = this.mdata_labels[index].split('_')[0];
// Use the reduce() method to accumulate the indices of species containing the latin name
const foundIndices = this.labels.reduce((indices, element, index) => {
element.includes(latin) && indices.push(index);
return indices;
}, []);
foundIndices.forEach(index => {
// If we want an override list...=>
//if (! ['Dotterel', 'Stone-curlew', 'Spotted Crake'].some(this.labels[index])) BLOCKED_IDS.push(index)
additionalIDs.push(index)
DEBUG && console.log('Including: ', index, 'name', this.labels[index], 'probability', mdata_probs[i].toFixed(3) )
})
}
if (localBirdsOnly){ // placeholder for condition
// Now get list of local birds
const local_ids = await this.setList({lat,lon,week, listType:'location', useWeek, threshold})
// Create a list of indices that appear in both lists
includedIDs = includedIDs.filter(id => local_ids.included.includes(id));
}
includedIDs = includedIDs.filter(id => additionalIDs.includes(id));

}
} else {

Expand Down
5 changes: 3 additions & 2 deletions js/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class State {
this.filteredOffset = {}, // Current species start number for filtered results
this.selection = false,
this.blocked = [],
this.audio = { format: 'mp3', bitrate: 128, padding: false, fade: false, downmix: false, quality: 5 },
this.audio = { gain: 0, format: 'mp3', bitrate: 128, padding: false, fade: false, downmix: false, quality: 5 },
this.filters = { active: false, highPassFrequency: 0, lowShelfFrequency: 0, lowShelfAttenuation: 0, SNR: 0 },
this.detect = { nocmig: false, contextAware: false, confidence: 450 },
this.chart = { range: { start: undefined, end: undefined }, species: undefined },
Expand All @@ -29,7 +29,8 @@ export class State {
this.speciesThreshold = undefined,
this.useWeek = false,
this.week = -1,
this.list = 'everything'
this.list = 'everything',
this.local = true
}


Expand Down
Loading

0 comments on commit c37de38

Please sign in to comment.