Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

audio-network

robertrypula62MIT1.3.2

Data transmission over sound waves written in JavaScript without any dependencies. All you need is just microphone, speakers and the browser!

transmission over sound, Web Audio API, audio, network, sound, waves, transmit, receive, binary data, PSK, QPSK, BPSK, DFT, OFDM, phase shift keying, discrete fourier transform, orthogonal frequency division multiplexing, modem

readme

Audio Network

Data transmission over sound waves written in <strike>JavaScript</strike>TypeScript without any dependencies. All you need is just microphone, speakers and the browser!

News

  • 2018 May 11 - PR#4 was merged with master. Important: migration process from ES5 to TypeScript is still NOT finished. It will just continue on the master branch.

  • 2018 May 10 - Thanks to Programista magazine, all 3 parts of my articles about data transmission over sound in JavaScript are now available as PDFs. Please find links in the next section.

  • 2018 Feb 06 - Project looks like dead but it's not. Now I'm focusing on configuration that allows me to move entire AudioNetwork project to TypeScript, WebPack, Jest unit test with coverge, Travis etc.

Self-made network stack based on Frequency-Shift Keying (>= v1.2.0)

Audio Network library is a hobby project in which the goal was to create simple network stack that uses sound waves to transmit data. Initialy it was using PSK modulation technique which was later changed into FSK.

Brief introduction to the topic is available as presentation hosted on slides.com here.

If you are interested in details you can read more in polish Programista magazine:

Part 1 Part 2 Part 3

Part 3 of the article was the last one. It means that more focus will go to this website and Audio Network releases. If you can't wait please go trought some of the examples:

Full list of examples is available here:

Full example list

Note: some of the modules are not yet in main NPM package. Please verify roadmap.

This library is supported by PGS Software - the place where I work as Frontend Developer

Deprecated Phase-Shift Keying approach (last update at v1.1.0, will be removed at v2.0.0)

Initial versions of this lib were based on Phase-Shift Keying modulation technique. Unfortunatelly after some tests in turned out that this implementation was not working reliable on mobile devices. Starting from v1.2.0 this part of the lib will be marked as deprecated.

!!! IMPORTANT NOTE !!! - Everything described below (up to Licence) might be outdated or not working properly. Project is now in a phase of big technical changes. Entire v1.x was created mostly to learn DSP and share this knowledge in the 'Programista' magazine articles. Version 2.x will have more stable and simpler API and will be better consumable as npm package.

If you want to try AudioNetwork by yourself I would recommend to first open Demo - simple because Demo - full features might be little scary for the first time. You can also watch YouTube video that shows how to transmit data over sound between two laptops:

Data transmission over sound waves (AudioNetwork)

How can I transmit something over sound?

First of all you need to have two devices. One for sending, one for receiving data. It should work with any device (PC, Mac, tablet, smartphone) with browser that supports Web Audio API. Next you need to open Demo - simple at both devices and follow steps below:

  1. [Receiver] - Before you load demo page you need to be quiet :) Receiving device needs to listen to 'silence' around you when there is no signal in the air. It's indicated by IDLE_INIT state and it ends when FIRST_SYNC_WAIT state will come. If you fell that your silence wasn't good enough you can always use RESET button to start again.

  2. [Transmitter] - All you need to do is to click on SYNC button.

  3. [Receiver] - When SYNC transmission is in progress state should change to FIRST_SYNC. After about 2 seconds it should switch to IDLE. At this point we are ready to send some data!

  4. [Transmitter] - Now you can put some data to textarea and click on Send packet button. After short moment your data will appear on the receiver's side.

By default PSK-2 modulation is used so you can use only symbols 0 and 1 but it's enough to send data bit by bit. For example to send ASCII a character (0x61) you need to put inside textarea 0 1 1 0 0 0 0 1. Please remember to always put one space between each symbol but whole text needs to be without trailing and leading spaces.

If at any point you will see FATAL_ERROR state you need to click on RESET button and start all points again.

