Hodetelefoner kan brukes til så mangt – inkludert å klippe og lime tekst ved hjelp av lydsignaler. (Bilde: Martin Kirkholt Melhus/martinmelhus.com)

Martin måtte jobbe på en maskin uten internett – bygget modem med HTML5

Det er nesten så man ikke tror at det kan være sant, men det finnes faktisk datamaskiner som rent fysisk rett og slett ikke kan koples til internettet i det Herrens år 2017.

Dette er historen om en av disse kaldsvette-fremkallende maskinene.

Og det er historien om en mann som klarte å kave seg ut av marerittet – og det på et vis som uten tvil er en MacGyver-episode verdig.

Men all den tid vi i digi.no ikke sitter på rettigheter til MacGyver-franchisen (ennå!), er det minste vi kan gjøre å skjenke dette over gjennomsnittet kreative prosjektet en egen artikkel.

Har du eller noen du kjenner et prosjekt vi kan dele med leserne? Send det på e-post til andrea.bruer@digi.no – gjerne med bilder eller skjermdumper. 

Web Audio-API til unnsetning

Vår helt heter Martin Kirkholt Melhus. Han jobber som utvikler for Visma Consulting i Oslo. 

– I forbindelse med et oppdrag hos en kunde, kunne ikke utviklermaskinen jeg fikk utdelt kobles til internett, forteller Martin.

– Jeg begynte jeg å se på muligheter til å omgå dette. Målet var å kunne kopiere tekst mellom denne maskinen og min laptop – som hadde internett.

Og slik fikk Martin ideen om å bygge intet mindre enn et godt gammeldags modem med HTML5 – nærmere bestemt HTML5 sitt Web Audio-API.

Martin har filmet løsningen i aksjon. Under videoen kan du lese hvordan Martin gikk frem for å få til dette:

Martin skriver:

Som utvikler hender det at koden man skriver ikke vil kompilere, og da kan det være greit å kopiere feilmeldingen inn i et Google-søkefelt.

Dette fører deg stort sett til et svar på Stack Overflow, hvor noen vennlige sjeler har lagt igjen en kodesnutt som løser problemet.

Denne kodesnutten kopieres deretter inn i kode-editoren, og man klapper seg selv på skulderen for å ha gjort en god jobb.

Internett er noe vi har lært oss å ta for gitt, men som utvikler i Visma Consulting har jeg opplevd at det slett ikke er noen selvfølge.

Hos en kunde fikk jeg utdelt en utviklermaskin uten internett.

Derfor måtte jeg gå alternative veier for å kunne kopierer tekst inn og ut av nettleseren.

Det viste seg at utviklermaskinen hadde en lett tilgjengelig inngang for hodetelefoner. Hos denne kunden hadde jeg også mitt eget kontor, slik at litt støy var uproblematisk.

Lyd avspilt fra utviklermaskinen kunne enkelt fanges opp av laptopen ved hjelp av kreativ posisjonering av hodetelefoner, så jeg valgte å bruke dette til min fordel og implementerte et modem i JavaScript.

Teorien var at ved å kopiere tekst inn i modemet på én maskin, kunne en annen maskin (med internett) lytte på lyden som ble generert, og oversette lydsignalet tilbake til tekst.

Dermed ville det gjøre meg i stand til å overføre tekst mellom datamaskinene ved hjelp av lyd, som igjen ville spare meg for manuell tasting.

Slik fungerer det

Omkoding

En bokstav kan representeres som en char (8 bit). Eksempelvis har bokstaven 'a' verdien 97, som kan skrives som binær-tallet 01100001.

Hver bit-posisjon kan tildeles en unik frekvens, slik at en char kan representeres som en kombinasjon av opp til 8 unike frekvenser.

Frekvensen inkluderes hvis den korresponderende bit-verdien er lik 1. Bokstaven 'a' vil dermed bestå av tre frekvenser.

Ved å representere frekvensene som sinus-bølger får man et signal som kan spilles av som lyd i nettleseren ved hjelp av Web Audio API.

