Slide 1

Slide 1 text

Recreating the ZX Spectrum loader Remy Sharp @REM

Slide 2

Slide 2 text

What? Why!?

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

1986

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Sound

Slide 17

Slide 17 text

1.Pilot tone 2.data

Slide 18

Slide 18 text

1.Pilot tone 2.data

Slide 19

Slide 19 text

1.Pilot tone 2.data

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

What does 01110010 sound like?

Slide 25

Slide 25 text

* 3.5Hmz

Slide 26

Slide 26 text

* 3.5Hmz * 1 t-state = 1/3,500,000

Slide 27

Slide 27 text

* 3.5Hmz * 1 t-state = 1/3,500,000 * 1T = 1/2 pulse wave

Slide 28

Slide 28 text

* 3.5Hmz * 1 t-state = 1/3,500,000 * 1T = 1/2 pulse wave * 1 bit of data = 2 equal pulses

Slide 29

Slide 29 text

* 3.5Hmz * 1 t-state = 1/3,500,000 * 1T = 1/2 pulse wave * 1 bit of data = 2 equal pulses * binary 0 = 855T

Slide 30

Slide 30 text

* 3.5Hmz * 1 t-state = 1/3,500,000 * 1T = 1/2 pulse wave * 1 bit of data = 2 equal pulses * binary 0 = 855T * binary 1 = 1710T

Slide 31

Slide 31 text

What does 01110010 LOOK like?

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

http://teropa.info/blog/2016/08/04/sine-waves.html

Slide 35

Slide 35 text

Circles & Timing

Slide 36

Slide 36 text

const T = 1/3500000; const SAMPLE_RATE = 44100; // 44.1Mhz const ONE = 1710 * 2; // 2 = HIGH + LOW const asHz = pulse => 1 / (T * pulse); const toRadian = hz => hz * Math.PI * 2; function generateSample(output) { const length = ONE * T * SAMPLE_RATE; for (let i = 0; i < length; i++) { const time = i / SAMPLE_RATE; const angle = time * toRadian(asHz(ONE)); // store a square wave output[i] = Math.sin(angle) < 0 ? -1 : 1; } }

Slide 37

Slide 37 text

000100110000000000000000000000110110001101101111011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011 011101000000000011111111001000011011101011000011001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101 001111000010000000010001011110101100001100000001010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011 010101000010000000110100000100000000011100010001000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000 100100001001000010010000100100001001000010010000000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000 100100001001000010010000100100001001000000010110000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000 100100001001000010010000100100000000000000011000001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110 011001010110001101110100001101000010000000100000011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011 010111000011111000000010110011010000000100010110000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000 110011010011110000100000110010010001011000000110000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110 000010100000110010010000100100001001000010010000100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100 000011001001000010010000100100001001000010010000100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100 100100001001000010010000100100001001000010010000100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110 011111100011110000011000000000001000010100010011000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000 000000000000000000000000100000001011101101110100000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110 110000110000000100001100000000001100110100111100001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011 010011110100111001001110010001010100001101010100001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000 100100000001011000001011000011001001000010010000100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000 000101100000110100001100100100001001000010010000100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110 000011110000110010010000100100001001000010010000100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000 000000110110001101101111011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111 001000011011101011000011001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001 011110101100001100000001010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100 000100000000011100010001000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000 100100001001000010010000000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000 100100001001000000010110000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000 100100000000000000011000001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110011001010110001101110100 001101000010000000100000011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011010111000011111000000010 110011010000000100010110000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000110011010011110000100000 110010010001011000000110000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110000010100000110010010000 100100001001000010010000100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100000011001001000010010000 100100001001000010010000100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100100100001001000010010000 100100001001000010010000100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110011111100011110000011000 000000001000010100010011000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000000000000000000000000000 100000001011101101110100000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110110000110000000100001100 000000001100110100111100001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011010011110100111001001110 010001010100001101010100001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000100100000001011000001011 000011001001000010010000100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000000101100000110100001100 100100001001000010010000100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110000011110000110010010000 100100001001000010010000100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000000000110110001101101111 011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111001000011011101011000011 001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001011110101100001100000001 010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100000100000000011100010001 000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000100100001001000010010000 000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000100100001001000000010110 000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000100100000000000000011000 001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110011001010110001101110100001101000010000000100000 011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011010111000011111000000010110011010000000100010110 000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000110011010011110000100000110010010001011000000110 000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110000010100000110010010000100100001001000010010000 100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100000011001001000010010000100100001001000010010000 100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100100100001001000010010000100100001001000010010000 100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110011111100011110000011000000000001000010100010011 000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000000000000000000000000000100000001011101101110100 000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110110000110000000100001100000000001100110100111100 001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011010011110100111001001110010001010100001101010100 001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000100100000001011000001011000011001001000010010000 100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000000101100000110100001100100100001001000010010000 100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110000011110000110010010000100100001001000010010000 100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000000000110110001101101111011011100110111001100101 011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111001000011011101011000011001000100111101101011100 001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001011110101100001100000001010000000000000011001101 001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100000100000000011100010001000000010001011000001010 000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000100100001001000010010000000101100000110000001100 100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000100100001001000000010110000011100000110010010000 100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000100100000000000000011000001111000111111001111110

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

Problem: Buffers Bad