AudioNetwork needs raw microphone output without any filter applied. You can verify that by opening Sound settings in your operating system. When you enter your microphone properties you might need to un-check all filters. The worst scenario is when your microphone has some hardware filter that you can't disable. In this case you need to throw it away and buy a new one ;)

How to add sound transmission to my project?

Audio Network is available at GitHub and npm so you can just run one of the commands below:

git clone https://github.com/robertrypula/AudioNetwork.git
cd AudioNetwork
npm install
gulp serve

or

npm install audio-network

In both cases at build directory you will find minified and unminified js file with whole library. Pick one and include it into your HTML file. For example:

<script src="node_modules/audio-network/build/audio-network-v1.3.1.min.js"></script>

Now you can access AudioNetwork object at global JavaScript scope. It's the entry point for all components:

var physicalLayer, transmitAdapter, receiveAdapter;

physicalLayer = new AudioNetwork.PhysicalLayer.PhysicalLayer({
    // config
});
transmitAdapter = new AudioNetwork.PhysicalLayerAdapter.TransmitAdapter(physicalLayer);
receiveAdapter = new AudioNetwork.PhysicalLayerAdapter.ReceiveAdapter(physicalLayer);

Below you can find Demo - simple source code:

To work properly Web Audio API requires running your HTML file via web server (http://localhost...) like Apache or some Node stuff like gulp-webserver (I'm using it and it works great). In case of local machine normal http connection would work. Unfortunately when you will want to go live you have to provide https. It's because in some browsers accessing microphone is not allowed when site is not hosted over https. You can read more about this here

In case you are curious about long namespaces: Under the hood AudioNetwork works on simple Injector implementation. For example AudioNetwork.PhysicalLayer.PhysicalLayer is just alias for AudioNetwork.Injector.resolve('PhysicalLayer.PhysicalLayer'). Other public classes/services that have aliases you can find here

More about full features demo

Full feature demo is growing with AudioNetwork library core. It's developer sandbox for all new features.

Demo is divided into two sections - Receive and Transmit (blue bar with white text). You can show or hide stuff by clicking on View type buttons at Initialize section.

Receive section

Here you can find lots of information like input spectrum, constellation diagrams, power chart.

At Receive section you can also check current status of ReceiveAdapter.

ReceiveAdapter is wrapper for lower level PhysicalLayer's object rx events.

The most important thing in this adapter is its state. You can find it at the top of the orange box (capital letters text like IDLE_INIT, FIRST_SYNC_WAIT, etc). Other important area to look is output of RX adapter - PACKETS. Here you can find any potential packet that will be found in the air. I wrote potential because integrity of incoming packets is not verified so some of them might be corrupted. It's because it's not PhysicalLayer responsibility to deal with it. In some future release of AudioNetwork packet verification will be added. This would be in next layer in the network stack - DataLinkLayer (like in OSI model).

Orange box is kind of developer debug box - it will be replaced to some nicer UX in the future...

Transmit section

At Transmit section there is not that much as at Receive section. You will find there only few buttons to send SYNC signal, PACKET and individual symbols (0, 1, ...). Number of unique symbols is determined by PSK symbol size (you need to switch view type to Complex to see that). At Packet textarea please remember to always put one space between each symbol but whole text needs to be without trailing and leading spaces.

Features

  • Dependency free library
  • Unit test friendly. Build-in Dependency Injector (tests are planned in the future)
  • Multiple channel support for parallel transmission without collisions
  • OFDM (Orthogonal Frequency-Division Multiplexing) support per each channel. This helps to save bandwidth and pack more data into one burst. It's the same technique as used in many existing standards like LTE, Wi-Fi, DAB, DVB.
  • Constellation Diagram, that helps to easily verify phase and amplitude of carrier frequency in the realtime.
  • Spectrum Analyzer of incoming signal is based on AnalyserNode for Web Audio API.
  • Adapter classes (ReceiveAdapter and TransmitAdapter) that acts as easy-to-use wrappers over raw PhysicalLayer class with lower level tx and rx methods. If you want you can also write your own adapter class and attach PhysicalLayer to it. AudioNetwork is shipped with PSK adapters but you can write any other modulation like PWM.
  • ReceiveAdapter internally has state machine with states listed here. The idea is to extract potential packets from raw carrier details data that rx method provides.
  • Auto phase correction. PSK modulation is ultra sensitive to frequency offsets. Even if transmitter and receiver frequencies are de-tuned by 0.05 Hz it will rotate symbol at constellation diagram every 20 seconds. It means that we will not be sure what symbol we are reading. Fortunately Adapter classes adds SyncPreamble (symbol with zero phase offset relative to reference) before each packet. ReceiveAdapter takes that SyncPreamble and restores symbol alignment before each packet. During packet transmission it may still rotate but at least at the beginning of the packet symbols are aligned at both sides - sender and receiver
  • Auto frequency correction. Similar to point above. PSK is ultra sensitive to frequency offsets so basically we need to fine-tune receiver to sender. This feature looks how fast constellation point rotates and corrects receiver's reference carrier frequency to minimize this effect.
  • Auto detection of signal and noise levels. ReceiveAdapter after reset first listens to ambient noise to set averageIdlePower (during IDLE_INIT state). Then it waits for sender for synchronization signal which is basically symbol zero transmitted for few seconds. During sync transmission receiver stores averageFirstSyncPower at FIRST_SYNC state. After those steps receiver calculates powerThreshold and it's ready for packets from sender. All above is needed to propperly determine signal and noise power levels. Without it we will not be able to decode packet.

Known limitations

  • Currently Guard Interval needs to be few times longer than Symbol Duration (two or tree times longer). It's because ReceiveAdapter, when restoring packet symbols, is 'clocked' by carrier frequency power changes.

Few more words about the project

Internally AudioNetwork library uses Web Audio API for retrieving audio data from the microphone and sending audio data to the speakers. To be more precise it is using ScriptProcessorNode. ScriptProcessorNode fires event when new portion of samples arrives (from mic) or new portion of samples is required (for speakers). Basically AudioNetwork library is analysing/providing raw arrays of sound samples inside audio events handlers. In other words all DSP is performed by internal AudioNetwork's classes or services written in pure JavaScript.

Apart from ScriptProcessorNode two other Web Audio API nodes were used. First was GainNode for connecting other nodes together and the second was AnalyserNode. AnalyserNode was used only to show some 'nice' visual stuff to the user. We can switch between time domain (to see raw samples) and frequency domain (spectrum analyzer like on FM radio display). It's optional and not required when we want to just send or receive data.

If somebody is familiar with Web Audio API one question may now come - why I didn't use OscillatorNode for sound generation or AnalyzerNode's FrequencyData output for detecting carrier frequencies? Well... I could, but the main goal for this project was to use PSK (Phase Shift Keying) modulation to send/receive data and learn a bit about modern wireless communication techniques. Those two goals even forced me to write everything from scratch because AnalyserNode's frequency domain data output (it is using FFT - Fast Fourier Transform) provides only amplitude information without any phase information. It turned out that the only way to achieve PSK modulation was to implement own Discrete Fourier Transform with amplitude/phase or use some DSP library. I wanted to dig into the details so I picked the first option :) Wikipedia about DFT looks ultra scary but in the code there is no rocket science. Basic implementation of Discrete Fourier Transform is very simple. We will not find there any mathematical tricks like used in FFT. If you don't believe me, check this class CarrierRecovery.

There's no rose without a thorn... CarrierRecovery class code is simple but when we would like to create more of its instances, to have more carrier frequencies, we will reduce overall performance pretty fast. It is not good but at least it's closer to KISS design principle ;)

