<file_sep>//
//  LifeGridModel.swift
//  AudioModem
//
//  Created by Nick on 22/06/2017.
//  Copyright © 2017 Nick. All rights reserved.
//

import Foundation

class LifeGridModel {
    
    let dimensions: (x: Int, y: Int)
    var grid: [Bool]
    
    init(dimensions: (x: Int, y: Int)) {
        self.dimensions = dimensions
        self.grid = []
        self.reloadRandomGrid()
        
        //self.grid = Array(repeating: false, count: dimensions.x * dimensions.y)
    }
    
    func randomBool() -> Bool {
        
        return arc4random_uniform(3) == 0
    }
    
    func reloadRandomGrid() {
        self.grid = []
        for _ in 0..<(dimensions.x * dimensions.y) {
            self.grid.append(self.randomBool())
        }
    }
    
    func index(forCoordinate coordinate: (x: Int, y: Int)) -> Int {
        guard coordinate.x < dimensions.x, coordinate.y < dimensions.y else {
            fatalError("grid coordinate is out of range")
        }
        
        return dimensions.x * coordinate.y + coordinate.x
    }
    
    subscript(x: Int, y: Int) -> Bool {
        get {
            let idx = index(forCoordinate: (x, y))
            return grid[idx]
        }
        set {
            let idx = index(forCoordinate: (x, y))
            grid[idx] = newValue
        }
    }
    
}
<file_sep>//
//  ViewController.swift
//  AudioModem
//
//  Created by Nick on 22/06/2017.
//  Copyright © 2017 Nick. All rights reserved.
//

import UIKit
import AVFoundation

class ViewController: UIViewController {

    @IBOutlet weak var lifeGridView: LifeGridView!
    @IBOutlet weak var frequencySlider: UISlider!
    
    let controlFrequency: Double = 120.0
    let trueFrequency: Double = 200.0
    let falseFrequency: Double = 320.0
    let normalTimeInterval: TimeInterval = 0.25
    let controlTimeInterval: TimeInterval = 3.0
    
    static let squareSize = 40
    lazy var recommendedDimensions: (Int, Int) = {
            let xDimension = Int(self.view.bounds.size.width) / ViewController.squareSize
            let yDimension = Int(self.view.bounds.size.height) / ViewController.squareSize
            
            return (8, 16)
            //return (xDimension, yDimension)
    }()
    
    var gridModel: LifeGridModel?
    
    var engine: AVAudioEngine!
    var tone: AVTonePlayerUnit!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.gridModel = LifeGridModel(dimensions: recommendedDimensions)
        self.lifeGridView.gridModel = self.gridModel
        
        self.setupToneGeneration()
    }

    @IBAction func onTapSend(_ sender: UIButton) {
        if tone.isPlaying {
            
            //sender.setTitle("Send", for: UIControlState())
            self.stopTone()
            
        } else {
            
            sender.setTitle("Stop", for: UIControlState())
            self.playTone()
            
        }
    }
    
}

extension ViewController{
    
    func setupToneGeneration() {
        
        tone = AVTonePlayerUnit()
        tone.frequency = controlFrequency // This can be changed accordingly
        
        let format = AVAudioFormat(standardFormatWithSampleRate: tone.sampleRate, channels: 1)
        print(format.sampleRate)
        engine = AVAudioEngine()
        engine.attach(tone)
        let mixer = engine.mainMixerNode
        engine.connect(tone, to: mixer, format: format)
        do {
            try engine.start()
        } catch let error as NSError {
            print(error)
        }
    }
    
    @IBAction func frequencyChanged(_ sender: UISlider){
        let freq = Double(sender.value)
        tone.frequency = freq
        
    }
    
    func playTone(){
        
        tone.preparePlaying()
        engine.mainMixerNode.volume = 1.0
        print("Started Sending Tone")
        self.playGridToneDataBlast()
    }
    
    func stopTone(){
        
        engine.mainMixerNode.volume = 0.0
        tone.stop()
        
        self.gridModel?.reloadRandomGrid()
        self.lifeGridView.setNeedsDisplay()
        
        print("Stopped Sending Tone")
    }
}

extension ViewController{
    
    func playGridToneDataBlast() {
        
        tone.play()
        // Initial Blast signifying that new grid data is about to be transmitted
        self.playControlFrequencyBlast(duration: controlTimeInterval)
        
        let count = self.gridModel?.grid.count
        
        for i in 0...(count! - 1) {
            
            self.playControlFrequencyBlast(duration:normalTimeInterval)
            
            if (self.gridModel?.grid[i])!{
                self.playToneBlast(frequency: trueFrequency, duration: normalTimeInterval)
            }
            else{
                self.playToneBlast(frequency: falseFrequency, duration: normalTimeInterval)
            }
        }
        
        // Ending Blast signifying that grid data has finished transmitting
        self.playControlFrequencyBlast(duration: controlTimeInterval)
        self.stopTone()
        
    }
    
    func playControlFrequencyBlast(duration: Double) {
        self.playToneBlast(frequency: controlFrequency, duration: duration)
    }
    
    func playToneBlast(frequency: Double,duration: TimeInterval) {
        print("Playing Frequency: \(frequency), for Duration: \(duration)")
        tone.frequency = frequency
        Thread.sleep(forTimeInterval: duration)
    }
    
}

<file_sep>//
//  LifeGridView.swift
//  AudioModem
//
//  Created by Nick on 22/06/2017.
//  Copyright © 2017 Nick. All rights reserved.
//

import Foundation
import UIKit

class LifeGridView: UIView {
    
    public var gridModel: LifeGridModel?
    
    var xSquareSize: CGFloat? {
        get {
            guard let dimensions = gridModel?.dimensions else {
                return nil
            }
            return self.bounds.size.width / CGFloat(dimensions.x)
        }
    }
    
    var ySquareSize: CGFloat? {
        get {
            guard let dimensions = gridModel?.dimensions else {
                return nil
            }
            return self.bounds.size.height / CGFloat(dimensions.y)
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let gridModel = gridModel else {
            return
        }
        guard let xSize = self.xSquareSize, let ySize = self.ySquareSize else {
            return
        }
        guard let touch = touches.first else {
            return
        }
        
        let point = touch.location(in: self)
        let x = Int(point.x / xSize)
        let y = Int(point.y / ySize)
        
        gridModel[x, y] = !gridModel[x, y]
        self.setNeedsDisplay()
    }
    
    override func draw(_ rect: CGRect) {
        guard let gridModel = gridModel else {
            return
        }
        guard let xSize = self.xSquareSize, let ySize = self.ySquareSize else {
            return
        }
        
        let ctx = UIGraphicsGetCurrentContext()
        ctx?.setStrokeColor(UIColor.blue.cgColor)
        ctx?.setLineWidth(1.0)
        
        for x in 0..<gridModel.dimensions.x {
            for y in 0..<gridModel.dimensions.y {
                let color = gridModel[x, y] ? UIColor.darkGray : UIColor.lightGray
                ctx?.setFillColor(color.cgColor)
                
                let origin = CGPoint(x: CGFloat(x) * xSize + 1.0, y: CGFloat(y) * ySize + 1.0)
                let rect = CGRect(origin: origin, size: CGSize(width: xSize, height: ySize))
                ctx?.addRect(rect)
                
                ctx?.drawPath(using: .fillStroke)
            }
        }
        
        let size = CGSize(width: self.bounds.size.width - 1.0, height: self.bounds.size.height - 1.0)
        let rect = CGRect(origin: CGPoint.zero, size: size)
        ctx?.addRect(rect)
        ctx?.strokePath()
    }
    
}
