2

I can't understand, how I should use UnsafeMutablePointer in Swift 3. Especially in AURenderCallback.

I try below code:

import Foundation
import AudioToolbox

let sineFrequency = 880.0

// MARK: User data struct
struct SineWavePlayer {
    var outputUnit: AudioUnit? = nil
    var startingFrameCount: Double = 0
}

// MARK: Callback function
let SineWaveRenderProc: AURenderCallback = {(inRefCon, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData) -> OSStatus in

    var player = UnsafeMutablePointer<SineWavePlayer>(inRefCon)


    var j = player.pointee.startingFrameCount
    let cycleLength = 44100 / sineFrequency

    for frame in 0..<inNumberFrames {
        var buffers = UnsafeMutableAudioBufferListPointer(ioData)

        UnsafeMutablePointer<Float32>(buffers[0].mData)[Int(frame)] = Float32(sin(2 * M_PI * (j / cycleLength)))
        UnsafeMutablePointer<Float32>(buffers[1].mData)[Int(frame)] = Float32(sin(2 * M_PI * (j / cycleLength)))

        // Or iterate through array:
//        for buffer in buffers {
//            UnsafeMutablePointer<Float32>(buffer.mData)[Int(frame)] = Float32(sin(2 * M_PI * (j / cycleLength)))
//        }

        j++
        if j > cycleLength {
            j -= cycleLength
        }
    }

    player.pointee.startingFrameCount = j
    return noErr
}

// MARK: Utility function
func CheckError(_ error: OSStatus, operation: String) {
    guard error != noErr else {
        return
    }

    var result: String = ""
    var char = Int(error.bigEndian)

    for _ in 0..<4 {
        guard isprint(Int32(char&255)) == 1 else {
            result = "\(error)"
            break
        }
        result.append(String(describing: UnicodeScalar(char&255)))
        char = char/256
    }

    print("Error: \(operation) (\(result))")

    exit(1)
}

func CreateAndConnectOutputUnit(_ player: inout SineWavePlayer) {
    // Generate a description that matches the output device (speakers)
    var outputcd = AudioComponentDescription(componentType: kAudioUnitType_Output, componentSubType: kAudioUnitSubType_DefaultOutput, componentManufacturer: kAudioUnitManufacturer_Apple, componentFlags: 0, componentFlagsMask: 0)

    let comp = AudioComponentFindNext(nil, &outputcd)

    if comp == nil {
        print("Can't get output unit")
        exit(-1)
    }

    CheckError(AudioComponentInstanceNew(comp!, &player.outputUnit),
        operation: "Couldn't open component for outputUnit")

    // Register the render callback
    var input = AURenderCallbackStruct(inputProc: SineWaveRenderProc, inputProcRefCon: &player)

    CheckError(AudioUnitSetProperty(player.outputUnit!, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, UInt32(MemoryLayout<AURenderCallbackStruct>.size)),
        operation: "AudioUnitSetProperty failed")

    // Initialize the unit
    CheckError(AudioUnitInitialize(player.outputUnit!),
        operation: "Couldn't initialize output unit")
}

func main() {
    var player = SineWavePlayer()

    // Set up output unit and callback
    CreateAndConnectOutputUnit(&player)

    // Start playing
    CheckError(AudioOutputUnitStart(player.outputUnit!),
        operation: "Couldn't start output unit")

    // Play for 5 seconds
    sleep(5)

    // Clean up
    AudioOutputUnitStop(player.outputUnit!)
    AudioUnitUninitialize(player.outputUnit!)
    AudioComponentInstanceDispose(player.outputUnit!)
}

main()

But this line of code

var player = UnsafeMutablePointer<SineWavePlayer>(inRefCon) 

is not working. How to translate this line to Swift 3?

Please, help me.

Kaushik Makwana
  • 1,329
  • 2
  • 14
  • 24
Ruslan
  • 67
  • 1
  • 6
  • You can use the same approach as in http://stackoverflow.com/a/30788165/1187415. – Martin R Dec 29 '16 at 21:16
  • @MartinR, `SineWavePlayer` is struct, so the same approach would not work. – OOPer Dec 29 '16 at 22:41
  • @OOPer: the refcon is a pointer, so making `SineWavePlayer` a class make more sense anyway. – Martin R Dec 29 '16 at 22:47
  • can you write some lines of code? How it should be? – Ruslan Dec 29 '16 at 23:22
  • @MartinR, refCon is a general purpose pointer of type `void *`. Passing an object reference to `void *` is sort of a hack. I really doubt if such hacks _make more sense_. – OOPer Dec 29 '16 at 23:51
  • @OOPer: Well, that was perhaps badly phrased. But tunnelling a reference to an object through a C callback via void pointer is certainly an option, for example (in Objective-C) done here: https://www.cocoawithlove.com/2010/10/ios-tone-generator-introduction-to.html. Of course your solution works as well. – Martin R Dec 30 '16 at 12:37
  • @MartinR, True. I use "hack" with very positive meanings and I would write an answer very similar to the one in your first link, if the code in question was mainly class based. Maybe I should have expressed such things better. Anyway, thanks for clarification and kind comments (as always). – OOPer Dec 30 '16 at 13:33