Licence

The MIT License (MIT)

Copyright (c) 2015-2018 Robert Rypuła - https://audio-network.rypula.pl

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.

changelog

Roadmap

  • 1.1.0 Last update for old PSK code
  • 1.2.x Completely rewritten Physical Layer with FSK support. Old PSK code marked as deprecated
  • 1.3.x Data link layer with frames and checksums
  • 1.4.x Transport layer that supports simplified TCP protocol
  • 1.5.x Application layer with simple text exchange protocol
  • 1.x.x move to webpack
  • 2.0.0 Old PSK code removed from the project.
  • 2.x.x ?? OFDM
  • 2.x.x ?? Move to TypeScript

v1.3.2 (2018-05-16)

  • BugFix for the same issue in other area: "The AudioContext was not allowed to start. It must be resume (or created) after a user gesture on the page. https://goo.gl/7K7WLu"

v1.3.1 (2018-05-10)

  • BugFix: "The AudioContext was not allowed to start. It must be resume (or created) after a user gesture on the page. https://goo.gl/7K7WLu"

v1.3.0 (2018-01-02)

  • adds Data Link Layer to the network stack
  • up to 7 bytes of data can travel inside frame with header and checksum (Fletcher-8)
  • frames tranmission progress listener
  • incomming frames listener
  • incomming frame candidates listener
  • 'TwoWaySync' that helps to exchange 'Sync' sequences and SampleRate values (this will be simplified even more in future versions)

