Saturday, May 27, 2017

How to Sort Array, Dictionary, and Array of Tuples

The code below shows how to sort an array, a dictionary or an array of tuples in Swift 3.1 with Xcode 8.3.1 Playground. Sorting can be done in ascending or descending order. A dictionary can be sorted by key or by value.

Note: When sorting a dictionary, the returned type is an array of tuples.

//Array
let array = [3, 5, 9, 7, 4, 1, 2]

let arrayInc = array.sorted()
let arrayDec = array.sorted(by: >)

//Dictionary
let dict = ["A": 123, "B": 789, "C": 567, "D": 432]

print(dict)

let dictKeyInc = dict.sorted(by: <)
let dictKeyDec = dict.sorted(by: >)

print(dictKeyInc)
print(dictKeyDec)

let dictValInc = dict.sorted(by: { $0.value < $1.value })
let dictValDec = dict.sorted(by: { $0.value > $1.value })

print(dictValInc)
print(dictValDec)

for item in dictValDec {
    print("key:\(item.key) value:\(item.value)")
}

//Array of Tuples
let tupleArray = [("A", 123), ("B", 789), ("C", 567), ("D", 432)]

let tupleArrayInc = tupleArray.sorted(by: { $0.1 < $1.1 })


print(tupleArrayInc)


Result:

["B": 789, "A": 123, "C": 567, "D": 432]
[(key: "A", value: 123), (key: "B", value: 789), (key: "C", value: 567), (key: "D", value: 432)]
[(key: "D", value: 432), (key: "C", value: 567), (key: "B", value: 789), (key: "A", value: 123)]
[(key: "A", value: 123), (key: "D", value: 432), (key: "C", value: 567), (key: "B", value: 789)]
[(key: "B", value: 789), (key: "C", value: 567), (key: "D", value: 432), (key: "A", value: 123)]
key:B value:789
key:C value:567
key:D value:432
key:A value:123
[("A", 123), ("D", 432), ("C", 567), ("B", 789)]

Reference

Sort Dictionary by Key Value
cannot assign value of type '[(string, string)]' to type '[string : string]'

Monday, May 22, 2017

Dictionary of Arrays in Swift 3

The code below shows how to include an array in a dictionary in Swift 3.1 with Xcode 8.3.1 Playground.

dictionarySemitones["abc"]=[1,2,3]
dictionarySemitones["def"]=[4,5,6]

print(dictionarySemitones)
print(dictionarySemitones["abc"]!)

Reference:

A Dictionary of Arrays in Swift

Sunday, May 21, 2017

Convert an Integer Array to a String

The code below shows how to convert an integer array to a string in Swift 3.1 with Xcode 8.3.1 Playground.

let arrayInt = [1, 2, 3, 4, 5]
let string = "\(arrayInt)"
print(string)

Reference:

How can I convert an Int array into a String? array in Swift

Conversion from Numbered Musical Notation to Musical Note Letters

The code below converts Numbered Musical Notation (簡譜) to musical note letters using Xcode 8.3.2 playground in Swift 3.1. Techniques used in the code involves:

1. Separating a string into an array
2. Type casting each array element into an integer

3. Converting the numerical notation into a matched letter
4. Joining all letters into a single string

//Twinkle, Twinkle, Little Star 一閃一閃亮晶晶
let numberedNotation = "1 1 5 5 6 6 5"

//1. Separating a string into an array let numberedNoteArray = numberedNotation.components(separatedBy: " ")

let lookupTable = ["C","D","E","F","G","A","B"]

var noteArray = [String]()

for note in numberedNoteArray {
    //2. Type casting each array element into an integer
    let noteInt = Int(note)!

    //3. Converting the numerical notation into a matched letter
    let noteChar = lookupTable[noteInt-1]
    noteArray.append(noteChar)
}

//4. Joining all letters into a single string let noteString = noteArray.joined(separator: " ")

