{"id":17134338,"url":"https://github.com/brianium/blah","last_synced_at":"2025-09-21T04:31:25.148Z","repository":{"id":146514763,"uuid":"460910276","full_name":"brianium/blah","owner":"brianium","description":"A library for working with microphones in ClojureScript","archived":false,"fork":false,"pushed_at":"2023-04-11T11:20:11.000Z","size":1409,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-07T17:15:02.446Z","etag":null,"topics":["audio-processing","clojurescript","microphone"],"latest_commit_sha":null,"homepage":"https://brianium.github.io/blah/","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brianium.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-18T15:43:39.000Z","updated_at":"2024-04-29T09:08:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"b4c2649c-1163-489b-a7da-ad6e9708f3a5","html_url":"https://github.com/brianium/blah","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fblah","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fblah/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fblah/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fblah/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brianium","download_url":"https://codeload.github.com/brianium/blah/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233550328,"owners_count":18692829,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio-processing","clojurescript","microphone"],"created_at":"2024-10-14T19:44:39.555Z","updated_at":"2025-09-21T04:31:19.849Z","avatar_url":"https://github.com/brianium.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"mic.gif\"\u003e\n  \u003cbr /\u003e\n  \u003cimg src=\"logo-text.png\" alt=\"Blah\"\u003e\n\u003c/p\u003e\n\n\nBlah lets you stream babbling itself!\n\nA ClojureScript library for reading audio input (i.e microphone input) as a stream of data. Record it! Transform it! Send it\nsome place for transcription! The possibilities are endless!\n\n[![cljdoc badge](https://cljdoc.org/badge/com.github.brianium/blah)](https://cljdoc.org/d/com.github.brianium/blah/CURRENT) [![Clojars Project](https://img.shields.io/clojars/v/com.github.brianium/blah.svg)](https://clojars.org/com.github.brianium/blah)\n\n## Table of contents\n\n- [Usage](#usage)\n  - [Inputs](#inputs)\n  - [Transducers](#transducers)\n  - [Caveats](#caveats)\n- [Data](#data)\n- [Development](#development)\n- [Advanced Compilation](#advanced-compilation)\n\n### Usage\n\nBlah is built on top of [core.async](https://github.com/clojure/core.async). It produces a channel that yields audio data as a stream of Float32 samples.\n\n```clojure\n(require '[blah.core :as blah])\n\n;;; Start a session using the default input\n(let [session (blah/listen)]\n  (go-loop []\n    (let [audio-data (\u003c! session)]\n      (when audio-data\n        (process-audio-data audio-data)\n        (recur)))))\n```\n\n#### Inputs\n\nIf a specific input is desired, they can be obtained by creating an input channel or querying via `query-inputs`\n\n```clojure\n(require '[blah.core :as blah])\n\n(blah/query-inputs\n  (fn [inputs]\n    (let [session (blah/listen (first inputs))]\n      (go-loop []\n        (let [audio-data (\u003c! session)]\n          (when audio-data\n            (process-audio-data audio-data)\n            (recur)))))))\n```\n\nAn input channel can be useful for keeping tabs on audio inputs as they become available/unavailable\n\n```clojure\n(let [input-ch (blah/input-ch)\n      ui-state (atom)]\n  (go-loop []\n    (let [audio-inputs (a/\u003c! input-ch)]\n      (when audio-inputs\n        (swap! ui-state assoc :inputs audio-inputs)\n        (recur)))))\n```\n\n#### Transducers\n\nA transducer can be supplied, and this works exactly as it does with a core.async channel:\n\n```clojure\n;;; We give nil as the input to use the default audio input\n(let [session (blah/listen nil blah.transforms/float32)]\n  (go-loop []å\n    ,,,))\n```\n\nIf a transducer is omitted it will use `blah.transforms/float32`.\n\n#### Caveats\n\nMany browsers require a user action to initiate audio contexts and enumerate devices. This means a robust application of blah will start a session as part of a click handler or some other user interaction. See [dev/cljs/user.cljs](dev/cljs/user.cljs) for an example of developing with blah.\n\n### Data\n\nThe magic behind blah is that data is streamed in realtime via an [AudioWorkletProcessor](https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletProcessor).\n\nThis processor sends data to blah as JS types, and transforms are intended to make this data more accessible to a Clojure developer.\n\nThe implementation of the processor looks like the following:\n\n```javascript\nclass WorkletProcessor extends AudioWorkletProcessor {\n  process (inputs, outputs, params) {\n    const input = inputs[0];\n\n    let frames = [];\n    for (let ch = 0; ch \u003c input.length; ch++) {\n        const samples = input[ch];\n        frames[ch] = samples;\n    }\n\n    this.port.postMessage(frames);\n    \n    return true;\n  }\n}\n```\n\nThis sends data as a JS array of channel samples, such that `data[0]` would be an array of audio samples for the first channel of the input, and `data[1]`\nwould be an array of audio samples for the second channel, and so on, and so on. All samples are Float32 types from JavaScript.\n\nTransforms work with this data structure.\n\nSee [src/blah/transforms.cljs](src/blah/transforms.cljs) for an example of transforming audio data.\n\n### Development\n\nBlah is developed using a vanilla ClojureScript repl. This plays well with development environments like [Calva](https://calva.io/), or a plain repl.\n\nSee the [ClojureScript quick start](https://clojurescript.org/guides/quick-start) for info on running a repl. \n\nSee [dev/cljs/user.cljs](dev/cljs/user.cljs). This is where development takes place.\n\n### Advanced Compilation\n\nBlah is developed using ClojureScript 1.11.4+\n\nVersions prior to this one should work fine, but they may have issues with advanced compilation. The reason\nfor this is that externs for the native `AudioContext` and `audioWorklet` were not fully in place.\n\nAs of 1.11.4, advanced compilation is no problem.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianium%2Fblah","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrianium%2Fblah","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianium%2Fblah/lists"}