v1.2.0 (2017-12-28)

  • completely rewritten Physical Layer core based on Frequency-Shift Keying
  • special "sync" sequence for symbol synchronization based on correlation technique
  • many new examples
  • old FSK code marked as deprecated
  • volume control issue on mobile devices solved by detaching from microphone
  • Injector aliases again enabled by default

v1.1.0 (2017-11-12) Last update for old PSK code

  • Dynamic dev files loading
  • Boot config added
  • Env variables added (isBrowser, isNode, isWebWorker)
  • NodeJs export added - you can now use AudioNetwork in NodeJs applications!
  • ActiveAudioContext abstraction
  • CarrierGenerate and CarrierRecovery refactor
  • Constellation diagram: do not redraw constellation if queue wasn't changed
  • Constellation diagram: queue elements approach changed (only one item per constellation point now)
  • WindowFunction service with blackmanNuttall method
  • Visualizer abstract classes (Abstract2DVisualizer, AbstractVisualizer)
  • Visualizer code refactor
  • rename audio-network-begin to audio-network-boot
  • WebWorker basic functionality added

v1.0.4 (2016-07-04)

  • new example where only CarrierRecovery/CarrierGenerate were used to send data using Amplitude Modulation (without PhysicalLayer module)
  • expand class names - this allows easier debug in dev tools (for example CTM is now ChannelTransmitManager)
  • introduce SimplePromise class (idea is to have code as less dependent to browser as possible in order to port it easily to other languages)
  • move all charts to dedicated namespace 'visualizer'
  • move CarrierRecovery and CarrierGenerate to 'common' namespace
  • move audio service to separate namespace 'audio' and change name of the service to ActiveAudioContext
  • move adapter classes to 'physical-layer-adapter'
  • move current audio service logic to factory and connect it to ActiveAudioContext
  • AudioContext class was renamed to SimpleAudioContext in order to avoid collision with real window.AudioContext
  • addToQueue is now taking object as a parameter instead array of objects
  • Jasmine unit test web runner added (currently still without tests but at least running environment was prepared)
  • rename example directories (000 for basic demos, 100 for physical layer demos, ...)
  • add htaccess redirection to renamed examples
  • create new chart 'SampleChart', create new example where CarrierGenerate will be demonstrated

v1.0.3 (2016-05-30)

  • fix transition from FIRST_SYNC_INIT -> IDLE, currently there are some SYMBOL/GUARD states which are not right at this point
  • default settings update (symbol duration vs guard interval, sync duration reduced from 3 sec to 2 sec, notification per seconds increased)
  • gulp 'serve' task for serving the app locally
  • add YouTube movie with demo
  • move common css away of example
  • readme moved to index.html, styles and pages structure refactored
  • simple demo added
  • updates at README.md
  • added CHANGELOG.md
  • licence moved to MIT
  • SEO updates to whole page