print("\(noteString)")

Result:

C C G G A A G

Saturday, May 13, 2017

Mac Technique: External keyboard configuration for Home and End keys

To configure the [Home] and [End] keys of an external USB keyboard to move the cursor to the front or the end of line in Xcode, try:

1. create the following path:

~/Library/KeyBindings/

2. Create a file called DefaultKeyBinding.dict in this directory with:

{
    "\UF729" = moveToBeginningOfParagraph:; // home
    "\UF72B" = moveToEndOfParagraph:; // end
    "$\UF729" = moveToBeginningOfParagraphAndModifySelection:; // shift-home
    "$\UF72B" = moveToEndOfParagraphAndModifySelection:; // shift-end
}

With this solution, holding a [Shift] key and [Home]/[End] makes a selection for the characters to the beginning or end of the line.

References:

Remap “Home” and “End” to beginning and end of line
Mac OS X and Home / End keys

Sunday, May 7, 2017

Modified Version of AudioKit's Microphone Analysis Example without using the Storyboard

The code below is modified from AudioKit's Microphone Analysis example. The UI components are written programmatically without using the storyboard. The code is developed with Xcode 8.3.2 (Swift 3.1) and iOS 10.

1. Use CocoaPods to include the AudioKit framework by add this line to the Podfile:

pod 'AudioKit'

*************** Updated October 4, 2020 for AudioKit 4 *****************
Type this terminal command:

pod install



*************************** Update 2020 End *****************************

2. Enable the microphone by adding NSMicrophoneUsageDescription and a request string such as "This app needs microphone access." to Info.plist.

Your Info.plist should be like this:


When app is run at the first time, this message should be displayed:



Without this modification, you'll see error like this:


This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSMicrophoneUsageDescription key with a string value explaining to the user how the app uses this data.

3. Modify ViewController.swift as below

import UIKit
import AudioKit

class ViewController: UIViewController {
    