Artikkelen fortsetter under bildet.

Temperatursensor som sender oppdateringer på 433MHz-båndet en gang i minuttet sammen med en DVB-T/FM/DAB-tuner som kan justere mottakerfrekvens via programvare
Frekvens-respons til et lydsignal som representerer bokstaven 'a'.Foto: Martin Kirkholt Melhus/martinmelhus.com

Skrur man opp volumet på kun de frekvensene som inngår i et signal får man et lydsignal som representerer den aktuelle bokstaven. En samling oscillatorer med hver sin volum-kontroll opprettes i Web Audio API på følgende måte:

```javascript let audioContext = new AudioContext(); let masterGain = audioContext.createGain(); masterGain.gain.value = 1;

const frequencies = [392, 784, 1046.5, 1318.5, 1568, 1864.7, 2093, 2637];
// opprett sinus-signalgenerator for hver frekvens let sinusiods = frequencies.map(f => { let oscillator = audioContext.createOscillator(); oscillator.type = 'sine'; oscillator.frequency.value = f; oscillator.start(); return oscillator; }); // opprett volumkontrol for hver signalgenerator let oscillators = frequencies.map(f => { let volume = audioContext.createGain(); volume.gain.value = 0; return volume; }); // koble alt sammen, og sett lyd-ut til PC-høytalerene sinusoids.forEach((sine, i) => sine.connect(oscillators[i])); oscillators.forEach((osc) => osc.connect(masterGain)); masterGain.connect(audioContext.destination); ```

Hvilke oscillatorer som skal inngå for å representere en gitt char-verdi kan finnes på følgende vis:

```javascript
const char2oscillators = (char) => {
let charCode = char.charCodeAt(0);
return oscillators.filter((_, i) => {
return charCode & (1 << i);
});
};

```

Ved å benytte disse byggeklossene kan en bokstav konverteres til lyd ved å først skru ned volumet på alle oscillatorene, for deretter å skru på de frekvensene som inngår i bokstaven:

 ```javascript
const mute = () => {
oscillators.forEach(osc => {
osc.gain.value = 0
});
}

const encodeChar = (char) => {
let activeOscillators = char2oscillators(char);
activeOscillators.forEach(osc => {
osc.gain.value = 1;
});
}
```
`

Dekoding

Web Audio API har heldigvis funksjonalitet for frekvens-analyse, så dekoding av signalet blir relativt enkelt.

Hvilke frekvenser som inngår i et lydsignal sammenliknes med de 8 frekvensene som ble valgt for å representere de forskjellige bit-posisjonene i en byte. På den måten bygges en char-verdi opp hos mottakeren.

Med informasjon om frekvens-responsen til input-signalet kan vi oversette lydsignalet til en tallverdi. Følgende kode viser hvordan en tallverdi utledes, gitt en funksjon `isActive` som sier om en frekvens inngår i frekvens-responsen. ```javascript const getState = () => { return frequencies .reduce((acc, f, idx) => { if (isActive(f)) { acc += (1 << idx); } return acc; }, 0); }; ```

Vi lever dog ikke i en ideell verden, og derfor må man ta høyde for støy. Derfor kalles metoden `getState` kontinuerlig, og dekoderen gir en output kun hvis resultatet har stabilisert seg på samme verdi over mange etterfølgende kall.

Konklusjon

Med nåværende implementasjon har jeg nådd overføringshastigheter på 20 byte i sekundet.

Dette tilsvarer omtrent 0.02 KBps, så det er ingen tvil om at jeg fortsatt foretrekker å ha internett.

Det kom også fort frem at jeg foretrekker å høre på musikk over høyfrekvente pipelyder mens jeg jobber.

I tillegg kan det nevnes at feil-rate tatt i betraktning er minnepinne stort sett et raskere alternativ!

For en mer teknisk beskrivelse og kildekode, se martinmelhus.com.  

Kommentarer (12)

Kommentarer (12)
Til toppen