I have 999 (from 000 to 999) numbers as int
s, and they are all printed on a UILabel
, I also get the value of the UILabel
updated as updateCurrentValue
and all the numbers must be announced when I press the UIButton
that does the announcing function
, sounds of the numbers are imported each via AVFoundation
and the code for announcing (for example) the number 15 is:
if currentValue == 15 {
Sound15?.play()
}
now I'm looking for a code that makes it easier for the hundreds, because it'll be a waste of time to make a function
for all 999 numbers. is there any way that I can only give the value
of the hundreds and then it automatically finds the func
for the rest of the digits on its own? I hope I can make it clear enough so that someone can answer. Here's what I've got so far:
import UIKit
import AVFoundation
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var Sound1:AVAudioPlayer?
var Sound2:AVAudioPlayer?
var Sound3:AVAudioPlayer?
var Sound4:AVAudioPlayer?
var Sound5:AVAudioPlayer?
var Sound6:AVAudioPlayer?
var Sound7:AVAudioPlayer?
var Sound8:AVAudioPlayer?
var Sound9:AVAudioPlayer?
var Sound10:AVAudioPlayer?
var Sound11:AVAudioPlayer?
var Sound12:AVAudioPlayer?
var Sound13:AVAudioPlayer?
var Sound14:AVAudioPlayer?
var Sound15:AVAudioPlayer?
var Sound16:AVAudioPlayer?
var Sound17:AVAudioPlayer?
var Sound18:AVAudioPlayer?
var Sound19:AVAudioPlayer?
var Sound20:AVAudioPlayer?
var Sound30:AVAudioPlayer?
var Sound40:AVAudioPlayer?
var Sound50:AVAudioPlayer?
var Sound60:AVAudioPlayer?
var Sound70:AVAudioPlayer?
var Sound80:AVAudioPlayer?
var Sound90:AVAudioPlayer?
var Sound100:AVAudioPlayer?
var Sound200:AVAudioPlayer?
var Sound300:AVAudioPlayer?
var Sound400:AVAudioPlayer?
var Sound500:AVAudioPlayer?
var Sound600:AVAudioPlayer?
var Sound700:AVAudioPlayer?
var Sound800:AVAudioPlayer?
var Sound900:AVAudioPlayer?
var currentValue = 0
func updateCurrentValue() {
let hundreds = max(0, pickerView.selectedRow(inComponent: 0))
let tens = max(0, pickerView.selectedRow(inComponent: 1))
let ones = max(0, pickerView.selectedRow(inComponent: 2))
currentValue = hundreds * 100 + tens * 10 + ones
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 3
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return String(row)
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 10 // digits 0 - 9
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
updateCurrentValue()
changeLabelText()
}
//Some Functions as example of how it works:
private func accouncingNumbers() {
//for single digit
if currentValue == 1 {
Sound1?.play()
}
//for double digits
if currentValue == 24 {
Sound20?.play()
SoundTimer = Timer.scheduledTimer(withTimeInterval: 1.2, repeats: false) {timer in
self.Sound4?.play()
self.SoundTimer?.invalidate()
self.SoundTimer = nil
}
}
//for 3-digits
if currentValue == 146 {
Sound100?.play()
SoundTimer = Timer.scheduledTimer(withTimeInterval: 1.2, repeats: false) {timer in
self.Sound40?.play()
self.SoundTimer?.invalidate()
self.SoundTimer = nil
self.SoundTimer = Timer.scheduledTimer(withTimeInterval: 1.2, repeats: false) {timer in
self.Sound6?.play()
self.SoundTimer?.invalidate()
self.SoundTimer = nil
}
}
}
//Sounds imported here
do
{
let audioURL1 = Bundle.main.url(forResource: "1", withExtension: "mp3")!
Sound1 = try AVAudioPlayer(contentsOf: audioURL1)
Sound1?.prepareToPlay()
}
catch
{
print(error)
}
}
just looking to find an easier and shorter way for this process.
In order to minimize the amount of sound files you need to bundle up in your application, you will want an algorithm similar to this:
func verbalizeValue(_ value: Int) {
let announcements = prepareAnnouncements(value)
playAnnouncements(announcements)
}
func prepareAnnouncements(_ value: Int) -> [String] {
var valueToProcess: Int = value
var announcements: [String] = []
if valueToProcess >= 100 {
// values 100 and above
let hundred = valueToProcess / 100
valueToProcess = valueToProcess % 100
let soundfile = "say_\(hundred)00.wav"
announcements.append(soundfile)
}
if valueToProcess >= 20 {
// values 30 to 99
let dozen = valueToProcess / 10
valueToProcess = valueToProcess % 10
let soundfile = "say_\(dozen)0.wav"
announcements.append(soundfile)
}
if valueToProcess > 1 || announcements.count == 0 {
// values 0 to 19
let soundfile = "say_\(value).wav"
announcements.append(soundfile)
}
return announcements
}
func playAnnouncements(_ announcements: [String] ) {
// announcements contains an array of wave filenames to play one after the other
}
You can then use a single AVAudioPlayer that you reload with the proper wave file, start to play, when the current sound is done playing, load up the next wave file in queue and play until the queue is empty. Depending on your requirements you may want the ability to abort playback, flush the announcement queue, etc.
Alternatively, you could have a wave file for every single digit in the supported range and simply number them say_0.mp3 to say_999.mp3; just gets really tedious if you start supporting larger numbers (1 million 3 hundred and 75 thousand six hundred and eighty eight)
Here's a fully working example:
//
// ViewController.swift
// SayNumber
//
// Created by Dave Poirier on 2017-09-21.
// Copyright © 2017 Soft.io. All rights reserved.
//
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate {
var valueToAnnonce: Int = 321
var audioPlayer: AVAudioPlayer!
var audioQueue: [String] = []
@IBAction
func announceValue(_ sender: Any?) {
verbalizeValue(valueToAnnonce)
}
func verbalizeValue(_ value: Int) {
let announcements = prepareAnnouncements(value)
playAnnouncements(announcements)
}
func prepareAnnouncements(_ value: Int) -> [String] {
var valueToProcess: Int = value
var announcements: [String] = []
if valueToProcess >= 100 {
// values 100 and above
let hundred = valueToProcess / 100
valueToProcess = valueToProcess % 100
let soundfile = "say_\(hundred)00"
announcements.append(soundfile)
}
if valueToProcess >= 20 {
// values 30 to 99
let dozen = valueToProcess / 10
valueToProcess = valueToProcess % 10
let soundfile = "say_\(dozen)0"
announcements.append(soundfile)
}
if valueToProcess >= 1 || announcements.count == 0 {
// values 0 to 19
let soundfile = "say_\(valueToProcess)"
announcements.append(soundfile)
}
return announcements
}
func playAnnouncements(_ announcements: [String] ) {
// announcements contains an array of wave filenames to play one after the other
if nil != audioPlayer && audioPlayer!.isPlaying {
print("Audio player was active, stop it and play the new announcements!")
audioPlayer.stop()
}
audioQueue.removeAll()
for filename in announcements {
let path = pathForAudioFile(filename)
if path != nil {
audioQueue.append(path!)
}
}
playNextInQueue()
}
func playNextInQueue() {
let nextPathInQueue = audioQueue.first
if nextPathInQueue != nil {
audioQueue.removeFirst(1)
let audioFileURL = URL(fileURLWithPath: nextPathInQueue!)
audioPlayer = try? AVAudioPlayer(contentsOf: audioFileURL)
guard audioPlayer != nil else {
print("Oops, file not found: \(nextPathInQueue!)")
return
}
print("playing \(nextPathInQueue!)")
audioPlayer?.delegate = self
audioPlayer?.play()
} else {
print("looks like we are all done!")
}
}
func pathForAudioFile(_ filename: String) -> String? {
return Bundle.main.path(forResource: filename, ofType: "wav")
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
DispatchQueue.main.async {
self.playNextInQueue()
}
}
}
In the console log when you press the button, the following is visible:
playing /var/containers/Bundle/Application/E2D5E7AD-26B4-4BBA-B52C-B91B62BA7161/SayNumber.app/say_300.wav
playing /var/containers/Bundle/Application/E2D5E7AD-26B4-4BBA-B52C-B91B62BA7161/SayNumber.app/say_20.wav
playing /var/containers/Bundle/Application/E2D5E7AD-26B4-4BBA-B52C-B91B62BA7161/SayNumber.app/say_1.wav
looks like we are all done!
The controller expects audio files say_0.wav through say_19.wav, then for every dozen from say_20.wav to say_90.wav, then same for hundreds from say_100.wav to say_900.wav. Code should be easy to adapt for any length of number.