    var labelFrequencyValue : UILabel!
    var labelAmplitudeValue : UILabel!
    var labelSharpValue : UILabel!
    var labelFlatValue : UILabel!
    let mic = AKMicrophone()
    var tracker : AKFrequencyTracker!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let labelSing = UILabel(frame: CGRect(x: 0, y: 28, width: view.frame.width, height: 50))
        labelSing.text = "Sing into the Microphone"
        labelSing.textAlignment = .center
        labelSing.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightBold) //System Font Bold
        labelSing.textColor = UIColor.white
        labelSing.backgroundColor = UIColor(red: 2/255, green: 181/255, blue: 31/255, alpha: 1.0)
        view.addSubview(labelSing)
        
        let labelFrequency = UILabel(frame: CGRect(x: 16, y: 86, width: 85.5, height: 20.5))
        labelFrequency.text = "Frequency:"
        view.addSubview(labelFrequency)
        
        labelFrequencyValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 86, width: 50, height: 20.5))
        labelFrequencyValue.text = "0"
        labelFrequencyValue.textAlignment = .right
        view.addSubview(labelFrequencyValue)
        
        let labelAmplitude = UILabel(frame: CGRect(x: 16, y: 114.5, width: 85.5, height: 20.5))
        labelAmplitude.text = "Amplitude:"
        view.addSubview(labelAmplitude)
        
        labelAmplitudeValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 114.5, width: 50, height: 20.5))
        labelAmplitudeValue.text = "0"
        labelAmplitudeValue.textAlignment = .right
        view.addSubview(labelAmplitudeValue)
        
        let labelSharp = UILabel(frame: CGRect(x: 16, y: 142, width: 111.5, height: 20.5))
        labelSharp.text = "Note (Sharps):"
        view.addSubview(labelSharp)
        
        labelSharpValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 142, width: 50, height: 20.5))
        labelSharpValue.text = "C4"
        labelSharpValue.textAlignment = .right
        view.addSubview(labelSharpValue)
        
        let labelFlat = UILabel(frame: CGRect(x: 16, y: 170.5, width: 94, height: 20.5))
        labelFlat.text = "Note (Flats):"
        view.addSubview(labelFlat)
        
        labelFlatValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 170.5, width: 50, height: 20.5))
        labelFlatValue.text = "F4"
        labelFlatValue.textAlignment = .right
        view.addSubview(labelFlatValue)
        
        let labelPlot = UILabel(frame: CGRect(x: 0, y: 199, width: view.frame.width, height: 21.5))
        labelPlot.text = "Audio Input Plot"
        labelPlot.textAlignment = .center
        view.addSubview(labelPlot)
        

        tracker = AKFrequencyTracker.init(mic)
        let silence = AKBooster(tracker, gain: 0)
        AudioKit.output = silence
        AudioKit.start()
        
        setupPlot()
        
        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateUI), userInfo: nil, repeats: true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func setupPlot() {
        let audioInputPlot = EZAudioPlot(frame: CGRect(x: 0, y: view.center.y, width: view.frame.width, height: 200))
        
        let plot = AKNodeOutputPlot(mic, frame: audioInputPlot.bounds)
        plot.plotType = .rolling
        plot.shouldFill = true
        plot.shouldMirror = true
        plot.color = UIColor.blue
        audioInputPlot.addSubview(plot)
        view.addSubview(audioInputPlot)
    }
    
    func updateUI() {
        
        let noteFrequencies = [16.35,17.32,18.35,19.45,20.6,21.83,23.12,24.5,25.96,27.5,29.14,30.87]
        let noteNamesWithSharps = ["C", "C","D","D","E","F","F","G","G","A","A","B"]
        let noteNamesWithFlats = ["C", "D","D","E","E","F","G","G","A","A","B","B"]
        
        if tracker.amplitude > 0.1 {
            labelFrequencyValue.text = String(format: "%0.1f", tracker.frequency)
        
            var frequency = Float(tracker.frequency)
            while (frequency > Float(noteFrequencies[noteFrequencies.count-1])){
                frequency = frequency / 2.0
            }
            while (frequency < Float(noteFrequencies[0])) {
                frequency = frequency * 2.0
            }
            
            var minDistance : Float = 10000.0
            var index = 0
            
            for i in 0..<noteFrequencies.count {
                let distance = fabsf(Float(noteFrequencies[i]) - frequency)
                if (distance < minDistance) {
                    index = i
                    minDistance = distance
                }
            }
            let octave = Int(log2f(Float(tracker.frequency) / frequency))
            labelSharpValue.text = "\(noteNamesWithSharps[index])\(octave)"
            labelFlatValue.text = "\(noteNamesWithFlats[index])\(octave)"
        }
        labelAmplitudeValue.text = String(format: "%0.2f", tracker.amplitude)
    }

}

4. Result:



Related Information

AudioKit
Beethoven (Pitch Detection)




Monday, May 1, 2017

UIButton - Update button label when pressed

This post shows how to change the text/title of a button every time the button is pressed.
The code below is written in Swift 3.1 with Xcode 8.3.2 and tested with iOS 10.3.1.

Code:

import UIKit

class ViewController: UIViewController {
    
    var count : Int = 1
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
        button.center = view.center
        button.setTitle("Press", for: UIControlState.normal)
        button.setTitleColor(UIColor.blue, for: UIControlState.normal)
        button.setTitleColor(UIColor.cyan, for: UIControlState.highlighted)
        button.addTarget(self, action: #selector(buttonPressed(sender:)), for: UIControlEvents.touchUpInside)
        view.addSubview(button)
    }
    
    func buttonPressed(sender: UIButton) {
        sender.setTitle("Pressed \(count)", for: UIControlState.normal)
        count = count+1
        print("button pressed!!")
    }
}

Reference:

UIButton - Selector Warning (Update with Swift 3.1)