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 Listen to online radio - ## 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: 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 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). > > 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 * Declared * Definition * clock_get_time() gives a mach_timespec_t time * Called from mach_absolute_time() * mach_timespec_t * Type documentation * Declaration * * * Sleep * mach_wait_until() * Notes * nanosleep() * Apple doc * Definition * clock_sleep_trap() * Used from nanosleep() * Declared * Definition * Uses clock_sleep_internal() * Uses ADD_MACH_TIMESPEC * clock type constants * 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() * time/timer.c / * kern/clock.h ## Press coverage * * * ================================================ 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 ``` ![RF test setup](https://user-images.githubusercontent.com/382183/115773678-98d16a80-a37e-11eb-9f4b-4dabb9d401a2.jpg) 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 ================================================ System Bus Radio

System Bus Radio

Tested on MacBook Air / Chrome with AM tuner at 1560 kHz. See field reports for other equipment and frequencies.

Edit the above to make any music you like. Tune file format is frequency (Hz) and time (ms).

Chrome has errors if you open this file locally (file://). Try using php -S localhost:8000 or similar for a quick web server.

Ported by Yeo Quan Yang & maintained by Elliot Gerchak.

Original machine code by William Entriken.

Project site at https://github.com/fulldecent/system-bus-radio

================================================ 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 #include #include #include #include #ifdef __MACH__ #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #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 #include #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #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 #include #include #include #include #include #include #include // For FILE, fopen, fgets, etc. #include // 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 lk{m}; cv.wait(lk); std::atomic 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(nsec_per_sec / frequency)); auto start = high_resolution_clock::now(); auto const end = start + nanoseconds(static_cast(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 ` ` 1. `` is the frequency of the beep in Hz or `0` for silence 2. `` 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