1 Answers1

5

In Swift 3, initializers cannot be used to convert pointer types. In your case, the type of inRefCon is UnsafeMutableRawPointer, so you need to use assumingMemoryBound(to:) method.

And one more, the address of player passed to the callback needs to be stable all while the sound is playing, addresses taken from inout arguments (specified by & prefix) does not fulfil this requirement.

The two things above fixed, your code would be something like this:

import Foundation
import AudioToolbox

let sineFrequency = 880.0

// MARK: User data struct
struct SineWavePlayer {
    var outputUnit: AudioUnit? = nil
    var startingFrameCount: Double = 0
}

// MARK: Callback function
let SineWaveRenderProc: AURenderCallback = {(inRefCon, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData) -> OSStatus in

    var player = inRefCon.assumingMemoryBound(to: SineWavePlayer.self)


    var j = player.pointee.startingFrameCount
    let cycleLength = 44100 / sineFrequency

    for frame in 0..<inNumberFrames {
        var buffers = UnsafeMutableAudioBufferListPointer(ioData)

        buffers?[0].mData?.assumingMemoryBound(to: Float32.self)[Int(frame)] = Float32(sin(2 * M_PI * (j / cycleLength)))
        buffers?[1].mData?.assumingMemoryBound(to: Float32.self)[Int(frame)] = Float32(sin(2 * M_PI * (j / cycleLength)))

        j += 1
        if j > cycleLength {
            j -= cycleLength
        }
    }

    player.pointee.startingFrameCount = j
    return noErr
}

// MARK: Utility function
func CheckError(_ error: OSStatus, operation: String) {
    guard error != noErr else {
        return
    }

    var result: String = ""
    var char = Int(error.bigEndian)

    for _ in 0..<4 {
        guard isprint(Int32(char&255)) == 1 else {
            result = "\(error)"
            break
        }
        result.append(String(describing: UnicodeScalar(char&255)))
        char = char/256
    }

    print("Error: \(operation) (\(result))")

    exit(1)
}

func CreateAndConnectOutputUnit(_ playerPtr: UnsafeMutablePointer<SineWavePlayer>) {
    // Generate a description that matches the output device (speakers)
    var outputcd = AudioComponentDescription(componentType: kAudioUnitType_Output, componentSubType: kAudioUnitSubType_DefaultOutput, componentManufacturer: kAudioUnitManufacturer_Apple, componentFlags: 0, componentFlagsMask: 0)

    let comp = AudioComponentFindNext(nil, &outputcd)

    if comp == nil {
        print("Can't get output unit")
        exit(-1)
    }

    CheckError(AudioComponentInstanceNew(comp!, &playerPtr.pointee.outputUnit),
               operation: "Couldn't open component for outputUnit")

    // Register the render callback
    var input = AURenderCallbackStruct(inputProc: SineWaveRenderProc, inputProcRefCon: playerPtr)

    CheckError(AudioUnitSetProperty(playerPtr.pointee.outputUnit!, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, UInt32(MemoryLayout<AURenderCallbackStruct>.size)),
               operation: "AudioUnitSetProperty failed")

    // Initialize the unit
    CheckError(AudioUnitInitialize(playerPtr.pointee.outputUnit!),
               operation: "Couldn't initialize output unit")
}

func main() {
    let playerPtr = UnsafeMutablePointer<SineWavePlayer>.allocate(capacity: 1)
    defer {playerPtr.deallocate(capacity: 1)}
    playerPtr.initialize(to: SineWavePlayer())
    defer {playerPtr.deinitialize()}

    // Set up output unit and callback
    CreateAndConnectOutputUnit(playerPtr)

    // Start playing
    CheckError(AudioOutputUnitStart(playerPtr.pointee.outputUnit!),
               operation: "Couldn't start output unit")

    // Play for 5 seconds
    sleep(5)

    // Clean up
    AudioOutputUnitStop(playerPtr.pointee.outputUnit!)
    AudioUnitUninitialize(playerPtr.pointee.outputUnit!)
    AudioComponentInstanceDispose(playerPtr.pointee.outputUnit!)
}
OOPer
  • 47,149
  • 6
  • 107
  • 142