Showing posts with label dependency manager. Show all posts
Showing posts with label dependency manager. Show all posts

Wednesday, September 30, 2020

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)




Wednesday, July 27, 2016

Marker Clustering with Google's Utility library for Maps SDK (Google-Maps-iOS-Utils)

This tutorial shows how to group multiple map markers with Marker Clustering utility library provided by Google-Maps-iOS-Utils(V1.0.1), which requires Google Maps SDK for iOS (V2.0). This tutorial is created in Swift 2.2 with Xcode 7.3.1. The following screenshots show a lot of map markers and the clustering results using GMUClusterManager with default circles or custom images.




Follow these steps:

1. Install CocoaPods as the dependency manager.

Type this command in the terminal:

sudo gem install cocoapods

2. Create a Single View Application project with Xcode and close it.

3. Go to the Xcode project directory in the terminal, type pod init or nano Podfile to create a Podfile and save it as:


platform :ios, '9.0'
target "ProjectName" do
    pod 'GoogleMaps'
    pod 'Google-Maps-iOS-Utils'
end

4.  Type this terminal command:

pod install

You should see something like this in the terminal:




If your GoogleMaps is an old version, update it with this terminal command:

pod update

5. Open the projectName.xcworkspace file just automatically created. (Don't open the original .xcodeproj file)



6. Add a temporary Objective-C file to your project. You may give it any name you like, e.g. Temp.m.



Select Create Bridging Header.




7. Delete the temporary Objective-C file (Temp.m) you just created.

8. In the projectName-Bridging-Header.h file just created, add this line:

#import <Google-Maps-iOS-Utils/GMUMarkerClustering.h>

9. Get the iOS API key like AIza................... from Google Developers Console. (For more details, see Step 6 of the Using Google Maps SDK for iOS in Swift tutorial.

10. Edit the AppDelegate.swift file:
    func application(application: UIApplicationdidFinishLaunchingWithOptions launchOptions: [NSObjectAnyObject]?) -> Bool {
        
        GMSServices.provideAPIKey("AIza....") //iOS API key
        
        return true

    }

10. Modify ViewController.swift as below:


import UIKit

class ViewController: UIViewController, GMSMapViewDelegate, GMUClusterManagerDelegate {
    
    private var mapView : GMSMapView!
    private var clusterManager: GMUClusterManager!
    
    //true - marker clustering / false - map markers without clustering
    let isClustering : Bool = true
    
    //true - images / false - default icons
    let isCustom : Bool = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        mapView = GMSMapView(frame: view.frame)
        
        //Default position at Chungli (Zhongli) Railway Station, Taoyuan, Taiwan.
        mapView.camera = GMSCameraPosition.cameraWithLatitude(24.953232, longitude: 121.225353, zoom: 12.0)
        
        mapView.mapType = kGMSTypeNormal
        mapView.delegate = self
        
        view.addSubview(mapView)
        
        if isClustering {
            var iconGenerator : GMUDefaultClusterIconGenerator!
            if isCustom {
                var images : [UIImage] = []
                for imageID in 1...5 {
                    images.append(UIImage(named: "m\(imageID).png")!)
                }
                iconGenerator = GMUDefaultClusterIconGenerator(buckets: [ 10, 50, 100, 200, 500 ], backgroundImages: images)
            } else {
                iconGenerator = GMUDefaultClusterIconGenerator()
            }

            let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
            let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
            
            clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
            
            generateCoord(true)
            
            // Call cluster() after items have been added to perform the clustering and rendering on map.
            clusterManager.cluster()
            
            // Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
            clusterManager.setDelegate(self, mapDelegate: self)
        } else {
            generateCoord(false)
        }
    }
    
    /// Point of Interest Item which implements the GMUClusterItem protocol.
    class POIItem: NSObject, GMUClusterItem {
        var position: CLLocationCoordinate2D
        var name: String!
        
        init(position: CLLocationCoordinate2D, name: String) {
            self.position = position
            self.name = name
        }
    }

    func generateCoord(isCluster: Bool) {
        
        let latitudeMin   : Double = 24.79
        let latitudeMax   : Double = 25.10
        let latitudeDiff  : Double = latitudeMax - latitudeMin
        let longitudeMin  : Double = 120.99
        let longitudeMax  : Double = 121.50
        let longitudeDiff : Double = longitudeMax - longitudeMin
        
        for count in 1...5000 {
            
            let latitude  = latitudeMin  + Double(arc4random()%10000)/10000*latitudeDiff
            let longitude = longitudeMin + Double(arc4random()%10000)/10000*longitudeDiff
        
            let position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
            
            if isCluster {
                let item = POIItem(position: position, name: "#\(count)")
                
                clusterManager.addItem(item)
            } else {
                let marker = GMSMarker(position: position)
                marker.title = "#\(count)"
                marker.map = mapView
            }
        }

    }
    
    func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
        let newCamera = GMSCameraPosition.cameraWithTarget(cluster.position,
                                                           zoom: mapView.camera.zoom + 1)
        let update = GMSCameraUpdate.setCamera(newCamera)
        mapView.moveCamera(update)
    }
    
    //Show the marker title while tapping
    func mapView(mapView: GMSMapView, didTapMarker marker: GMSMarker) -> Bool {
        let item : POIItem = marker.userData as! POIItem

        marker.title = item.name
        
        mapView.selectedMarker = marker
        
        return true
    }
    
    //Optional Feature:
    //Add new markers while tapping at coordinates without markers/clusters
    func mapView(mapView: GMSMapView, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
        
        let item = POIItem(position: coordinate, name: "NEW")
        
        clusterManager.addItem(item)
        
        clusterManager.cluster()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

The above code is modified from Google's ViewController.swift of the SwiftDemoApp.

11. Add the file image files below to the Xcode project:

https://github.com/googlemaps/google-maps-ios-utils/tree/master/app/Resources/Images

12. Edit the Info.plist file (Required for Xcode 7 and iOS 9):

Key: LSApplicationQueriesSchemes
Type: Array

Key: Item 0
Type: String
Value: googlechromes

Key: Item 1
Type: String
Value: comgooglemaps



Without modifying Info.plist, you'll get


Pressing the Google logo on the map in the iOS simulator shows:


-canOpenURL: failed for URL: "comgooglemaps://" - error: "This app is not allowed to query for scheme comgooglemaps"


-canOpenURL: failed for URL: "googlechromes://" - error: "This app is not allowed to query for scheme

This is because the iOS simulator does not include Google Maps and Chrome apps. So check this feature with a device.


13. Run the code. You should see result like this:



14. Set isCustom as true to see the custom clustering images:

    //true - images / false - default icons
    let isCustom : Bool = true


15. Try tap at different locations on the map to add new markers:


Related Information:

Marker Clustering
Google-Maps-iOS-Utils(GitHub)
Google Maps SDK for iOS
CocoaPods Tutorial - Google Maps SDK for iOS