Slide 40

Slide 40 text

this.node = ctx.createScriptProcessor(bufferSize, 1, 1); this.node.onaudioprocess = audioProcessingEvent => { const channel = 0; const inputBuffer = audioProcessingEvent.inputBuffer; const input = inputBuffer.getChannelData(channel); // then we'll read the values for own processing this.read(input, performance.now()); // copy the input directly across to the output const outputBuffer = audioProcessingEvent.outputBuffer; const output = outputBuffer.getChannelData(channel); inputBuffer.copyFromChannel(output, channel, channel); }; // constructor continues...

Slide 41

Slide 41 text

Otherwise:

Slide 42

Slide 42 text

Visuals

Slide 43

Slide 43 text

IMAGE > ...> binary ...> audio

Slide 44

Slide 44 text

IMAGE > ...> binary ...> audio ...> Canvas

Slide 45

Slide 45 text

IMAGE > ...> binary ...> audio

Slide 46

Slide 46 text

* Draw image into canvas

Slide 47

Slide 47 text

* Draw image into canvas * export canvas as blob

Slide 48

Slide 48 text

* Draw image into canvas * export canvas as blob * File Reader API to read as binary string

Slide 49

Slide 49 text

* Draw image into canvas * export canvas as blob * File Reader API to read as binary string * convert char to binary

Slide 50

Slide 50 text

* Draw image into canvas * export canvas as blob * File Reader API to read as binary string * convert char to binary * TURN binary INTO audio

Slide 51

Slide 51 text

function imageToBlob(img) { const c = document.createElement('canvas'); const ctx = c.getContext('2d'); c.width = img.width; c.height = img.height; ctx.drawImage(img, 0, 0); return new Promise(resolve => { c.toBlob(file => resolve(file)); }) }

Slide 52

Slide 52 text

function imageToBlob(img) { const c = document.createElement('canvas'); const ctx = c.getContext('2d'); c.width = img.width; c.height = img.height; ctx.drawImage(img, 0, 0); return new Promise(resolve => { c.toBlob(file => resolve(file)); }) }

Slide 53

Slide 53 text

function fileToBinary(blob) { return new Promise(resolve => { const reader = new window.FileReader(); reader.onloadend = () => { const binary = []; const result = reader.result; for (let i = 0; i < result.length; i++) { const char = result[i]; binary.push(charToBinary(char)); } resolve( binary.reduce((acc, byte) => { return acc.concat(byte.split('')); }, []) ); }; reader.readAsBinaryString(blob); }); }

Slide 54

Slide 54 text

function fileToBinary(blob) { return new Promise(resolve => { const reader = new window.FileReader(); reader.onloadend = () => { const binary = []; const result = reader.result; for (let i = 0; i < result.length; i++) { const char = result[i]; binary.push(charToBinary(char)); } resolve( binary.reduce((acc, byte) => { return acc.concat(byte.split('')); }, []) ); }; reader.readAsBinaryString(blob); }); }

Slide 55

Slide 55 text

function charToBinary(chr) { return char .charCodeAt(0) // R = 82 .toString(2) // 82 = 1010010 .padStart(8, '0'); // 1010010 = 01010010 }

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Reception

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Accessibility

Slide 60

Slide 60 text

Seizures #SZR

Slide 61

Slide 61 text

Vestibular disorders #dzy

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

IMAGE > ...> binary ...> audio ...> Canvas?

Slide 64

Slide 64 text

* SAMple audio

Slide 65

Slide 65 text

* SAMple audio * detect edge

Slide 66

Slide 66 text

* SAMple audio * detect edge * length = 1/2 pulse

Slide 67

Slide 67 text

* SAMple audio * detect edge * length = 1/2 pulse * read 2nd pulse, ?=== len

Slide 68

Slide 68 text

* SAMple audio * detect edge * length = 1/2 pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1

Slide 69

Slide 69 text

* SAMple audio * detect edge * length = 1/2 pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits

Slide 70

Slide 70 text

* SAMple audio * detect edge * length = 1/2 pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits *

Slide 71

Slide 71 text

* SAMple audio * detect edge * length = 1/2 pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits * * make image from bytes

Slide 72

Slide 72 text

export function loadEdge1(buffer) { if (!buffer.length) { return null; } let last = null; let point = buffer.shift(); pulseBuffer.push(point); do { // search for when the buffer point crosses the zero threshold if (last !== null) { // important: when we hit an edge, the data doesn't include the edge if (edge(point, last)) { // create a new array and return that instead const res = Array.from(pulseBuffer); // reset the buffer pulseBuffer = []; return res; } } pulseBuffer.push(point); last = point; } while ((point = buffer.shift())); return null; // no edge found }

Slide 73

Slide 73 text

pulse => bit?

Slide 74

Slide 74 text

PULSE = 1 / sample rate * (HI count) BIT = PULSE === 855 ? 0 : 1;

Slide 75

Slide 75 text

BIT => Bytes

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

4b FF 80 32 00 a1 42 02 BB 80 c3 20 d1 d9 ff 02

Slide 81

Slide 81 text

45 seconds later...

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

if (data.filter(Boolean).length === 0) { return; }

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

What did I learn?

Slide 89

Slide 89 text

Why?

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

demos: zx.isthe.link checkout Thanks, @rem