v1.0.2 (2016-05-16)

  • huge readme update, npm keywords update

v1.0.1 (2016-05-10)

  • Carrier example bug fix. Versioning information added for main example

v1.0.0 (2016-05-10)

  • fix typo: physial-layer into physical-layer
  • keep view type after reinitialization
  • remove audio-network prefix from main classes names
  • change name: dftTimeSpan -> dftWindowTimeSpan
  • move general configuration to some common service
  • refactor NO_SIGNAL state
  • change input TX to LOOPBACK
  • move templates code to dedicated files
  • introduce Dependency Injection
  • prepare release version + some code minification
  • measure CPU load by measuring times before and after execution

Finished stuff before release was even planned

  • BUG FIXED for some dftWindowTime after converting it to numbers of samples CarrierRecovery queue size is fractional
  • real/imm delete
  • compute at getCarrierDetail
  • index passed into handler
  • decibel power/amplitude check
  • load wav file
  • add getOutput* methods
  • add outputTxEnable/outputTxDisable methods
  • check inverse phase shift issue
  • add script node sample to config
  • rewrite main API
    • move code to factory
    • cleanup inside main service
    • internal notifyHandler for constellation update, external for user purposes
    • add rx method outside the factory
    • destroy constellation
    • ability to change frequency
    • fix recorded file loading logic
    • fix history point colors
    • add ability to choose destination source
  • move script processor node to receive manager
  • move sin/cos to internal Math service (to give ability to quickly add lookup tables)
  • fix layout
  • add phase offset input to align symbol '0'
  • add html generation as js + ofdm support
  • change send logic (add amplitude, symbolCount, symbol to each OFDM block)
  • change send sequence logic (use format: '5.5.2.0 1.2.4.1')
  • add DFT time span to config
  • internal loop for notifications
    • add script node block time (from audiocontext)
    • add sample offset time from script node block time
  • add symbol config to rx
  • prefill amplitude value basing on ofdm size at channel tx
  • add symbol detection to rx
  • move example code related to html generation to separate file with comment
  • add buttons for symbols (TX)
  • add squares with symbol number (RX)
  • update sequence textarea after pskSize change
  • rename delay-loop-handler
  • after psk change only related tx/rx should be updated
  • add rx/tx to channel headers
  • change 'frame' to 'packet'
  • add 'Send sync signal' to TX
  • ability to hide some of the widgets: 'Easy' and 'Advanced' view
  • fix styles
    • add source button active class
    • improve responsive design
  • add margin to sections
  • use first symbol of each packet to fine tune phase offset (add checkbox for that feature)
  • add inter-packet gap duration
  • add quick configs like: 'baud-5, ofdm-1, psk-2' or 'baud-20, ofdm-16, psk-2' or ...
    • add checkbox for tx/rx config
    • add callback to destroy
    • add bit speed information at UI
  • refactor all transmit and receive logic (move it to physical layer internals)
    • [TX] remove symbol generation from template-util
    • [TX] symbol shouldn't have any guard interval or/and interpacket gap
    • [RX] add setTimes* methods (maybe it's worth to add some error margin - times inside are for internal adapter use)
    • [RX] add setSyncPreamble(true/false) method
    • [RX] add packet receive handler packetReceived(data)
    • [RX] add multiple channels support (introduce new class in the middle)
    • set threshold to very low value (-100 dB) to force idle state for a while
    • compute average noise level power at idle state
    • after noise level is set raise threshold xx dB above noise level
    • so far do not collect symbol and packet data (wait for sync)
    • run sync on the TX side
    • sync state will be detected - grab average max signal strength
    • subtract 10 decibels from max signal and enable symbol/packet collecting
    • [RX] add method to reset receiver state machine (to follow steps above again)
    • [RX] double check state times at receive adapter
      • move sync time to main adapter class
      • check symbol, guard times
      • average sample sizes should be config dependent (mostly for samplesPerSecond setting)
    • [RX] grab/clean packet data and notify packet handler