{"id":20311089,"url":"https://github.com/arl/blip","last_synced_at":"2026-05-11T14:34:09.052Z","repository":{"id":262117713,"uuid":"883961356","full_name":"arl/blip","owner":"arl","description":"Blip is a low-level audio Go library","archived":false,"fork":false,"pushed_at":"2025-05-17T10:29:41.000Z","size":2706,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-03T14:38:33.695Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","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":"2024-11-05T22:11:56.000Z","updated_at":"2025-05-17T10:27:58.000Z","dependencies_parsed_at":"2024-11-10T18:15:26.733Z","dependency_job_id":null,"html_url":"https://github.com/arl/blip","commit_stats":null,"previous_names":["arl/blip"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arl/blip","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arl%2Fblip","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arl%2Fblip/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arl%2Fblip/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arl%2Fblip/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arl","download_url":"https://codeload.github.com/arl/blip/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arl%2Fblip/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32899149,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-14T17:36:01.882Z","updated_at":"2026-05-11T14:34:09.040Z","avatar_url":"https://github.com/arl.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white\u0026style=round-square)](https://pkg.go.dev/github.com/arl/blip)\n\n# Blip\n\n\nBlip is a low-level audio Go library to resample audio waveforms from input\nclock rate to output sample rate.\n\nThis is particularly useful for emulating sound hardware, where the input clock\nrate is the clock rate of the hardware and the output sample rate is the sample\nrate of the audio device.\n\nExamples:\n\n| Package                                       | Description                                                           |\n|-----------------------------------------------|-----------------------------------------------------------------------|\n| [demo_basic](./examples/demo_basic/main.go)   | Generates square wave sweep                                           |\n| [demo_stereo](./examples/demo_stereo/main.go) | Generates stereo sound using two blip buffers                         |\n| [demo_fixed](./examples/demo_fixed/main.go)   | Works in fixed-point time rather than clocks                          |\n| [demo_sdl](./examples/demo_sdl/main.go)       | Plays sound live using SDL multimedia library                         |\n| [demo_chip](./examples/demo_chip/main.go)     | Emulates sound hardware and plays back log.txt                        |\n| [wave](./wave/wave.go)                        | Simple package demonstrating a wave sound file write, used by demos   |\n\n\n\nThis library is a **pure-go** port of the C library called **blip_buf** by Shay Green (blargg).  \nHere's the README from the original C library, slightly modified for cosmetic\nreasons, from which blip also kept the original license, which you can find in the LICENSE file.\n\n`blip_buf` (original C library name)\n--------------\nAuthor  : Shay Green \u003cgblargg@gmail.com\u003e  \nWebsite : http://www.slack.net/~ant/  \nLicense : GNU Lesser General Public License (LGPL)  \n\n\nContents\n--------\n* Overview\n* Buffer creation\n* Waveform generation\n* Time frames\n* Complex waveforms\n* Sample buffering\n* Thanks\n\n\nOverview\n--------\nThis library resamples audio waveforms from input clock rate to output\nsample rate.\n\nUsage follows this general pattern:\n\n* Create buffer with ``blip.NewBuffer()``.\n* Set clock rate and sample rate with `buf.SetRates()`.\n* Waveform generation loop:\n\t- Generate several clocks of waveform with `buf.AddDelta()`.\n\t- End time frame with `buf.EndFrame()`.\n\t- Read samples from buffer with `buf.ReadSamples()`.\n\n\nBuffer creation\n---------------\nBefore synthesis, a buffer must be created with `blip.NewBuffer()`. Its\nsize is the maximum number of unread samples it can hold. For most uses,\nthis can be 1/10 the sample rate or less, since samples will usually be\nread out immediately after being generated.\n\nAfter the buffer is created, the input clock rate and output sample rate\nmust be set with `buf.SetRates()`. This determines how many input clocks\nthere are per second, and how many output samples are generated per\nsecond.\n\nIf the compiler supports a 64-bit integer type, then the input-output\nratio is stored very accurately. If the compiler only supports a 32-bit\ninteger type, then the ratio is stored with only 20 fraction bits, so\nsome ratios cannot be represented exactly (for example, _sample\nrate=48000_ and _clock rate=48001_). The ratio is internally rounded up, so\nthere will never be fewer than 'sample rate' samples per second. Having\ntoo many per second is generally better than having too few.\n\n\nWaveform generation\n-------------------\nWaveforms are generated at the input clock rate. Consider a simple\nsquare wave with 8 clocks per cycle (4 clocks high, 4 clocks low):\n\n                   |\u003c-- 8 clocks -\u003e|\n        +5|        ._._._._        ._._._._        ._._._._        ._._\n          |        |       |       |       |       |       |       |\n    Amp  0|._._._._        |       |       |       |       |       |\n          |                |       |       |       |       |       |\n        -5|                ._._._._        ._._._._        ._._._._ \n           * . . . * . . . * . . . * . . . * . . . * . . . * . . . * .\n    Time   0       4       8      12      16      20      24      28\n\nThe wave changes amplitude at time points 0, 4, 8, 12, 16, etc.\n\nThe following generates the amplitude at every clock of above waveform\nat the input clock rate:\n\n```go\n\tvar wave [30]int\n\n\tfor i := 4; i \u003c 30; i++ {\n\t\tif i%8 \u003c 4 {\n\t\t\twave[i] = -5\n\t\t} else {\n\t\t\twave[i] = +5\n\t\t}\n\t}\n```\n\nWithout this library, the wave array would then need to be resampled\nfrom the input clock rate to the output sample rate. This library does\nthis resampling internally, so it won't be discussed further; waveform\ngeneration code can focus entirely on the input clocks.\n\nRather than specify the amplitude at every clock, this library merely\nneeds to know the points where the amplitude **changes**, referred to as a\ndelta. The time of a delta is specified with a _clock count_. The deltas\nfor this square wave are shown below the time points they occur at:\n\n        +5|        ._._._._        ._._._._        ._._._._        ._._\n          |        |       |       |       |       |       |       |\n    Amp  0|._._._._        |       |       |       |       |       |\n          |                |       |       |       |       |       |\n        -5|                ._._._._        ._._._._        ._._._._ \n           * . . . * . . . * . . . * . . . * . . . * . . . * . . . * .\n    Time   0       4       8      12      16      20      24      28\n    Delta         +5     -10     +10     -10     +10     -10     +10\n\nThe following calls generate the above waveform:\n\n```go\n\tbuf.AddDelta(  4,  +5 );\n\tbuf.AddDelta(  8, -10 );\n\tbuf.AddDelta( 12, +10 );\n\tbuf.AddDelta( 16, -10 );\n\tbuf.AddDelta( 20, +10 );\n\tbuf.AddDelta( 24, -10 );\n\tbuf.AddDelta( 28, +10 );\n```\n\nIn the examples above, the amplitudes are small for clarity. The 16-bit\nsample range is -32768 to +32767, so actual waveform amplitudes would\nneed to be in the thousands to be audible (for example, -5000 to +5000).\n\nThis library allows waveform generation code to pay NO attention to the\noutput sample rate. It can focus **entirely** on the essence of the\nwaveform: the points where its amplitude changes. Since these points can\nbe efficiently generated in a loop, synthesis is efficient. Sound chip\nemulation code can be structured to allow full accuracy down to a single\nclock, with the emulated CPU being able to simply tell the sound chip to\n\"emulate from wherever you left off, up to clock time T within the\ncurrent time frame\".\n\n\nTime frames\n-----------\nSince time keeps increasing, if left unchecked, at some point it would\noverflow the range of an integer. This library's solution to the problem\nis to break waveform generation into time frames of moderate length.\nClock counts within a time frame are thus relative to the beginning of\nthe frame, where 0 is the beginning of the frame. When a time frame of\nlength T is ended, what was at time T in the old time frame is now at\ntime 0 in the new time frame. Breaking the above waveform into time\nframes of 10 clocks each looks like this:\n\n        +5|        ._._._._        ._._._._        ._._._._        ._._\n          |        |       |       |       |       |       |       |\n    Amp  0|._._._._        |       |       |       |       |       |\n          |                |       |       |       |       |       |\n        -5|                ._._._._        ._._._._        ._._._._ \n           * . . . * . . . * . . . * . . . * . . . * . . . * . . . * .\n    Time  |0       4       8  |    2       6      |0       4       8  |\n          | first time frame  | second time frame | third time frame  |\n          |\u003c--- 10 clocks ---\u003e|\u003c--- 10 clocks ---\u003e|\u003c--- 10 clocks ---\u003e|\n\nThe following calls generate the above waveform. After they execute, the\nfirst 30 clocks of the waveform will have been resampled and be\navailable as output samples for reading with buf.ReadSamples().\n\n```go\n\tbuf.AddDelta( 4,  +5 );\n\tbuf.AddDelta( 8, -10 );\n\tbuf.EndFrame( 10 );\n\t\n\tbuf.AddDelta( 2, +10 );\n\tbuf.AddDelta( 6, -10 );\n\tbuf.EndFrame( 10 );\n\t\n\tbuf.AddDelta( 0, +10 );\n\tbuf.AddDelta( 4, -10 );\n\tbuf.AddDelta( 8, +10 );\n\tbuf.EndFrame( 10 );\n\t...\n```\n\nTime frames can be a convenient length, and the length can vary from one\nframe to the next. Once a time frame is ended, the resulting output\nsamples become available for reading immediately, and no more deltas can\nbe added to it.\n\nThere is a limit of about 4000 output samples per time frame. The number\nof clocks depends on the clock rate. At common sample rates, this allows\ntime frames of at least 1/15 second, plenty for most uses. This limit\nallows increased resampling ratio accuracy.\n\nIn an emulator, it is usually convenient to have audio time frames\ncorrespond to video frames, where the CPU's clock counter is reset at\nthe beginning of each video frame and thus can be used directly as the\nrelative clock counts for audio time frames.\n\n\nComplex waveforms\n-----------------\nAny sort of waveform can be generated, not just a square wave. For\nexample, a saw-like wave:\n\n        +5|        ._._._._                ._._._._                ._._\n          |        |       |               |       |               |\n    Amp  0|._._._._        |       ._._._._        |       ._._._._\n          |                |       |               |       |\n        -5|                ._._._._                ._._._._\n           * . . . * . . . * . . . * . . . * . . . * . . . * . . . * .\n    Time   0       4       8      12      16      20      24      28\n    Delta         +5     -10      +5      +5     -10      +5      +5\n\nCode to generate above waveform:\n\n```go\n\tbuf.AddDelta(  4,  +5 );\n\tbuf.AddDelta(  8, -10 );\n\tbuf.AddDelta( 12,  +5 );\n\tbuf.AddDelta( 16,  +5 );\n\tbuf.AddDelta( 20, +10 );\n\tbuf.AddDelta( 24,  +5 );\n\tbuf.AddDelta( 28,  +5 );\n```\n\nSimilarly, multiple waveforms can be added within a time frame without\nproblem. It doesn't matter what order they're added, because all the\nlibrary needs are the deltas. The synthesis code doesn't need to know\nall the waveforms at once either; it can calculate and add the deltas\nfor each waveform individually. Deltas don't need to be added in\nchronological order either.\n\n\nSample buffering\n----------------\nSample buffering is very flexible. Once a time frame is ended, the\nresampled waveforms become output samples that are immediately made\navailable for reading with `buf.ReadSamples()`. They don't have to be\nread immediately; they can be allowed to accumulate in the buffer, with\neach time frame appending more samples to the buffer. When reading, some\nor all of the samples in can be read out, with the remaining unread\nsamples staying in the buffer for later. Usually a program will\nimmediately read all available samples after ending a time frame and\nplay them immediately. In some systems, a program needs samples in\nfixed-length blocks; in that case, it would keep generating time frames\nuntil some number of samples are available, then read only that many,\neven if slightly more were available in the buffer.\n\nIn some systems, one wants to run waveform generation for exactly the\nnumber of clocks necessary to generate some desired number of output\nsamples, and no more. In that case, use `buf.ClocksNeeded( N )` to\nfind out how many clocks are needed to generate N additional samples.\nEnding a time frame with this value will result in exactly N more\nsamples becoming available for reading.\n\n\nThanks\n------\nThanks to Jsr (FamiTracker author), the Mednafen team (multi-system\nemulator), ShizZie (Nhes GMB author), Marcel van Tongeren, Luke Molnar\n(UberNES author), Fredrick Meunier (Fuse contributor) for using and\ngiving feedback for another similar library. Thanks to Disch for his\ninterest and discussions about the synthesis algorithm itself, and for\nwriting his own implementation of it (Schpune) rather than just using\nmine. Thanks to Xodnizel for Festalon, whose sound quality got me\ninterested in video game sound emulation in the first place, and where I\nfirst came up with the algorithm while optimizing its brute-force\nfilter.\n\n-- \nShay Green \u003cgblargg@gmail.com\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farl%2Fblip","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farl%2Fblip","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farl%2Fblip/lists"}