Repository: fulldecent/system-bus-radio
Branch: main
Commit: 68862bb799c5
Files: 31
Total size: 66.1 KB
Directory structure:
gitextract_qt828ii7/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── RTL-SDR-GUIDE.md
├── TEST-DATA.tsv
├── docs/
│ ├── .gitignore
│ ├── Gemfile
│ ├── airgap.js
│ ├── index.html
│ ├── main.css
│ └── worker.js
├── implementations/
│ ├── c-_mm_stream_si128/
│ │ ├── Makefile
│ │ └── main.c
│ ├── c-apple-silicon/
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── main.c
│ ├── c-apple-silicon-wav/
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── gen_sweep.c
│ │ └── main.c
│ ├── c-neon-threads/
│ │ ├── Makefile
│ │ └── main.c
│ └── cpp-counter-threads/
│ ├── Makefile
│ └── main.cpp
├── tests/
│ └── check-test-data-tabs.sh
└── tunes/
├── README.md
├── mary_had_a_little_lamb.tune
├── morse_code_sos.tune
└── smb.tune
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [fulldecent]
custom: ["https://www.paypal.me/fulldecent", "https://amazon.com/hz/wishlist/ls/EE78A23EEGQB"]
================================================
FILE: .github/workflows/tests.yml
================================================
name: Check Lines Script
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
run-check-lines:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run check-lines.sh script
run: ./tests/check-test-data-tabs.sh
================================================
FILE: .gitignore
================================================
# Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
/main
main
gmain
cmain
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 William Entriken
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# System Bus Radio
This program transmits radio on computers / phones without radio transmitting hardware.
## Thank you to our sponsors
<img src="https://radiostay.com/images/logo.svg" width=200>
Listen to online radio - <https://radiostay.com/>
## Why?
Some computers are intentionally disconnected from the rest of the world. This includes having their internet, wireless, bluetooth, USB, external file storage and audio capabilities removed. This is called "air gapping". Even in such a situation, this program can transmit radio.
Publicly available documents already discuss exfiltration from secured systems using various electromagnetic radiations. This is documented in the TEMPEST guidelines published by the US National Security Agency and the US Department of Defense. This project simply adds to that discussion.
## How to use it
**NEW:** Try it in your browser, click here: <http://fulldecent.github.io/system-bus-radio/>
Enter the implementations folder, select any of them and compile using `make`.
```sh
make
```
Run this using a 2015 model MacBook Air. Then use a Sony STR-K670P radio receiver with the included antenna and tune it to 1580 kHz on AM.
Run it and reference the a tune file or make your own!
```sh
./main ../../tunes/mary_had_a_little_lamb.tune
```
You should hear the "Mary Had a Little Lamb" tune playing repeatedly. Other equipment and tuning may work as well. On the equipment above, the author has achieved clear transmission over two meters of open air or one meter through drywall. Different results will be achievable with different equipment.
Are you using an antenna? At the beginning, the author placed the antenna directly on top of the number 4 key and that worked best (on any AM frequency). It was a round antenna. Then once they knew it worked they moved the antenna back. Moving it back reduced the number of frequencies that it worked on, and eventually only that one (1580 kHz) worked. Different hardware will certainly have different frequency response. Here are some results that have been sent in by readers. Please mail <github.com@phor.net> with your results (including makes and models of all equipment involved) or [edit this file directly](https://github.com/fulldecent/system-bus-radio/edit/master/TEST-DATA.tsv) and create a pull request.
**WANTED:** Please post your test results using Raspberry Pi and other embedded systems. This may be particularly good targets because of less shielding/hardening of their hardware.
**NEW:** See our [basic RTL SDR guide] to receive system bus signals using another computer with RTL SDR hardware.
## Technical explanation
This program runs instructions on the computer that cause electromagnetic radiation. The emissions are of a broad frequency range. To be accepted by the radio, those frequencies must:
* Be emitted by the computer processor and other subsystems
* Escape the computer shielding
* Pass through the air or other obstructions
* Be accepted by the antenna
* Be selected by the receiver
By trial and error, the above frequency was found to be ideal for that equipment.
The actual emissions are caused by the `_mm_stream_si128` instruction that writes through to a memory address. Inspiration for using this instruction was provided in:
> Guri, M., Kachlon, A., Hasson, O., Kedma, G., Mirsky, Y. and Elovici, Y., 2015. GSMem: data exfiltration from air-gapped computers over GSM frequencies. In 24th USENIX Security Symposium (USENIX Security 15) (pp. 849-864).
>
> <https://www.usenix.org/node/190937>
Please note that replacing `_mm_stream_si128` with a simple `x++;` will work too. The author's experience has been that `_mm_stream_si128` produces a stronger signal. There may be other ideas that work even better, and it would be nice to improve this method to be more portable (not require SSE extensions).
The program uses square wave modulation, which is depicted below:
```
|<--------------------TIME-------------------->|
| |
|‾|_|‾|_|‾|_____________|‾|_|‾|_|‾|_____________
| | | |
|<------SIGNAL--------->| | |
| |
|<->| CARRIER
```
Notes on high precision time APIs:
* Get current time
* mach_absolute_time() gives time in int64_t of nanoseconds
* Converting to nanoseconds <https://developer.apple.com/library/mac/qa/qa1398/_index.html>
* Declared <https://opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/mach/mach_time.h>
* Definition <https://opensource.apple.com/source/Libc/Libc-320/i386/mach/mach_absolute_time.c>
* clock_get_time() gives a mach_timespec_t time
* Called from mach_absolute_time()
* mach_timespec_t
* Type documentation <https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KernelProgramming/services/services.html>
* Declaration <https://opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/mach/clock_types.h>
* <http://stackoverflow.com/a/21352348/300224>
* <https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x>
* Sleep
* mach_wait_until()
* Notes <https://developer.apple.com/library/ios/technotes/tn2169/_index.html>
* nanosleep()
* Apple doc <https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/nanosleep.2.html>
* Definition <https://opensource.apple.com/source/Libc/Libc-320.1.3/gen/nanosleep.c?txt>
* clock_sleep_trap()
* Used from nanosleep()
* Declared <https://opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/mach/mach_traps.h>
* Definition <http://unix.superglobalmegacorp.com/xnu/newsrc/osfmk/kern/clock.c.html>
* Uses clock_sleep_internal()
* Uses ADD_MACH_TIMESPEC
* clock type constants <https://opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/mach/clock_types.h?txt>
* TIME_ABSOLUTE
* TIME_RELATIVE
* Defines ADD_MACH_TIMESPEC(t1, t2) // t1 += t2
* Defines CMP_MACH_TIMESPEC(t1, t2) // t1 <=> t2, also (t1 - t2) in nsec with max of +- 1 sec
* msleep() <https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KernelProgramming/services/services.html>
* time/timer.c / <http://lxr.free-electrons.com/source/kernel/time/timer.c#L1673>
* kern/clock.h <https://opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/kern/clock.h>
## Press coverage
* <https://hardware.slashdot.org/story/16/03/01/1727226/microcasting-color-tv-by-abusing-a-wi-fi-chip>
* <https://news.softpedia.com/news/emitting-radio-waves-from-a-computer-with-no-radio-transmitting-hardware-501260.shtml>
* <https://tenwatts.blogspot.com/2018/01/system-bus-radio.html>
================================================
FILE: RTL-SDR-GUIDE.md
================================================
# RTL STR Basic Setup
## Test subject
For this guide, we are inspecting a **MacBook Pro M1 (13-inch, 2020)** for electromagnetic radiation.
This subject is tough because:
- Low power (wattage)
- System is tightly integrated (no user-replaceable RAM)
- Aluminum casing
If we are successful with the approaches in this paper, you should get even better results with other kinds of laptops.
## Hardware
*Other setups will work too. But this guide is explored with the following hardware:*
* Computer running macOS (to run SDR)
* RTL-SDR Blog V3 / [buy from manufacturer](https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/) / [data sheet](https://www.rtl-sdr.com/wp-content/uploads/2018/02/RTL-SDR-Blog-V3-Datasheet.pdf)
* Ham It Up Plus / [buy from manufacturer](https://nooelec.com/store/ham-it-up-plus.html) / [data sheet](https://www.nooelec.com/store/downloads/dl/file/id/99/product/284/ham_it_up_plus_datasheet_revision_2.pdf)
* Requires a USB A/B cable and a USB power source (do not use the same device running your SDR)
* Balun One Nine / [buy from manufacturer](https://www.nooelec.com/store/balun-one-nine-v2.html) / could not find data sheet
* An AM loop antenna
* Affiliate link to buy everything / [from Amazon](https://www.amazon.com/ideas/amzn1.account.AHUITP6B2VTROJ7IMNP2LCUA5QDA/18H46X17FDG76?type=explore&_encoding=UTF8&tag=phornetandrel-20&linkCode=ur2&linkId=dedd255a129c5ac7415a9dcb713ae618&camp=1789&creative=9325)
You may have seen that the RTL-SDR Blog V3 above [already includes support for lower frequencies](https://www.rtl-sdr.com/rtl-sdr-blog-v-3-dongles-user-guide/), obviating the upconverter (Ham It Up Plus). General advice on the scene has been that you want to use an upconverter rather than the built-in bias tee. If I can get better results with the bias tee approach I will update this guide to recommend that simpler and more ecoromical approach.
## Setup
Your equipment is plugged in as:
```
Computer --M/M-plug-- RTL-SDR --M/M-plug-- Ham It Up Plus --optional-long-wire-- Balun One Nine --spring-terminal-- antenna
```

Note that the upconverter means you will be tuning your radio to +125 MHz offset versus the frequencies you want.
## Software
**Test subject**
For quick browsing and playing, I used the [counter and threads](https://github.com/fulldecent/system-bus-radio/tree/master/Using%20counter%20and%20threads) implementation running on the M1 test subject. This allowed me to quickly edit the code and rerun it at different frequencies. (I ran the compiler command by hand as the Makefile did not work on the M1. Not sure if this needs fixing.)
**Radio**
This was an 2018 MacBook Pro (Intel). [CubicSDR](https://cubicsdr.com) was easy to set up. Also it claimed to let me try the bias tee approach, but I failed to make it work.
Alternately, I tried running [RTL Power](http://kmkeen.com/rtl-power/) to sweep various frequencies. Try it like this with the test subject off:
```sh
time rtl_power -f 125M:126M:20K -g 50 -i 1m -1 noise.csv; say done
```
And then run it again with `signal.csv`. And compare those two results.
Here is a quick Swift program to convert from RTL power into something you can use in Excel:
```swift
import Foundation
while let line = readLine() {
// date, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...
let columns = line.components(separatedBy: ", ")
let hzLow = Double(columns[2])!
let hzStep = Double(columns[4])!
var hzCurrent = hzLow
for dbm in columns[6...] {
print(Int(hzCurrent), dbm)
hzCurrent += hzStep
}
}
```
And of course:
```sh
paste -d, noise-transpose.csv signal-transpose.csv > merged.csv
```
Now you can plot the signal-to-noise ratio.
## Results
The antenna placed directly over the bottom left of the keyboard produces the best signal. Start here so you can clearly hear the signal and as your tuning improves, begin backing away the antenna.
I could clearly hear the signal between 63 kHz and 5.5 MHz using bandwidths between 10 kHz and 50 kHz. The best signal was 1.52 MHz at 40 kHz bandwidth. Using the AM demodulator.
With this approach I achieved audible signals up to only several inches away from the M1 Mac. Not very useful for spying/exfiltration. Possible ways to improve that are below.
## Further research
*These are a little more advanced idea I'd like to try with a mentor. If you'd like to pick up the ball and explain how to improve with these techniques, please feel free to open a new wiki page, and ping @fulldecent in a new issue.*
* It may be possible to improve the signal by adding a low-noise amplifier between the antenna and the upconverter. I'd like to test the signal levels around these compenents and read specs before considering that further.
* I could not get GNU Radio to connect to the RTL-SDR V3. It could certainly create a better custom receiver for this project:
1. Wideband 2.5MHz (or, somehow, twin 5.0MHz) signal input
2. A 1,000-band equalizer based on the envelope above (see RTL Power above)
3. Demodulate with AM
4. Tight bandpass at 440 Hz (for a 440 Hz system bus signal)
* Better hardware. If you can recommend a better RTL-SDR, antenna, LNA, upconverter that might help, please let me know and I can add to my [project tip jar](https://github.com/fulldecent/system-bus-radio#system-bus-radio).
================================================
FILE: TEST-DATA.tsv
================================================
Date Tester Transmitter Receiver Software Frequency Result Recording
2016-03-01 William Entriken MacBook Air (13-inch, Early 2015) Sony STR-K670P, stock antenna _mm_stream_si128 1580 kHz 2m thru air, 1m thru drywall https://youtu.be/caGPmyMLYUI
2016-03-01 Scott Buchanan "MacBook Pro Retina 15"", early 2013" N/A _mm_stream_si128 ?m audible https://goo.gl/ll3PxH
2016-03-01 Samuel Steele MacBook Air (13-inch, Mid-2013) Onkyo HT-R550, JVC Loop antenna _mm_stream_si128 1580 kHz "2"", noisy by 6"
2016-03-01 Chris Smolinski MBP (??-inch, 2010) netSDR, ??? antenna _mm_stream_si128 Entire AM band No signal found
2016-03-01 Chris Smolinski iMac (??-inch, 2015) netSDR, ??? antenna _mm_stream_si128 Entire AM band No signal found
2016-03-01 Chris Smolinski MBP (??-inch, 2010) Sony 7600G, no antenna _mm_stream_si128 1580 kHz, Long wave 4 https://youtu.be/l8AYHnF8ZrA
2016-03-01 Chris HP ENVY 15-j142na (i7 version), Linux Icom IC-R10, ??? antenna PR #19 ?m audible https://youtu.be/TXkh1ANSFGw
2016-03-01 João Ventura MacBook Pro (15-inch, Late 2013) Tech Fuzzion, tele antenna _mm_stream_si128 1600 kHz 4 https://youtu.be/oXAeGZaka7o
2016-03-01 Elvis Pfutzenreuter MacBook (12-inch, Early 2015) Sony ICF-SW11, internal antenna _mm_stream_si128 1580 kHz 2m, recommends turning off mains & light
2016-03-01 somini Asus X201E, Linux Clock radio, internal antenna _mm_stream_si128 1580 kHz 4 https://youtu.be/Nroc2BtO6NU
2016-03-01 janka102 MacBook Pro (15-inch, Early 2011) iHome iP90, included AM antenna _mm_stream_si128 1580 kHz 8 https://youtu.be/qN9D3bxkbXk
2016-03-01 Ryan Faerman MacBook Air (11-inch, 2014) Grundid Traveler 2 Digital, internal antenna _mm_stream_si128 1600 kHz "6""-8"
2016-03-02 Tomi Salmi Mac mini (Late 2014) Sharp stereo cassette recorder WQ-T282H(GR), tele antenna _mm_stream_si128 1580 kHz "4"" noisy"
2016-03-02 Fe Yi MacBook Pro (13-inch, Early 2015) TECSUN PL-310ET, internal antenna _mm_stream_si128 1580 kHz ~10cm above keyboard
2016-03-02 Ryou Ezoe Acer ASPIRE 5750, GNU/Linux Tecsun PL-310 fm/am Stereo World Band Dsp Receiver, internal antenna PR #12 1440 kHz 30cm
2016-03-02 Yuval Adam MacBook Pro (13-inch, Mid-2010) HackRF, 125Mhz upconverter, random wire antenna _mm_stream_si128 1580 kHz No discernible signal
2016-03-02 Kyohei Takahashi MacBook Pro (Retina, 13-inch, Late 2012) KOIZUMI SAD-7701-R AM mode _mm_stream_si128 500 kHz – 1400 kHz 30cm https://youtu.be/RJlOnoK5WpQ
2016-03-02 David Haberthür "MacBook Pro Core i7 2.4 15"" Late 2011" Sony CFD-S38L _mm_stream_si128 1584 kHz 6cm
2016-03-02 Jeremy Zerfas MacBook Pro (15-inch, Mid 2012) 2.3 Sony CFS-201 boom-box, internal antenna _mm_stream_si128 Various AM channels 6'
2016-03-02 Jeremy Zerfas MacBook Pro (15-inch, Early 2008) 2.4 Yamaha RX-V675, Loop antenna nanosleep mod Various AM channels 7', definitely farther than Mid 2012 model w/ same rcvr
2016-03-02 Jeremy Zerfas Athlon II X2 240, Gigabyte GA-MA785GM-US2H, Antec FusionRemote 350 Yamaha RX-V675, Loop antenna PR #12 Various AM channels "6"" from the processor"
2016-03-02 Nipun Gunawardena MacBook Pro Retina (13-inch, Late 2013) Onkyo CR305TX, Loop antenna _mm_stream_si128 1610 kHz 85cm Other frequencies also usable when very close
2016-03-02 Masahiko Uota MacBook Pro 2.8GHz i7 15-inch Mid 2014 Sony ICF-T46, no antenna _mm_stream_si128 1300 kHz, 900 kHz 6 inches https://twitter.com/muota_here/status/704924596802342913
2016-03-02 Yuji Fujita Thinkpad X200 Sony ICF-SW100 PR #12 1363 kHz 0.5m https://youtu.be/li9hHM4NkWA
2017-07-25 Ernesto Sanchez Lenovo Thinkpad X201 INDIN BC-R28 master 1600 kHz 0.3m None
2016-03-02 Redgar Nord HP ProBook 4340s, Linux Sharp radio, whip antenna PR #12 ~1590 kHz "6-7"", very orientation dependent"
2016-03-02 Redgar Nord RaspberryPi, Linux Sharp radio, whip antenna PR #12 ~1590 kHz No signal at all
2016-03-02 Erin Pinheiro Acer Aspire E1-572-6 BR691 Generic AM radio, retractable antenna PR #12 ~1590 kHz 10-20cm Slightly noisy https://dl.dropboxusercontent.com/u/9435923/code/audio_2016-03-02_18-24-30.ogg
2016-03-02 Trevor Summerfield MacBook Pro (Retina, 15-inch, Mid 2015) Grundig G8 Traveler II Digital, Internal AM Antenna _mm_stream_si128 1580 kHz 8” above keyboard 50dbu signal, 0db SNR, audible
2016-03-05 Alessio Gerace MacBookPro Retina (13-inch, 2014) Majestic TT34CD/TP/USB _mm_stream_si128 ~1610 kHz 10/30 cm
2016-03-05 Quan Yang MacBookPro Retina (Late 2013) AR1741 Multiband Receiver Javascript 1560 khz 0.5m, more frequencies usable if charging https://youtu.be/6tM4EKUYogI
2016-03-07 Mehdi Asgari MacBook Pro Retina (13-inch, 2015) Tecsun PL680, internal whip antenna _mm_stream_si128 1610 kHz 90cm
2016-03-10 Gabriel Tremblay Cooler Master HAF 912, ASUS M5A97, AMD FX-8120, Corsair Vengeance 32GB Not known PR #12 ~1550 kHz ~20-30cm
2016-09-05 Chris Rochford Macbook Pro Retina (13-inch, 2015) Yamaha HTR-5730, loop antenna Javascript 1580 kHz 6 inches
2016-10-13 John Sampson MacBook Pro Retina (15-inch, Mid 2014) Sony ICFCS15IPN (stock antenna) _mm_stream_si128 ~1560 kHz 5 inches
2018-01-24 Philipp Hanslovsky Lenovo X1 Carbon (14-inch, 2014) Yamaha RX-V675, Loop antenna _mm_stream_si128 @ 03c3689 1590 kHz, 1600kHz 5 inches
2018-01-24 Fabian Briese Dell Inspiron 17 7000 (7737) Tevion Compact Disk Player Webbrowser (Chrome) 1560 kHz 125-150cm
2018-01-30 Troy Giorshev MacBook Air (13-inch, Early 2014) Eton Mini 300 _mm_stream_si128 1535 kHz 30cm
2020-12-17 Steven Shim MacBook Pro M1 (13-inch, 2020) Sony AM/FM Clock Radio _mm_stream_si128 1560kHz 2-3 inches from bottom right of trackpad
2022-02-01 Ilya Semichastnov MacBook Pro (16-inch, 2019) Yaesu FT-817 _mm_stream_si128 1530kHz wire antenna 50cm below the laptop
2024-01-03 Christian Mürtz MacBook Pro Late 2016 NORDMENDE - Compact Recorder 5043 K _mm_stream_si128 1550 kHz ~30cm https://www.youtube.com/watch?v=jUKchOM_TjM
2024-07-09 Noah King MacBook Late 2007 1958 Orion 10 Transistor Radio _mm_stream_si128 ~1690 kHz No antenna 10cm-1m https://www.youtube.com/watch?v=YCXruZPXfss
================================================
FILE: docs/.gitignore
================================================
/_site
Gemfile.lock
================================================
FILE: docs/Gemfile
================================================
source 'https://rubygems.org'
gem 'github-pages', group: :jekyll_plugins
================================================
FILE: docs/airgap.js
================================================
var player; // Define "player" var to make my code linter happy
function start() { // Start Web Worker & send song data to player
var logs = document.getElementById('progress'); // Define log element
window.logs = logs; // Make variable Global
window.logs.value = "";
// Create Web Worker if it doesn't already exist
if (window.Worker && typeof(player) == "undefined") {
var player = new Worker("worker.js");
window.player = player; // Make variable Global
player.onmessage = function(event) {
var data = event.data;
console.log(data)
window.logs.value += "x\n";
};
// Send song data to player
var song = document.getElementById("tune").value;
player.postMessage(song);
}
}
function end() { // Stops the Web Worker
window.logs.value = "";
player.terminate();
}
================================================
FILE: docs/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="System Bus Radio JS Port: Play AM radio waves from a computer without an AM radio, using only your web browser.">
<title>System Bus Radio</title>
<link rel="stylesheet" href="main.css"></style>
<script src="airgap.js"></script>
</head>
<body style="text-align:center">
<header>
<h1>System Bus Radio</h1>
</header>
<p><em>Tested on MacBook Air / Chrome with AM tuner at 1560 kHz. <a href="https://github.com/fulldecent/system-bus-radio/blob/master/TEST-DATA.tsv">See field reports</a> for other equipment and frequencies.</em></p>
<p>
<button id="play" onclick="start()">Play tune</button>
<button id="stop" onclick="end()">Stop tune</button>
</p>
<section style="display:flex;width:60%;margin:auto">
<textarea style="width:2rem" id="progress" readonly></textarea>
<textarea style="flex:1;height:50rem" id="tune">
2673 400
2349 400
2093 400
2349 400
2673 400
0 400
2673 400
0 400
2673 790
2349 400
2349 400
0 400
2349 790
2673 400
3136 400
0 400
3136 790
2673 400
2349 400
2093 400
2349 400
2673 400
0 400
2673 400
0 400
2673 400
0 400
2673 400
2349 400
0 400
2349 400
2673 400
2349 400
2093 790</textarea>
</section>
<p>Edit the above to make any music you like. <a href="https://github.com/fulldecent/system-bus-radio/issues/28">Tune file format</a> is frequency (Hz) and time (ms).</p>
<p>Chrome has errors if you open this file locally (<code>file://</code>). Try using <code>php -S localhost:8000</code> or similar for a quick web server.</p>
<p>Ported by <a href="https://github.com/quanyang">Yeo Quan Yang</a> & maintained by <a href="https://github.com/rocketinventor">Elliot Gerchak</a>.</p>
<p>Original machine code by <a href="https://github.com/fulldecent">William Entriken</a>.</p>
<p><strong>Project site at <a href="https://github.com/fulldecent/system-bus-radio">https://github.com/fulldecent/system-bus-radio</a></strong></p>
</body>
</html>
================================================
FILE: docs/main.css
================================================
/* Main style */
body {
background-color: #3498db;
color: white;
font-family: Futura, Helvetica, "Century Gothic";
text-shadow: 1px 1px 0px black;
}
p {
font-size: 1.2rem;
}
a {
color: white;
}
a:hover {
color: red;
}
h1 {
font-size: 3rem;
font-weight: normal;
text-align: center;
}
textarea {
font-size: 1.25rem;
font-family: courier;
}
/* BUTTON FROM https://github.com/eisenfox/css3-button-hovers */
button {
background: purple;
text-align: center;
display: inline-block;
position: relative;
text-decoration: none;
color: #fff;
text-transform: capitalize;
font-size: 18px;
padding: 20px 0px;
width: 150px;
border-radius: 6px;
overflow: hidden;
-webkit-transition: all 0.2s linear 0s;
transition: all 0.2s linear 0s;
}
button#play:before {
content: "\25BA";
}
button#stop:before {
content: "\25FC";
}
button:before {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
top: 0;
left: 0px;
height: 100%;
width: 30px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 0 50% 50% 0;
-webkit-transform: scale(0, 1);
transform: scale(0, 1);
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transition: all 0.2s linear 0s;
transition: all 0.2s linear 0s;
}
button:hover {
text-indent: 30px;
}
button:hover:before {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
text-indent: 0;
}
================================================
FILE: docs/worker.js
================================================
//Credits to https://github.com/fulldecent/system-bus-radio
//As well as Jordan Harband for the nodejs simd library
//Tested to be working on Chrome at 1560khz
var window = self; // Create "window" object
function now() {
return window.performance.now() * 1000000;
}
var NSEC_PER_SEC = 1000000000;
var register = 3.1415;
function square_am_signal(freq, time) { // This function generates the radio waves
postMessage("\nPlaying / " + time + " seconds / " + freq + "Hz");
var period = NSEC_PER_SEC / freq;
var start = now();
var end = now() + time * NSEC_PER_SEC;
while (now() < end) {
var mid = start + period / 2;
var reset = start + period;
while (now() < mid) {
for (var i = 0; i < 100; i++) {
register = 1 - Math.log(register) / 1.7193;
}
}
while (now() < reset) {}
start = reset;
}
}
function play(song) { // Parse song data, and call on required scripts to run it
song = song.split("\n");
var length = song.length;
var line, freq, time;
for (var i = 0; i < length; i++) {
line = song[i].split(" ");
if (+line[1] == 0) {
pause(line[0]);
}
else {
time = (+line[1])*.001;
freq = +line[0];
square_am_signal(freq, time);
}
}
close(); // Close Web Worker
}
function pause(time) { // A useless function to run when there is no noise to play
postMessage("\nPaused / " + time*.001 + " seconds");
var dt = new Date();
while ((new Date()) - dt <= time) { /* Do nothing */ }
}
onmessage = function(event) { // Recieve song data from main page
var data = event.data;
play(data);
};
================================================
FILE: implementations/c-_mm_stream_si128/Makefile
================================================
CPPFLAGS=-Wall -O2 -msse2
main: main.c
.PHONY: clean
clean:
rm -f main
================================================
FILE: implementations/c-_mm_stream_si128/main.c
================================================
// SYSTEM BUS RADIO
// https://github.com/fulldecent/system-bus-radio
// Copyright 2016 William Entriken
#include <stdio.h>
#include <emmintrin.h>
#include <inttypes.h>
#include <time.h>
#include <math.h>
#ifdef __MACH__
#include <mach/mach_traps.h>
#include <mach/mach_time.h>
#endif
#ifndef NSEC_PER_SEC
#define NSEC_PER_SEC 1000000000ull
#endif
#ifndef __MACH__
#define TIME_ABSOLUTE CLOCK_REALTIME
typedef struct timespec mach_timespec_t;
typedef unsigned int mach_port_t;
static inline uint64_t mach_absolute_time(void) {
mach_timespec_t tp;
int res = clock_gettime(CLOCK_REALTIME, &tp);
if (res < 0) {
perror("clock_gettime");
exit(1);
}
uint64_t result = tp.tv_sec * NSEC_PER_SEC;
result += tp.tv_nsec;
return result;
}
// non-conformant wrapper just for the purposes of this application
static inline void clock_sleep_trap(mach_port_t clock_port, int sleep_type, time_t sec, long nsec, mach_timespec_t *remain) {
mach_timespec_t req = { sec, nsec };
int res = clock_nanosleep(sleep_type, TIMER_ABSTIME, &req, remain);
if (res < 0) {
perror("clock_nanosleep");
exit(1);
}
}
#endif // __MACH__
__m128i reg;
__m128i reg_zero;
__m128i reg_one;
mach_port_t clock_port;
mach_timespec_t remain;
static inline void square_am_signal(float frequency, float time) {
printf("Playing / %0.3f seconds / %4.0f Hz\n", time, frequency);
uint64_t period = NSEC_PER_SEC / frequency;
uint64_t start = mach_absolute_time();
uint64_t end = start + (uint64_t)(time * NSEC_PER_SEC);
while (mach_absolute_time() < end) {
uint64_t mid = start + period / 2;
uint64_t reset = start + period;
while (mach_absolute_time() < mid) {
_mm_stream_si128(®, reg_one);
_mm_stream_si128(®, reg_zero);
}
clock_sleep_trap(clock_port, TIME_ABSOLUTE, reset / NSEC_PER_SEC, reset % NSEC_PER_SEC, &remain);
start = reset;
}
}
int main(int argc, char* argv[])
{
#ifdef __MACH__
mach_timebase_info_data_t theTimeBaseInfo;
mach_timebase_info(&theTimeBaseInfo);
puts("TESTING TIME BASE: the following should be 1 / 1");
printf(" Mach base: %u / %u nanoseconds\n\n", theTimeBaseInfo.numer, theTimeBaseInfo.denom);
#endif
uint64_t start = mach_absolute_time();
uint64_t end = mach_absolute_time();
printf("TESTING TIME TO EXECUTE mach_absolute_time()\n Result: %"PRIu64" nanoseconds\n\n", end - start);
reg_zero = _mm_set_epi32(0, 0, 0, 0);
reg_one = _mm_set_epi32(-1, -1, -1, -1);
FILE* fp;
if (argc == 2) {
fp = fopen(argv[1], "r");
} else {
printf("No song file given!\nUsage: %s file.song\n", argv[0]);
exit(1);
}
char buffer[20] = {0};
int freq_hz;
int time_ms;
while (1) {
fgets(buffer, 20 - 1, fp);
if (sscanf(buffer, "%d %d", &freq_hz, &time_ms) == 2) {
square_am_signal(1.0 * time_ms / 1000, freq_hz);
}
if (feof(fp)) {
rewind(fp);
}
}
}
================================================
FILE: implementations/c-apple-silicon/Makefile
================================================
CPPFLAGS=-Wall -O2
main: main.c
.PHONY: clean
clean:
rm -f main
================================================
FILE: implementations/c-apple-silicon/README.md
================================================
# System Bus Radio for Apple Silicon (M1/M2/M3)
This implementation is specifically optimized for Apple Silicon (M1/M2/M3) chips, which have strict power management and thermal throttling that can interfere with traditional memory bus noise generation.
## Why a specialized version?
Standard implementations that hammer the memory bus continuously are quickly detected by the macOS power management unit (PMU) as "runaway processes." This results in:
* Thermal throttling (clock speed reduction).
* Process prioritization penalties (moving to efficiency cores).
* Signal loss after a few seconds of playback.
## Solution: Pulse-Packet Modulation
This implementation uses a **"Pulse-Packet" strategy**:
1. **Burst Transmission:** It blasts the memory bus with 4 parallel threads (utilizing all P-Cores) for short durations (e.g., 20ms).
2. **Micro-Sleep:** It forces a tiny sleep (e.g., 0.5ms) between bursts.
This intermittent load tricks the PMU into thinking the process is behaving normally, allowing sustained high-power transmission without throttling.
## Hardware Setup (Tested Configuration)
* **Computer:** MacBook Air (M1, 2020)
* **Radio:** SONY ICF-B99 (AM Receiver)
* **Frequency:** ~1100 kHz (1.1 MHz) - *Note: This differs from the original 1580 kHz recommendation for Intel Macs.*
* **Antenna Position:** Bottom center of the laptop (near the logic board).
## Compilation & Usage
1. Compile the program:
```sh
gcc -O3 main.c -o main
```
2. Run with a tune file:
```sh
./main ../../tunes/mary_had_a_little_lamb.tune
```
3. **Important:** For best results:
* Keep your Mac plugged into power.
* Close other heavy applications.
* Tune your AM radio to **~1100 kHz** (explore nearby frequencies for the strongest signal).
================================================
FILE: implementations/c-apple-silicon/main.c
================================================
// SYSTEM BUS RADIO - M1 PULSE-PACKET TRANSMITTER (4 CORES)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <arm_neon.h>
#include <time.h>
#include <mach/mach_time.h>
#include <mach/thread_policy.h>
#include <mach/mach_init.h>
#include <mach/thread_act.h>
#include <pthread.h>
#include <pthread/qos.h>
#define NUM_THREADS 4
#define MEM_SIZE (64 * 1024 * 1024)
#define PACKET_MS 20
uint8_t *g_mem;
static mach_timebase_info_data_t timebase_info;
volatile int g_running = 1;
// Shared Parameters
volatile uint64_t g_current_freq = 0;
volatile uint64_t g_current_duration_ms = 0;
// Custom Barrier Structure
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int count;
int crossing;
int total;
} my_barrier_t;
my_barrier_t g_barrier_start;
my_barrier_t g_barrier_end;
void barrier_init(my_barrier_t *b, int total) {
pthread_mutex_init(&b->mutex, NULL);
pthread_cond_init(&b->cond, NULL);
b->count = 0;
b->crossing = 0;
b->total = total;
}
void barrier_wait(my_barrier_t *b) {
pthread_mutex_lock(&b->mutex);
b->count++;
if (b->count >= b->total) {
b->crossing++;
b->count = 0;
pthread_cond_broadcast(&b->cond);
} else {
int my_crossing = b->crossing;
while (my_crossing == b->crossing) {
pthread_cond_wait(&b->cond, &b->mutex);
}
}
pthread_mutex_unlock(&b->mutex);
}
static inline void bus_poke(size_t offset) {
// Write 0xFFFFFFFFFFFFFFFF to the bus
uint64_t val = 0xFFFFFFFFFFFFFFFFull;
asm volatile (
"stnp %0, %0, [%1]\n" "stnp %0, %0, [%1, #16]\n"
"stnp %0, %0, [%1, #32]\n" "stnp %0, %0, [%1, #48]\n"
: : "r"(val), "r"(&g_mem[offset]) : "memory"
);
}
void set_realtime(int affinity_tag) {
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
thread_affinity_policy_data_t affinity;
affinity.affinity_tag = affinity_tag;
thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY, (thread_policy_t)&affinity, THREAD_AFFINITY_POLICY_COUNT);
}
void* worker_thread(void* arg) {
int id = (int)(size_t)arg;
set_realtime(id + 1);
size_t segment = MEM_SIZE / NUM_THREADS;
size_t offset = id * segment;
size_t end_offset = offset + segment - 64;
size_t current = offset;
while (g_running) {
// 1. Wait for start signal
barrier_wait(&g_barrier_start);
if (!g_running) break;
uint64_t freq = g_current_freq;
uint64_t duration = g_current_duration_ms;
if (freq > 0) {
uint64_t start = mach_absolute_time();
uint64_t end = start + (duration * 1000000ull * timebase_info.denom / timebase_info.numer);
uint64_t period = (1000000000ull / freq) * timebase_info.denom / timebase_info.numer;
uint64_t half_period = period / 2;
uint64_t now = start;
while (now < end) {
uint64_t next_edge = now + half_period;
// ON Phase
while (mach_absolute_time() < next_edge) {
bus_poke(current);
current += 64;
if (current >= end_offset) current = offset;
}
next_edge += half_period;
// OFF Phase
while (mach_absolute_time() < next_edge) {
asm volatile("yield");
}
now = next_edge;
}
}
// 2. Report completion
barrier_wait(&g_barrier_end);
}
return NULL;
}
void play_tone(uint64_t freq_hz, uint64_t time_ms) {
uint64_t remaining = time_ms;
while (remaining > 0) {
uint64_t chunk = (remaining > PACKET_MS) ? PACKET_MS : remaining;
g_current_freq = freq_hz;
g_current_duration_ms = chunk;
// START
barrier_wait(&g_barrier_start);
// END WAIT
barrier_wait(&g_barrier_end);
// Short pause (0.5ms) to reset throttling counters
struct timespec ts = {0, 500000};
nanosleep(&ts, NULL);
remaining -= chunk;
}
}
int main(int argc, char* argv[]) {
mach_timebase_info(&timebase_info);
if (argc != 2) {
fprintf(stderr, "Usage: %s file.tune\n", argv[0]);
return 1;
}
FILE* fp = fopen(argv[1], "r");
if (!fp) { perror("fopen"); return 1; }
g_mem = malloc(MEM_SIZE);
memset(g_mem, 0, MEM_SIZE);
barrier_init(&g_barrier_start, NUM_THREADS + 1);
barrier_init(&g_barrier_end, NUM_THREADS + 1);
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, worker_thread, (void*)(size_t)i);
}
set_realtime(0);
printf("M1 Pulse-Packet Mode (Custom Barrier). Playing...\n");
char buffer[128];
int freq_hz, time_ms;
while (1) {
if (fgets(buffer, sizeof(buffer), fp) == NULL) {
if (feof(fp)) {
rewind(fp);
continue;
}
break;
}
if (buffer[0] == '\n' || buffer[0] == '#' || buffer[0] == ' ') continue;
if (sscanf(buffer, "%d %d", &freq_hz, &time_ms) == 2) {
printf("\r%4d Hz ", freq_hz);
fflush(stdout);
play_tone(freq_hz, time_ms);
}
}
g_running = 0;
barrier_wait(&g_barrier_start); // Release waiting threads
for (int i = 0; i < NUM_THREADS; i++) pthread_join(threads[i], NULL);
fclose(fp);
return 0;
}
================================================
FILE: implementations/c-apple-silicon-wav/Makefile
================================================
CPPFLAGS=-Wall -O2
main: main.c
.PHONY: clean
clean:
rm -f main
================================================
FILE: implementations/c-apple-silicon-wav/README.md
================================================
# c-apple-silicon-wav
This is a **delta implementation** based on the Apple Silicon version in
`implementations/c-apple-silicon`, extended to play arbitrary WAV files.
For hardware assumptions and receiver setup (such as AM radio placement),
see `implementations/c-apple-silicon/README.md`.
This README summarizes **only the differences**.
## Changes from c-apple-silicon
1. Input format
- Changed input from `.tune` to WAV
- Run format: `./main input.wav`
2. WAV loader
- Added `RIFF/WAVE` validation
- Scans and reads `fmt ` / `data` chunks
- Handles chunk padding (even-byte alignment)
- Downmixes multi-channel audio to mono
- Applies input sample rate to playback timing
3. Audio preprocessing
- 32-bit-safe peak normalization
- Voice-oriented pre-EQ (HP/LP + presence)
- Compression and loudness boost via AGC + limiter
4. Modulation method
- Replaced random-sampling-based density generation with a Sigma-Delta
(error-accumulation) method
- Improves intelligibility by reducing fine-grained random on/off artifacts
5. Packet handling
- Keeps sample time continuity across packets
- Sets default `PACKET_MS` to `100ms`
- Adjustable at runtime via `SBR_PACKET_MS`
## Supported WAV
- `RIFF/WAVE`
- PCM 16-bit (`audio_format=1`, `bits_per_sample=16`)
- 1 channel or more (internally downmixed to mono)
- Any sample rate (tracked by internal timing)
Not supported:
- float PCM
- 24/32-bit PCM
- WAV with compressed codecs
## Build
```bash
cd /Users/cho45/tmp/system-bus-radio/implementations/c-apple-silicon-wav
make
```
## Run
```bash
./main input.wav
```
Examples:
```bash
./main hello.wav
./main sweep.wav
```
## Main runtime parameters
- `SBR_DENSITY_EXP`: Exponent of the modulation curve
- `SBR_DENSITY_DEPTH`: Modulation depth
- `SBR_AGC_TARGET`: Target envelope level for AGC
- `SBR_AGC_MAKEUP`: Makeup gain after AGC
- `SBR_AGC_MAX_GAIN`: Maximum AGC gain
- `SBR_PACKET_MS`: Packet length (ms)
## Tuning guidelines
- Still too quiet: increase `SBR_AGC_TARGET`, `SBR_AGC_MAKEUP`,
and `SBR_AGC_MAX_GAIN`
- Choppy output: increase `SBR_PACKET_MS` (for example `200` to `500`)
- Distortion: decrease `SBR_AGC_MAKEUP` or `SBR_DENSITY_DEPTH`
================================================
FILE: implementations/c-apple-silicon-wav/gen_sweep.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#define SAMPLE_RATE 24000
#define DURATION_PER_TONE 2
#define NUM_TONES 5
int frequencies[] = {500, 1000, 2000, 4000, 8000};
int main() {
FILE *fp = fopen("sweep.wav", "wb");
if (!fp) return 1;
uint32_t total_samples = SAMPLE_RATE * DURATION_PER_TONE * NUM_TONES;
uint32_t data_len = total_samples * 2;
uint32_t riff_len = data_len + 36;
fwrite("RIFF", 1, 4, fp);
fwrite(&riff_len, 4, 1, fp);
fwrite("WAVE", 1, 4, fp);
fwrite("fmt ", 1, 4, fp);
uint32_t fmt_len = 16;
uint16_t audio_fmt = 1;
uint16_t num_channels = 1;
uint32_t sample_rate = SAMPLE_RATE;
uint32_t byte_rate = SAMPLE_RATE * 2;
uint16_t block_align = 2;
uint16_t bits_per_sample = 16;
fwrite(&fmt_len, 4, 1, fp);
fwrite(&audio_fmt, 2, 1, fp);
fwrite(&num_channels, 2, 1, fp);
fwrite(&sample_rate, 4, 1, fp);
fwrite(&byte_rate, 4, 1, fp);
fwrite(&block_align, 2, 1, fp);
fwrite(&bits_per_sample, 2, 1, fp);
fwrite("data", 1, 4, fp);
fwrite(&data_len, 4, 1, fp);
for (int t = 0; t < NUM_TONES; t++) {
int freq = frequencies[t];
printf("Generating %d Hz...\n", freq);
for (int i = 0; i < SAMPLE_RATE * DURATION_PER_TONE; i++) {
double time = (double)i / SAMPLE_RATE;
double v = sin(2.0 * M_PI * freq * time);
int16_t sample = (int16_t)(v * 32000.0);
fwrite(&sample, 2, 1, fp);
}
}
fclose(fp);
printf("Generated sweep.wav (500, 1000, 2000, 4000, 8000 Hz)\n");
return 0;
}
================================================
FILE: implementations/c-apple-silicon-wav/main.c
================================================
// SYSTEM BUS RADIO - M1 WAV PLAYER (STOCHASTIC DENSITY MODULATION)
// Uses probability to modulate bus density, achieving high-resolution AM.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <arm_neon.h>
#include <time.h>
#include <mach/mach_time.h>
#include <mach/thread_policy.h>
#include <mach/mach_init.h>
#include <mach/thread_act.h>
#include <pthread.h>
#include <pthread/qos.h>
#define NUM_THREADS 4
#define MEM_SIZE (64 * 1024 * 1024)
#define SAMPLE_RATE 24000.0
#define PACKET_MS 100
uint8_t *g_mem;
static mach_timebase_info_data_t timebase_info;
volatile int g_running = 1;
int16_t *g_audio_data = NULL;
size_t g_audio_len = 0;
double g_sample_rate = SAMPLE_RATE;
double g_density_exp = 0.55;
double g_density_depth = 0.44;
double g_agc_target_env = 0.36;
double g_agc_makeup = 2.6;
double g_agc_max_gain = 96.0;
double g_packet_ms = PACKET_MS;
// Synchronization
volatile size_t g_packet_start_sample = 0;
volatile size_t g_packet_num_samples = 0;
// Custom Barrier
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int count;
int crossing;
int total;
} my_barrier_t;
my_barrier_t g_barrier_start;
my_barrier_t g_barrier_end;
void barrier_init(my_barrier_t *b, int total) {
pthread_mutex_init(&b->mutex, NULL);
pthread_cond_init(&b->cond, NULL);
b->count = 0;
b->crossing = 0;
b->total = total;
}
void barrier_wait(my_barrier_t *b) {
pthread_mutex_lock(&b->mutex);
b->count++;
if (b->count >= b->total) {
b->crossing++;
b->count = 0;
pthread_cond_broadcast(&b->cond);
} else {
int my_crossing = b->crossing;
while (my_crossing == b->crossing) {
pthread_cond_wait(&b->cond, &b->mutex);
}
}
pthread_mutex_unlock(&b->mutex);
}
static inline void bus_poke(size_t offset) {
uint64_t val = 0xFFFFFFFFFFFFFFFFull;
asm volatile (
"stnp %0, %0, [%1]\n" "stnp %0, %0, [%1, #16]\n"
"stnp %0, %0, [%1, #32]\n" "stnp %0, %0, [%1, #48]\n"
: : "r"(val), "r"(&g_mem[offset]) : "memory"
);
}
void set_realtime(int affinity_tag) {
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
thread_affinity_policy_data_t affinity;
affinity.affinity_tag = affinity_tag;
thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY, (thread_policy_t)&affinity, THREAD_AFFINITY_POLICY_COUNT);
}
void* worker_thread(void* arg) {
int id = (int)(size_t)arg;
set_realtime(id + 1);
size_t segment = MEM_SIZE / NUM_THREADS;
size_t offset = id * segment;
size_t end_offset = offset + segment - 64;
size_t current = offset;
// Per-thread sigma-delta state. Stagger the phase across threads.
double pdm_error = (double)id / (double)NUM_THREADS;
// Timebase conversion constants
uint64_t numer = timebase_info.numer;
uint64_t denom = timebase_info.denom;
// 1サンプルの長さ (mach time)
uint64_t sample_duration_mach = (uint64_t)((1000000000.0 / g_sample_rate) * (double)denom / (double)numer);
uint64_t next_sample_time = mach_absolute_time();
while (1) {
barrier_wait(&g_barrier_start);
if (!g_running) break;
size_t start_idx = g_packet_start_sample;
size_t num_samples = g_packet_num_samples;
uint64_t now = mach_absolute_time();
if (next_sample_time < now) next_sample_time = now;
for (size_t i = 0; i < num_samples; i++) {
int16_t sample = g_audio_data[start_idx + i];
// Strong speech-oriented companding to make low-level voice audible.
double sample_norm = (double)sample / 32768.0; // -1.0 ~ +1.0
double shaped = copysign(pow(fabs(sample_norm), g_density_exp), sample_norm);
double density = 0.5 + (shaped * g_density_depth);
if (density < 0.01) density = 0.01;
if (density > 0.99) density = 0.99;
next_sample_time += sample_duration_mach;
// 次のサンプル時刻まで sigma-delta 的に密度を実現する
while (mach_absolute_time() < next_sample_time) {
pdm_error += density;
if (pdm_error >= 1.0) {
bus_poke(current);
current += 64;
if (current >= end_offset) current = offset;
pdm_error -= 1.0;
} else {
asm volatile("yield");
}
}
}
barrier_wait(&g_barrier_end);
}
return NULL;
}
static uint16_t read_le16(const uint8_t* p) {
return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
}
static uint32_t read_le32(const uint8_t* p) {
return (uint32_t)p[0] |
((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
}
static double get_env_double(const char* name, double default_value) {
const char* v = getenv(name);
if (!v || !*v) return default_value;
char* end = NULL;
double parsed = strtod(v, &end);
if (end == v) return default_value;
return parsed;
}
int load_wav(const char* filename) {
FILE* fp = fopen(filename, "rb");
if (!fp) { perror("fopen"); return 0; }
uint8_t riff_header[12];
if (fread(riff_header, 1, sizeof(riff_header), fp) != sizeof(riff_header)) {
fprintf(stderr, "Invalid WAV: header too short\n");
fclose(fp);
return 0;
}
if (memcmp(riff_header, "RIFF", 4) != 0 || memcmp(riff_header + 8, "WAVE", 4) != 0) {
fprintf(stderr, "Invalid WAV: missing RIFF/WAVE\n");
fclose(fp);
return 0;
}
int have_fmt = 0;
int have_data = 0;
uint16_t fmt_audio_format = 0;
uint16_t fmt_channels = 0;
uint32_t fmt_sample_rate = 0;
uint16_t fmt_bits_per_sample = 0;
long data_offset = 0;
uint32_t data_size = 0;
while (1) {
uint8_t chunk_header[8];
if (fread(chunk_header, 1, sizeof(chunk_header), fp) != sizeof(chunk_header)) break;
uint32_t chunk_size = read_le32(chunk_header + 4);
long chunk_data_pos = ftell(fp);
if (chunk_data_pos < 0) {
fclose(fp);
return 0;
}
if (memcmp(chunk_header, "fmt ", 4) == 0) {
if (chunk_size < 16) {
fprintf(stderr, "Invalid WAV: fmt chunk too short\n");
fclose(fp);
return 0;
}
uint8_t fmt_buf[16];
if (fread(fmt_buf, 1, sizeof(fmt_buf), fp) != sizeof(fmt_buf)) {
fprintf(stderr, "Invalid WAV: truncated fmt chunk\n");
fclose(fp);
return 0;
}
fmt_audio_format = read_le16(fmt_buf + 0);
fmt_channels = read_le16(fmt_buf + 2);
fmt_sample_rate = read_le32(fmt_buf + 4);
fmt_bits_per_sample = read_le16(fmt_buf + 14);
have_fmt = 1;
} else if (memcmp(chunk_header, "data", 4) == 0) {
data_offset = chunk_data_pos;
data_size = chunk_size;
have_data = 1;
}
long next_chunk = chunk_data_pos + (long)chunk_size + (chunk_size & 1u);
if (fseek(fp, next_chunk, SEEK_SET) != 0) break;
}
if (!have_fmt || !have_data) {
fprintf(stderr, "Invalid WAV: missing fmt/data chunk\n");
fclose(fp);
return 0;
}
if (fmt_audio_format != 1 || fmt_bits_per_sample != 16) {
fprintf(stderr, "Unsupported WAV: only PCM 16-bit is supported\n");
fclose(fp);
return 0;
}
if (fmt_channels == 0) {
fprintf(stderr, "Unsupported WAV: invalid channel count\n");
fclose(fp);
return 0;
}
if (fseek(fp, data_offset, SEEK_SET) != 0) {
perror("fseek");
fclose(fp);
return 0;
}
size_t bytes_per_frame = (size_t)fmt_channels * sizeof(int16_t);
if (bytes_per_frame == 0 || data_size < bytes_per_frame) {
fprintf(stderr, "Unsupported WAV: invalid data size\n");
fclose(fp);
return 0;
}
size_t total_frames = data_size / bytes_per_frame;
int16_t* raw = (int16_t*)malloc(total_frames * bytes_per_frame);
g_audio_data = (int16_t*)malloc(total_frames * sizeof(int16_t));
if (!raw || !g_audio_data) {
fprintf(stderr, "malloc failed\n");
fclose(fp);
free(raw);
free(g_audio_data);
g_audio_data = NULL;
return 0;
}
if (fread(raw, bytes_per_frame, total_frames, fp) != total_frames) {
fprintf(stderr, "Invalid WAV: truncated data chunk\n");
fclose(fp);
free(raw);
free(g_audio_data);
g_audio_data = NULL;
return 0;
}
fclose(fp);
// Downmix multi-channel PCM to mono.
for (size_t i = 0; i < total_frames; i++) {
int32_t sum = 0;
for (uint16_t ch = 0; ch < fmt_channels; ch++) {
sum += raw[i * fmt_channels + ch];
}
g_audio_data[i] = (int16_t)(sum / (int32_t)fmt_channels);
}
free(raw);
g_audio_len = total_frames;
g_sample_rate = (double)fmt_sample_rate;
// Normalize audio peak safely with 32-bit abs.
int32_t max_val = 0;
for (size_t i = 0; i < g_audio_len; i++) {
int32_t v = g_audio_data[i];
int32_t a = (v < 0) ? -v : v;
if (a > max_val) max_val = a;
}
if (max_val > 0 && max_val < 32000) {
double scale = 32000.0 / (double)max_val;
printf("Normalizing audio (Peak: %d, Scale: %.2f)...\n", max_val, scale);
for (size_t i = 0; i < g_audio_len; i++) {
int32_t scaled = (int32_t)lrint((double)g_audio_data[i] * scale);
if (scaled > 32767) scaled = 32767;
if (scaled < -32768) scaled = -32768;
g_audio_data[i] = (int16_t)scaled;
}
} else {
printf("Audio peak is %d (already loud enough or silence).\n", max_val);
}
// Speech-focused pre-EQ: DC/high-pass + low-pass + slight presence boost.
{
const double hp_fc = 180.0;
const double lp_fc = 3400.0;
const double dt = 1.0 / g_sample_rate;
const double rc_hp = 1.0 / (2.0 * M_PI * hp_fc);
const double hp_a = rc_hp / (rc_hp + dt);
const double lp_a = exp(-2.0 * M_PI * lp_fc / g_sample_rate);
const double presence = 0.35;
double hp_prev_x = 0.0;
double hp_prev_y = 0.0;
double lp_state = 0.0;
double prev_lp = 0.0;
for (size_t i = 0; i < g_audio_len; i++) {
double x = (double)g_audio_data[i] / 32768.0;
double hp = hp_a * (hp_prev_y + x - hp_prev_x);
hp_prev_x = x;
hp_prev_y = hp;
double lp = (1.0 - lp_a) * hp + lp_a * lp_state;
lp_state = lp;
double y = lp + presence * (lp - prev_lp);
prev_lp = lp;
if (y > 1.0) y = 1.0;
if (y < -1.0) y = -1.0;
g_audio_data[i] = (int16_t)lrint(y * 32767.0);
}
}
// AGC + limiter for low-RMS speech.
double attack = exp(-1.0 / (g_sample_rate * 0.002)); // 2ms attack
double release = exp(-1.0 / (g_sample_rate * 0.120)); // 120ms release
double env = 1e-4;
const double limiter_drive = 2.8;
const double limiter_norm = 1.0 / tanh(limiter_drive);
for (size_t i = 0; i < g_audio_len; i++) {
double x = (double)g_audio_data[i] / 32768.0;
double ax = fabs(x);
double coeff = (ax > env) ? attack : release;
env = coeff * env + (1.0 - coeff) * ax;
double gain = g_agc_target_env / (env + 1e-5);
if (gain < 1.0) gain = 1.0;
if (gain > g_agc_max_gain) gain = g_agc_max_gain;
double y = x * gain * g_agc_makeup;
y = tanh(y * limiter_drive) * limiter_norm;
int32_t out = (int32_t)lrint(y * 32767.0);
if (out > 32767) out = 32767;
if (out < -32768) out = -32768;
g_audio_data[i] = (int16_t)out;
}
printf("Loaded WAV: %u Hz, %u ch, %u bytes, %zu frames\n",
fmt_sample_rate, fmt_channels, data_size, g_audio_len);
return 1;
}
int main(int argc, char* argv[]) {
mach_timebase_info(&timebase_info);
if (argc != 2) {
fprintf(stderr, "Usage: %s input.wav\n", argv[0]);
return 1;
}
g_density_exp = get_env_double("SBR_DENSITY_EXP", 0.55);
g_density_depth = get_env_double("SBR_DENSITY_DEPTH", 0.44);
g_agc_target_env = get_env_double("SBR_AGC_TARGET", 0.36);
g_agc_makeup = get_env_double("SBR_AGC_MAKEUP", 2.6);
g_agc_max_gain = get_env_double("SBR_AGC_MAX_GAIN", 96.0);
g_packet_ms = get_env_double("SBR_PACKET_MS", (double)PACKET_MS);
if (g_density_exp < 0.05) g_density_exp = 0.05;
if (g_density_exp > 1.0) g_density_exp = 1.0;
if (g_density_depth < 0.05) g_density_depth = 0.05;
if (g_density_depth > 0.495) g_density_depth = 0.495;
if (g_agc_target_env < 0.05) g_agc_target_env = 0.05;
if (g_agc_target_env > 0.8) g_agc_target_env = 0.8;
if (g_agc_makeup < 0.2) g_agc_makeup = 0.2;
if (g_agc_makeup > 8.0) g_agc_makeup = 8.0;
if (g_agc_max_gain < 1.0) g_agc_max_gain = 1.0;
if (g_agc_max_gain > 128.0) g_agc_max_gain = 128.0;
if (g_packet_ms < 5.0) g_packet_ms = 5.0;
if (g_packet_ms > 2000.0) g_packet_ms = 2000.0;
if (!load_wav(argv[1])) return 1;
g_mem = malloc(MEM_SIZE);
memset(g_mem, 0xFF, MEM_SIZE);
barrier_init(&g_barrier_start, NUM_THREADS + 1);
barrier_init(&g_barrier_end, NUM_THREADS + 1);
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, worker_thread, (void*)(size_t)i);
}
set_realtime(0);
printf("M1 WAV Player (Stochastic Density). Playing...\n");
printf("Mod params: exp=%.3f depth=%.3f agc_target=%.3f makeup=%.2f max_gain=%.1f packet_ms=%.0f\n",
g_density_exp, g_density_depth, g_agc_target_env, g_agc_makeup, g_agc_max_gain, g_packet_ms);
size_t current_idx = 0;
size_t samples_per_packet = (size_t)(g_sample_rate * g_packet_ms / 1000.0);
while (current_idx < g_audio_len && g_running) {
size_t remaining = g_audio_len - current_idx;
size_t chunk = (remaining > samples_per_packet) ? samples_per_packet : remaining;
g_packet_start_sample = current_idx;
g_packet_num_samples = chunk;
barrier_wait(&g_barrier_start);
barrier_wait(&g_barrier_end);
current_idx += chunk;
printf("\rProgress: %zu / %zu samples", current_idx, g_audio_len);
fflush(stdout);
}
printf("\nDone.\n");
g_running = 0;
barrier_wait(&g_barrier_start);
for (int i = 0; i < NUM_THREADS; i++) pthread_join(threads[i], NULL);
free(g_mem);
free(g_audio_data);
return 0;
}
================================================
FILE: implementations/c-neon-threads/Makefile
================================================
CPPFLAGS=-Wall -O2
main: main.c
.PHONY: clean
clean:
rm -f main
================================================
FILE: implementations/c-neon-threads/main.c
================================================
// SYSTEM BUS RADIO - M1 PULSE-PACKET (CUSTOM BARRIER)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <arm_neon.h>
#include <time.h>
#include <mach/mach_time.h>
#include <mach/thread_policy.h>
#include <mach/mach_init.h>
#include <mach/thread_act.h>
#include <pthread.h>
#include <pthread/qos.h>
#define NUM_THREADS 4
#define MEM_SIZE (64 * 1024 * 1024)
#define PACKET_MS 20
uint8_t *g_mem;
static mach_timebase_info_data_t timebase_info;
volatile int g_running = 1;
// 共有パラメータ
volatile uint64_t g_current_freq = 0;
volatile uint64_t g_current_duration_ms = 0;
// 自作バリア構造体
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int count;
int crossing;
int total;
} my_barrier_t;
my_barrier_t g_barrier_start;
my_barrier_t g_barrier_end;
void barrier_init(my_barrier_t *b, int total) {
pthread_mutex_init(&b->mutex, NULL);
pthread_cond_init(&b->cond, NULL);
b->count = 0;
b->crossing = 0;
b->total = total;
}
void barrier_wait(my_barrier_t *b) {
pthread_mutex_lock(&b->mutex);
b->count++;
if (b->count >= b->total) {
b->crossing++;
b->count = 0;
pthread_cond_broadcast(&b->cond);
} else {
int my_crossing = b->crossing;
while (my_crossing == b->crossing) {
pthread_cond_wait(&b->cond, &b->mutex);
}
}
pthread_mutex_unlock(&b->mutex);
}
static inline void bus_poke(size_t offset) {
uint64_t v1 = 0x5555555555555555ull;
uint64_t v2 = 0xAAAAAAAAAAAAAAAAull;
asm volatile (
"stnp %0, %1, [%2]\n" "stnp %1, %0, [%2, #16]\n"
"stnp %0, %1, [%2, #32]\n" "stnp %1, %0, [%2, #48]\n"
: : "r"(v1), "r"(v2), "r"(&g_mem[offset]) : "memory"
);
}
void set_realtime(int affinity_tag) {
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
thread_affinity_policy_data_t affinity;
affinity.affinity_tag = affinity_tag;
thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY, (thread_policy_t)&affinity, THREAD_AFFINITY_POLICY_COUNT);
}
void* worker_thread(void* arg) {
int id = (int)(size_t)arg;
set_realtime(id + 1);
size_t segment = MEM_SIZE / NUM_THREADS;
size_t offset = id * segment;
size_t end_offset = offset + segment - 64;
size_t current = offset;
while (g_running) {
// 1. 開始合図待ち
barrier_wait(&g_barrier_start);
if (!g_running) break;
uint64_t freq = g_current_freq;
uint64_t duration = g_current_duration_ms;
if (freq > 0) {
uint64_t start = mach_absolute_time();
uint64_t end = start + (duration * 1000000ull * timebase_info.denom / timebase_info.numer);
uint64_t period = (1000000000ull / freq) * timebase_info.denom / timebase_info.numer;
uint64_t half_period = period / 2;
uint64_t now = start;
while (now < end) {
uint64_t next_edge = now + half_period;
// ON
while (mach_absolute_time() < next_edge) {
bus_poke(current);
current += 64;
if (current >= end_offset) current = offset;
}
next_edge += half_period;
// OFF
while (mach_absolute_time() < next_edge) {
asm volatile("yield");
}
now = next_edge;
}
}
// 2. 終了報告
barrier_wait(&g_barrier_end);
}
return NULL;
}
void play_tone(uint64_t freq_hz, uint64_t time_ms) {
uint64_t remaining = time_ms;
while (remaining > 0) {
uint64_t chunk = (remaining > PACKET_MS) ? PACKET_MS : remaining;
g_current_freq = freq_hz;
g_current_duration_ms = chunk;
// START
barrier_wait(&g_barrier_start);
// END WAIT
barrier_wait(&g_barrier_end);
// 短い休憩 (0.5ms)
struct timespec ts = {0, 500000};
nanosleep(&ts, NULL);
remaining -= chunk;
}
}
int main(int argc, char* argv[]) {
mach_timebase_info(&timebase_info);
if (argc != 2) {
fprintf(stderr, "Usage: %s file.tune\n", argv[0]);
return 1;
}
FILE* fp = fopen(argv[1], "r");
if (!fp) { perror("fopen"); return 1; }
g_mem = malloc(MEM_SIZE);
memset(g_mem, 0, MEM_SIZE);
barrier_init(&g_barrier_start, NUM_THREADS + 1);
barrier_init(&g_barrier_end, NUM_THREADS + 1);
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, worker_thread, (void*)(size_t)i);
}
set_realtime(0);
printf("M1 Pulse-Packet Mode (Custom Barrier). Playing...\n");
char buffer[128];
int freq_hz, time_ms;
while (1) {
if (fgets(buffer, sizeof(buffer), fp) == NULL) {
if (feof(fp)) {
rewind(fp);
continue;
}
break;
}
if (buffer[0] == '\n' || buffer[0] == '#' || buffer[0] == ' ') continue;
if (sscanf(buffer, "%d %d", &freq_hz, &time_ms) == 2) {
printf("\r%4d Hz ", freq_hz);
fflush(stdout);
play_tone(freq_hz, time_ms);
}
}
g_running = 0;
barrier_wait(&g_barrier_start); // 待機中のスレッドを解放
for (int i = 0; i < NUM_THREADS; i++) pthread_join(threads[i], NULL);
fclose(fp);
return 0;
}
================================================
FILE: implementations/cpp-counter-threads/Makefile
================================================
all : gmain cmain
clean :
rm -f gmain
rm -f cmain
grun : gmain
./gmain
crun : cmain
./cmain
gmain : main.cpp
g++ -Wall -O2 -std=c++11 -pthread -lrt -o gmain main.cpp
cmain : main.cpp
clang++ -Wall -O2 -std=c++11 -stdlib=libc++ -pthread -lrt -o cmain main.cpp
.PHONY : all clean run
================================================
FILE: implementations/cpp-counter-threads/main.cpp
================================================
// SYSTEM BUS RADIO
// https://github.com/fulldecent/system-bus-radio
// Copyright 2016 William Entriken
// C++11 port by Ryou Ezoe
#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <cstdio> // For FILE, fopen, fgets, etc.
#include <cstdlib> // For exit()
std::mutex m;
std::condition_variable cv;
std::chrono::high_resolution_clock::time_point mid;
std::chrono::high_resolution_clock::time_point reset;
void boost_song() {
using namespace std::chrono;
while (true) {
std::unique_lock<std::mutex> lk{m};
cv.wait(lk);
std::atomic<unsigned> x{0};
while (high_resolution_clock::now() < mid) {
++x;
}
std::this_thread::sleep_until(reset);
}
}
void square_am_signal(float frequency, float time) {
using namespace std::chrono;
std::cout << "Playing / " << time << " seconds / " << frequency << " Hz\n";
seconds const sec{1};
nanoseconds const nsec{sec};
using rep = nanoseconds::rep;
auto nsec_per_sec = nsec.count();
nanoseconds const period(static_cast<rep>(nsec_per_sec / frequency));
auto start = high_resolution_clock::now();
auto const end = start + nanoseconds(static_cast<rep>(time * nsec_per_sec));
while (high_resolution_clock::now() < end) {
mid = start + period / 2;
reset = start + period;
cv.notify_all();
std::this_thread::sleep_until(reset);
start = reset;
}
}
int main(int argc, char* argv[]) {
// Launch as many threads as supported by hardware
for (unsigned i = 0; i < std::thread::hardware_concurrency(); ++i) {
std::thread t(boost_song);
t.detach();
}
// File reading logic
FILE* fp;
if (argc == 2) {
fp = fopen(argv[1], "r");
if (!fp) {
perror("Error opening file");
exit(1);
}
} else {
std::cerr << "No song file given!\nUsage: " << argv[0] << " file.song\n";
exit(1);
}
char buffer[64] = {0}; // Buffer for reading lines from file
int freq_hz;
int time_ms;
while (true) {
if (fgets(buffer, sizeof(buffer) - 1, fp)) {
if (sscanf(buffer, "%d %d", &freq_hz, &time_ms) == 2) {
square_am_signal(1.0 * time_ms / 1000, freq_hz); // Convert ms to seconds
}
}
// Handle end of file and rewind for looping the song
if (feof(fp)) {
rewind(fp);
}
}
fclose(fp); // Close the file (though unreachable in this infinite loop)
return 0;
}
================================================
FILE: tests/check-test-data-tabs.sh
================================================
#!/bin/bash
# Ensure every line has the same number of tabs
HISTOGRAM=$(cat TEST-DATA.tsv | tr -cd '\t\n' | tr '\t' '.' | sed '${/^$/d;}' | sort | uniq -c)
echo $HISTOGRAM
if [ $(echo $HISTOGRAM | wc -w) -eq 2 ]; then
echo "✅ All lines have the same number of tabs"
else
echo "❌ Not all lines have the same number of tabs"
exit 1
fi
================================================
FILE: tunes/README.md
================================================
# Tune File Format
This file defines the `.tune` music file format.
Following is a simple example of the beginning parts of the *Super Mario Brothers* theme song:
```
660 100
0 150
660 100
0 300
660 100
0 300
510 100
```
## Full specification
1. Each line in the file is `<frequency> <duration>`
1. `<frequency>` is the frequency of the beep in Hz or `0` for silence
2. `<duration>` is the duration of the beep in milliseconds
2. Line ending is unix format
3. File extension is `.tune`
4. Although not necessarily part of the tune, consider adding a silence at the end so that looped playback sounds good :-)
Note: this is compatible with the [GRUB_INIT_TUNE](https://www.gnu.org/software/grub/manual/grub/grub.html#play) when using a `TEMPO` of 60,000 (bpm).
================================================
FILE: tunes/mary_had_a_little_lamb.tune
================================================
2673 400
2349 400
2093 400
2349 400
2673 400
2673 400
2673 790
2349 400
2349 400
2349 790
2673 400
3136 400
3136 790
2673 400
2349 400
2093 400
2349 400
2673 400
2673 400
2673 400
2673 400
2349 400
2349 400
2673 400
2349 400
2093 790
0 400
================================================
FILE: tunes/morse_code_sos.tune
================================================
1000 200
0 200
1000 200
0 200
1000 200
0 200
1000 600
0 200
1000 600
0 200
1000 600
0 200
1000 200
0 200
1000 200
0 200
1000 200
0 200
0 1400
================================================
FILE: tunes/smb.tune
================================================
660 100
0 150
660 100
0 300
660 100
0 300
510 100
0 100
660 100
0 300
770 100
0 550
380 100
0 575
510 100
0 450
380 100
0 400
320 100
0 500
440 100
0 300
480 80
0 330
450 100
0 150
430 100
0 300
380 100
0 200
660 80
0 200
760 50
0 150
860 100
0 300
700 80
0 150
760 50
0 350
660 80
0 300
520 80
0 150
580 80
0 150
480 80
0 500
510 100
0 450
380 100
0 400
320 100
0 500
440 100
0 300
480 80
0 330
450 100
0 150
430 100
0 300
380 100
0 200
660 80
0 200
760 50
0 150
860 100
0 300
700 80
0 150
760 50
0 350
660 80
0 300
520 80
0 150
580 80
0 150
480 80
0 500
500 100
0 300
760 100
0 100
720 100
0 150
680 100
0 150
620 150
0 300
650 150
0 300
380 100
0 150
430 100
0 150
500 100
0 300
430 100
0 150
500 100
0 100
570 100
0 220
500 100
0 300
760 100
0 100
720 100
0 150
680 100
0 150
620 150
0 300
650 200
0 300
1020 80
0 300
1020 80
0 150
1020 80
0 300
380 100
0 300
500 100
0 300
760 100
0 100
720 100
0 150
680 100
0 150
620 150
0 300
650 150
0 300
380 100
0 150
430 100
0 150
500 100
0 300
430 100
0 150
500 100
0 100
570 100
0 420
585 100
0 450
550 100
0 420
500 100
0 360
380 100
0 300
500 100
0 300
500 100
0 150
500 100
0 300
500 100
0 300
760 100
0 100
720 100
0 150
680 100
0 150
620 150
0 300
650 150
0 300
380 100
0 150
430 100
0 150
500 100
0 300
430 100
0 150
500 100
0 100
570 100
0 220
500 100
0 300
760 100
0 100
720 100
0 150
680 100
0 150
620 150
0 300
650 200
0 300
1020 80
0 300
1020 80
0 150
1020 80
0 300
380 100
0 300
500 100
0 300
760 100
0 100
720 100
0 150
680 100
0 150
620 150
0 300
650 150
0 300
380 100
0 150
430 100
0 150
500 100
0 300
430 100
0 150
500 100
0 100
570 100
0 420
585 100
0 450
550 100
0 420
500 100
0 360
380 100
0 300
500 100
0 300
500 100
0 150
500 100
0 300
500 60
0 150
500 80
0 300
500 60
0 350
500 80
0 150
580 80
0 350
660 80
0 150
500 80
0 300
430 80
0 150
380 80
0 600
500 60
0 150
500 80
0 300
500 60
0 350
500 80
0 150
580 80
0 150
660 80
0 550
870 80
0 325
760 80
0 600
500 60
0 150
500 80
0 300
500 60
0 350
500 80
0 150
580 80
0 350
660 80
0 150
500 80
0 300
430 80
0 150
380 80
0 600
660 100
0 150
660 100
0 300
660 100
0 300
510 100
0 100
660 100
0 300
770 100
0 550
380 100
0 575
gitextract_qt828ii7/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── RTL-SDR-GUIDE.md
├── TEST-DATA.tsv
├── docs/
│ ├── .gitignore
│ ├── Gemfile
│ ├── airgap.js
│ ├── index.html
│ ├── main.css
│ └── worker.js
├── implementations/
│ ├── c-_mm_stream_si128/
│ │ ├── Makefile
│ │ └── main.c
│ ├── c-apple-silicon/
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── main.c
│ ├── c-apple-silicon-wav/
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── gen_sweep.c
│ │ └── main.c
│ ├── c-neon-threads/
│ │ ├── Makefile
│ │ └── main.c
│ └── cpp-counter-threads/
│ ├── Makefile
│ └── main.cpp
├── tests/
│ └── check-test-data-tabs.sh
└── tunes/
├── README.md
├── mary_had_a_little_lamb.tune
├── morse_code_sos.tune
└── smb.tune
SYMBOL INDEX (40 symbols across 8 files)
FILE: docs/airgap.js
function start (line 3) | function start() { // Start Web Worker & send song data to player
function end (line 24) | function end() { // Stops the Web Worker
FILE: docs/worker.js
function now (line 7) | function now() {
function square_am_signal (line 14) | function square_am_signal(freq, time) { // This function generates the r...
function play (line 32) | function play(song) { // Parse song data, and call on required scripts t...
function pause (line 51) | function pause(time) { // A useless function to run when there is no noi...
FILE: implementations/c-_mm_stream_si128/main.c
type mach_timespec_t (line 21) | typedef struct timespec mach_timespec_t;
type mach_port_t (line 22) | typedef unsigned int mach_port_t;
function mach_absolute_time (line 24) | static inline uint64_t mach_absolute_time(void) {
function clock_sleep_trap (line 37) | static inline void clock_sleep_trap(mach_port_t clock_port, int sleep_ty...
function square_am_signal (line 53) | static inline void square_am_signal(float frequency, float time) {
function main (line 72) | int main(int argc, char* argv[])
FILE: implementations/c-apple-silicon-wav/gen_sweep.c
function main (line 12) | int main() {
FILE: implementations/c-apple-silicon-wav/main.c
type my_barrier_t (line 41) | typedef struct {
function barrier_init (line 52) | void barrier_init(my_barrier_t *b, int total) {
function barrier_wait (line 60) | void barrier_wait(my_barrier_t *b) {
function bus_poke (line 76) | static inline void bus_poke(size_t offset) {
function set_realtime (line 85) | void set_realtime(int affinity_tag) {
function read_le16 (line 152) | static uint16_t read_le16(const uint8_t* p) {
function read_le32 (line 156) | static uint32_t read_le32(const uint8_t* p) {
function get_env_double (line 163) | static double get_env_double(const char* name, double default_value) {
function load_wav (line 172) | int load_wav(const char* filename) {
function main (line 381) | int main(int argc, char* argv[]) {
FILE: implementations/c-apple-silicon/main.c
type my_barrier_t (line 28) | typedef struct {
function barrier_init (line 39) | void barrier_init(my_barrier_t *b, int total) {
function barrier_wait (line 47) | void barrier_wait(my_barrier_t *b) {
function bus_poke (line 63) | static inline void bus_poke(size_t offset) {
function set_realtime (line 73) | void set_realtime(int affinity_tag) {
function play_tone (line 127) | void play_tone(uint64_t freq_hz, uint64_t time_ms) {
function main (line 149) | int main(int argc, char* argv[]) {
FILE: implementations/c-neon-threads/main.c
type my_barrier_t (line 28) | typedef struct {
function barrier_init (line 39) | void barrier_init(my_barrier_t *b, int total) {
function barrier_wait (line 47) | void barrier_wait(my_barrier_t *b) {
function bus_poke (line 63) | static inline void bus_poke(size_t offset) {
function set_realtime (line 73) | void set_realtime(int affinity_tag) {
function play_tone (line 127) | void play_tone(uint64_t freq_hz, uint64_t time_ms) {
function main (line 149) | int main(int argc, char* argv[]) {
FILE: implementations/cpp-counter-threads/main.cpp
function boost_song (line 21) | void boost_song() {
function square_am_signal (line 35) | void square_am_signal(float frequency, float time) {
function main (line 59) | int main(int argc, char* argv[]) {
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (73K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 163,
"preview": "# These are supported funding model platforms\n\ngithub: [fulldecent]\ncustom: [\"https://www.paypal.me/fulldecent\", \"https:"
},
{
"path": ".github/workflows/tests.yml",
"chars": 326,
"preview": "name: Check Lines Script\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\njobs:\n run"
},
{
"path": ".gitignore",
"chars": 270,
"preview": "# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared object"
},
{
"path": "LICENSE",
"chars": 1083,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 William Entriken\n\nPermission is hereby granted, free of charge, to any person "
},
{
"path": "README.md",
"chars": 6667,
"preview": "# System Bus Radio\n\nThis program transmits radio on computers / phones without radio transmitting hardware.\n\n## Thank yo"
},
{
"path": "RTL-SDR-GUIDE.md",
"chars": 5452,
"preview": "# RTL STR Basic Setup\n\n## Test subject\n\nFor this guide, we are inspecting a **MacBook Pro M1 (13-inch, 2020)** for elect"
},
{
"path": "TEST-DATA.tsv",
"chars": 6076,
"preview": "Date\tTester\tTransmitter\tReceiver\tSoftware\tFrequency\tResult\tRecording\r\n2016-03-01\tWilliam Entriken\tMacBook Air (13-inch, "
},
{
"path": "docs/.gitignore",
"chars": 20,
"preview": "/_site\nGemfile.lock\n"
},
{
"path": "docs/Gemfile",
"chars": 73,
"preview": "source 'https://rubygems.org'\ngem 'github-pages', group: :jekyll_plugins\n"
},
{
"path": "docs/airgap.js",
"chars": 796,
"preview": "var player; // Define \"player\" var to make my code linter happy\n\nfunction start() { // Start Web Worker & send song data"
},
{
"path": "docs/index.html",
"chars": 2098,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, in"
},
{
"path": "docs/main.css",
"chars": 1653,
"preview": "/* Main style */\nbody {\n background-color: #3498db;\n color: white;\n font-family: Futura, Helvetica, \"Century Gothic\";"
},
{
"path": "docs/worker.js",
"chars": 1543,
"preview": "//Credits to https://github.com/fulldecent/system-bus-radio\n//As well as Jordan Harband for the nodejs simd library\n//Te"
},
{
"path": "implementations/c-_mm_stream_si128/Makefile",
"chars": 75,
"preview": "CPPFLAGS=-Wall -O2 -msse2\n\nmain: main.c\n\n.PHONY: clean\n\nclean:\n\trm -f main\n"
},
{
"path": "implementations/c-_mm_stream_si128/main.c",
"chars": 3058,
"preview": "// SYSTEM BUS RADIO\n// https://github.com/fulldecent/system-bus-radio\n// Copyright 2016 William Entriken\n\n#include <stdi"
},
{
"path": "implementations/c-apple-silicon/Makefile",
"chars": 68,
"preview": "CPPFLAGS=-Wall -O2\n\nmain: main.c\n\n.PHONY: clean\n\nclean:\n\trm -f main\n"
},
{
"path": "implementations/c-apple-silicon/README.md",
"chars": 1807,
"preview": "# System Bus Radio for Apple Silicon (M1/M2/M3)\n\nThis implementation is specifically optimized for Apple Silicon (M1/M2/"
},
{
"path": "implementations/c-apple-silicon/main.c",
"chars": 5592,
"preview": "// SYSTEM BUS RADIO - M1 PULSE-PACKET TRANSMITTER (4 CORES)\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#"
},
{
"path": "implementations/c-apple-silicon-wav/Makefile",
"chars": 68,
"preview": "CPPFLAGS=-Wall -O2\n\nmain: main.c\n\n.PHONY: clean\n\nclean:\n\trm -f main\n"
},
{
"path": "implementations/c-apple-silicon-wav/README.md",
"chars": 2173,
"preview": "# c-apple-silicon-wav\n\nThis is a **delta implementation** based on the Apple Silicon version in\n`implementations/c-apple"
},
{
"path": "implementations/c-apple-silicon-wav/gen_sweep.c",
"chars": 1650,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <math.h>\n\n#define SAMPLE_RATE 24000\n#define DURATION"
},
{
"path": "implementations/c-apple-silicon-wav/main.c",
"chars": 14875,
"preview": "// SYSTEM BUS RADIO - M1 WAV PLAYER (STOCHASTIC DENSITY MODULATION)\n// Uses probability to modulate bus density, achievi"
},
{
"path": "implementations/c-neon-threads/Makefile",
"chars": 68,
"preview": "CPPFLAGS=-Wall -O2\n\nmain: main.c\n\n.PHONY: clean\n\nclean:\n\trm -f main\n"
},
{
"path": "implementations/c-neon-threads/main.c",
"chars": 5478,
"preview": "// SYSTEM BUS RADIO - M1 PULSE-PACKET (CUSTOM BARRIER)\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#inclu"
},
{
"path": "implementations/cpp-counter-threads/Makefile",
"chars": 296,
"preview": "all : gmain cmain\n\nclean :\n\trm -f gmain\n\trm -f cmain\n\ngrun : gmain\n\t./gmain\n\ncrun : cmain\n\t./cmain\n\n\ngmain : main.cpp\n\tg"
},
{
"path": "implementations/cpp-counter-threads/main.cpp",
"chars": 2643,
"preview": "// SYSTEM BUS RADIO\n// https://github.com/fulldecent/system-bus-radio\n// Copyright 2016 William Entriken\n// C++11 port b"
},
{
"path": "tests/check-test-data-tabs.sh",
"chars": 341,
"preview": "#!/bin/bash\n\n# Ensure every line has the same number of tabs\n\nHISTOGRAM=$(cat TEST-DATA.tsv | tr -cd '\\t\\n' | tr '\\t' '."
},
{
"path": "tunes/README.md",
"chars": 772,
"preview": "# Tune File Format\n\nThis file defines the `.tune` music file format.\n\nFollowing is a simple example of the beginning par"
},
{
"path": "tunes/mary_had_a_little_lamb.tune",
"chars": 240,
"preview": "2673 400\n2349 400\n2093 400\n2349 400\n2673 400\n2673 400\n2673 790\n2349 400\n2349 400\n2349 790\n2673 400\n3136 400\n3136 790\n267"
},
{
"path": "tunes/morse_code_sos.tune",
"chars": 142,
"preview": "1000 200\n0 200\n1000 200\n0 200\n1000 200\n0 200\n1000 600\n0 200\n1000 600\n0 200\n1000 600\n0 200\n1000 200\n0 200\n1000 200\n0 200\n"
},
{
"path": "tunes/smb.tune",
"chars": 2140,
"preview": "660 100\n0 150\n660 100\n0 300\n660 100\n0 300\n510 100\n0 100\n660 100\n0 300\n770 100\n0 550\n380 100\n0 575\n510 100\n0 450\n380 100\n"
}
]
About this extraction
This page contains the full source code of the fulldecent/system-bus-radio GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (66.1 KB), approximately 21.3k tokens, and a symbol index with 40 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.