I am extremely new to writing code in Swift. Only part way through one book - iOS 10 App Development Essentials by Neil Smyth. But I have a question about setting constants and specifically looking for a maximum of two doubles that were assigned.
I have the following code:
Let Transfer1055 = 10
Let Diameter1: Double = 6.25
Let Diameter2: Double = 8.125
Let CenterDim: Double = 12
Let StructureWidth: Double = 60
Let Rad1 = Diameter1 / 2
Let Rad2 = Diameter2 / 2
Let RadMax = Max(Rad1,Rad2)
The problem I am having is in the last line, I can't get the max() function to work properly. Having Diameter1 set to 6.25 and Diameter2 set to 8.125. This would make Rad1=3.125 and Rad2=4.0625. I want RadMax to equal 4.0625, then I want to use that later in the code, but I can't get it to work.
Any suggestions would help. Thanks!
UPDATE: Thanks for the suggestions. Yes, this is just preliminary code in Playground to try out writing some variables and learning how to use a few math functions.
Works as expected
let transfer1055 = 10
let diameter1: Double = 6.25
let diameter2: Double = 8.125
let centerDim: Double = 12
let structureWidth: Double = 60
let rad1 = diameter1 / 2
let rad2 = diameter2 / 2
let radMax = max(rad1, rad2)
print(radMax) // Prints: "4.0625\n"
Just refactor your code a little:
let transfer1055 = 10
let diameter1: Double = 6.25
let diameter2: Double = 8.125
let centerDim: Double = 12
let structureWidth: Double = 60
let rad1 = diameter1 / 2
let rad2 = diameter2 / 2
let radMax = max(rad1,rad2)
And radMax is equal to rad2.
Where do you have this block on code? In playground it should work. Above viewDidLoad in a viewController it might require you to make the computed variable lazy.
Related
In my app, I give the user the option to play a small frame of audio (from a larger audio file)in order to listen over and over to do a manual transcription. AKPlayer makes this trivial. Now, because the frame of audio is pretty small, it's pretty intense to hear this loop over and over (a little maddening in the classical sense of the word). I'd like to either fade it out/fade it back in with the loop OR just inject like 500 ms of silence before the loop starts again. I have no idea where to start, here is the current working code as is:
public func playLoop(start: Double, end: Double) {
self.chordLoopPlayer.isLooping = true
self.chordLoopPlayer.buffering = .always
self.chordLoopPlayer.preroll()
let millisecondsPerSample : Double = 1000 / 44100
let startingDuration : Double = (((start * millisecondsPerSample) / 1000) / 2)
let endingDuration : Double = (((end * millisecondsPerSample) / 1000) / 2)
print("StartinDuration:\(startingDuration) | EndingDuration:\(endingDuration)")
self.chordLoopPlayer.loop.start = startingDuration
self.chordLoopPlayer.loop.end = endingDuration
self.chordLoopPlayer.play(from: startingDuration, to: endingDuration)
Thanks so much <3
You just need to set .fade values for your fade-in/fade-out prior to calling the play() function. AudioKit will execute them each time going in and out of the loop. So assuming you'd like a 2-second fade-out, and a 2-second fade-in (adjust to your taste), your code would look like:
public func playLoop(start: Double, end: Double) {
self.chordLoopPlayer.isLooping = true
self.chordLoopPlayer.buffering = .always
self.chordLoopPlayer.preroll()
let millisecondsPerSample : Double = 1000 / 44100
let startingDuration : Double = (((start * millisecondsPerSample) / 1000) / 2)
let endingDuration : Double = (((end * millisecondsPerSample) / 1000) / 2)
print("StartinDuration:\(startingDuration) | EndingDuration:\(endingDuration)")
self.chordLoopPlayer.loop.start = startingDuration
self.chordLoopPlayer.loop.end = endingDuration
// add fade in/out values to fade in or fade out during playback; reset to 0 to disable.
self.chordLoopPlayer.fade.inTime = 2 // in seconds
self.chordLoopPlayer.fade.outTime = 2 // in seconds
self.chordLoopPlayer.play(from: startingDuration, to: endingDuration)
}
I find the AudioKit documentation a bit frustrating in this respect, as it's not super-easy to find these properties if you don't already know what you're looking for, or to understand how to use them if you haven't already come across sample code, so I hope this is a useful example for others who happen to search on this topic on SO. In any case, the list of sub-properties associated with AudioKit's .fade property is here: https://audiokit.io/docs/Classes/AKPlayer/Fade.html
I am currently using FDWaveFormView to great success to display waveforms representing audio I record from AKMicrophone or AKAudioFile.
I am successfully able to highlight specific regions in the waveform and FDwaveForm gives back a range of the samples from the audiofile.
My problem now is I cannot find an appropriate method in AKPlayer that would let me play from a start sample to an end sample.
I noticed that AKSamplePlayer is now deprecated, but it did have a method:play(from: Sample, to: Sample)
My guess is that I would be able to do some math to translate Sample position to time (as a Double as prescribed in AKPlayer), however I have not found the appropriate math or functions to do this, any hints?
To be very explicit in what I am trying to do, please refer to the image below:
note for any AudioKit core members who may see this question, I know there are a variety of AudioKitUI components that may of made this easier, however only FDWaveFormView has given me the functionality I need for this particular app, i'm happy to discuss further offline, thanks again so much.
EDIT
I've come up with some code that I believe has solved it:
let startingSampleIndex = self.waveformPlot.highlightedSamples!.min()
let endingSampleIndex = self.waveformPlot.highlightedSamples!.max()
let millisecondsPerSample : Double = 1000 / 44100
let startingDuration : Double = (startingSampleIndex! * millisecondsPerSample) / 1000
let endingDuration : Double = (endingSampleIndex! * millisecondsPerSample) / 1000
print("StartSample:\(startingSampleIndex!) | EndSample:\(endingSampleIndex!) | milliPerSample:\(millisecondsPerSample) | StartDuration:\(startingDuration) | EndDuration:\(endingDuration)")
player.play(from: startingDuration, to: endingDuration)
The main equation being numberOfSamples * millisecondsPerSample = timeInMilliseconds by dividing by 1000 I can normalize everything to seconds which is what AKPlayer wants. If anyone sees something problematic here I'd love the advice but I think this has done it! Sorry I am still new to DSP and so thankful for AudioKit being an incredible Shepard into this world!
To convert from frames to seconds you should divide by the sample rate of the audio file, not a hardcoded 44100 value:
guard let frameRange = self.waveformPlot.highlightedSamples else { return }
let startTime = frameRange.min() / audioFile.fileFormat.sampleRate
let endTime = frameRange.max() / audioFile.fileFormat.sampleRate
player.play(from: startTime, to: endTime)
I found the solution, essentially RTFM on DSP 101 and samples 😅:
let startingSampleIndex = self.waveformPlot.highlightedSamples!.min()
let endingSampleIndex = self.waveformPlot.highlightedSamples!.max()
let millisecondsPerSample : Double = 1000 / 44100
let startingDuration : Double = (startingSampleIndex! * millisecondsPerSample) / 1000
let endingDuration : Double = (endingSampleIndex! * millisecondsPerSample) / 1000
print("StartSample:\(startingSampleIndex!) | EndSample:\(endingSampleIndex!) | milliPerSample:\(millisecondsPerSample) | StartDuration:\(startingDuration) | EndDuration:\(endingDuration)")
player.play(from: startingDuration, to: endingDuration)
This is working excellently, thanks again to both FDWaveFormView and AudioKit!
I have the following number
7.9775609756097534
and I'm using the code below to only show two decimals
let formatted = String(format: "Angle: %.2f", angle)
the problem is that the result is:
7.98
instead of
7.97
For cases such as yours we use NumberFormatter. It is a class designed to do what you need and more. For your case it should be enough to use the following:
let numberFormatter = NumberFormatter()
numberFormatter.roundingMode = .down
numberFormatter.minimumFractionDigits = 2
numberFormatter.maximumFractionDigits = 2
numberFormatter.string(from: 1.236)
This now locks fraction digits to be exactly 2. By increasing minimum fraction digits more "0" may be appended as in 0.10 may become 0.100. Maximum fraction digits will simply restrict up to what point the number will be displayed.
There are other options as well such as making 1234567.89 show as 1.234.567,89 which is really nice for users that are used to such formatting.
Alternatively, you can do bit string manipulation
let numberInFloat:Float = 7.9775609756097534
let numberInString: String = String(format: "%f", numberInFloat)
let numberParts = numberInString.components(separatedBy: ".")
print(String(format: "Output: %#.%#", String(numberParts[0]), String(numberParts[1].prefix(2))))
Output: 7.97
I have a slider in my app. I am using the value of slider to calculate another value in my code. Here is the code.
let P = slider1.value
let mulValue = 1.00
let i = P * mulValue
print("i:", i)
Slider min value = 0 , Slider Max value = 9,999,999. If the slider value is more than 1,000,000 the value of i is displayed as exponential number.
For example, value of "i" printed: 1.55058e+06 in console for P = 1,550,580
I want the full number to be printed instead of exponential number. How do I correct this issue?
As per the comments below, I tried using NSNumberFormatter(). But there resulted number is rounded. I do not want my "i" to be rounded. Below is code snippet I used, Please correct me if I'm wrong. Thanks !!
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
let finalNumber = numberFormatter.numberFromString(String(i))
print("fin num:", finalNumber)
Thanks for all your inputs.
There is slight different in the usage of NSNumberFormatter()
The following code worked for my code perfectly.
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
let finalNumber = numberFormatter.stringFromNumber(i)!
print("fin:", finalNumber)
I used stringFromNumber instead of numberFromString
In my iOS swift application, I receive some json from the web which contains some double values which represent currency. It looks like this:
[{"Amount": 5.0},{"Amount":-26.07},{"Amount": 4}, ...etc]
I cast these as Doubles and then try to feed these values as a Swift "Double" into the NSDecimalNumber's constructor like this:
let amount = NSDecimalNumber(double: amountAsDouble)
I'm running into problems with this approach because very frequently the NSDecimalNumber I created will contain a different number that goes 16 places passed the decimal point.
let amount = NSDecimalNumber(double: -15.97)
println(amount)
this returns -15.970000000000004096
I don't want this, I want -15.97.
Thanks,
A Double is stored with 18 decimal digits, you can't do anything about that, it's how it works.
Read here: http://en.wikipedia.org/wiki/Double-precision_floating-point_format
However, at the time of displaying the value on the screen, you can use NSNumberFormatter like this:
let amountInDouble: Double = -15.970000000000004096
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.roundingIncrement = 0.01
formatter.maximumFractionDigits = 2
let amountAsString = formatter.stringFromNumber(NSNumber(double: amountInDouble))
if let amountAsString = amountAsString {
println(amountAsString) // -15.97
}
I recently went through this for myself. I ended up using an NSNumberFormatter to get the proper decimal places.
let currFormatter = NSNumberFormatter()
currFormatter.numberStyle = .DecimalStyle
currFormatter.roundingIncrement = 0.01
currFormatter.minimumFractionDigits = 2
currFormatter.maximumFractionDigits = 2
let doubleAmount = currFormatter.numberFromString(amountAsDouble) as NSNumber!
let amount = doubleAmount as Double
println(amount)
Here's a tip: If you use NSJSONSerializer, numbers with decimal points are actually turned into NSDecimalNumber for you. NSDecimalNumber is a subclass of NSNumber. So what you are doing: You've got a perfectly fine NSDecimalNumber, round the value to double, and try to turn the double back into an NSDecimalNumber. Just check that what you have is indeed an NSDecimalNumber, and do no conversion if it is.
This is because the intermediate double representation is causing problems.
You should take the values from your dictionary as NSString objects and use the + decimalNumberWithString: method to convert without losing precision. In swift:
let amount = NSDecimalNumber(string: amountAsString)
let amount = NSDecimalNumber.init(value: -15.97)
let roundValue = amount.rounding(accordingToBehavior: NSDecimalNumberHandler(roundingMode: .bankers, scale: 2, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false))
print(roundValue)