Thanks in advance for the help!
I am trying to record calls using the Linphone SDK in Swift on Mac OS, and am having trouble passing a path into the function:
func linphone_call_params_set_record_file(_ cp: OpaquePointer!, _ path: UnsafePointer<Int8>!)
that works correctly (the SDK is written in C, though I am accessing it using Swift and a bridging header). The Linphone SDK works properly, and I can make and receive calls programmatically, with full audio support.
In trying to invoke the call recorder, I pass this function a path (pathtofile), such as:
let pathtofile = "/Users/Alex/Safety/1.wav"
where I would like to store the recording file.
func SafetyNetAVRecorderInitializer(pathtofile: String) -> Bool {
// Convert pathtofile to UnsafePointer<Int8>.
let cpathtofile = (pathtofile as NSString).utf8String
let path = UnsafeMutablePointer<Int8>(mutating: cpathtofile)
// Actually begin call recording.
if currentcall != nil {
let currentcallparameters = linphone_call_get_current_params(currentcall)
linphone_call_params_set_record_file (currentcallparameters, path)
linphone_call_start_recording(currentcall)
return true
}
return false
}
No runtime errors are encountered on linphone_call_params_set_record_file(), but when I try to invoke linphone_call_start_recording(), the recording does not begin, and an error is printed in the console that reads:
ortp-error-linphone_call_start_recording(): no output file specified. Use linphone_call_params_set_record_file().
How can I correctly pass a valid path to linphone_call_params_set_record_file()? I have tried directly passing a plain Swift String instead of an UnsafePointer<Int8> to no avail. Am I just misunderstanding how paths are formatted in C?
For reference, the SDK method source is:
void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){
if (cp->record_file){
ms_free(cp->record_file);
cp->record_file=NULL;
}
if (path) cp->record_file=ms_strdup(path);
}
Thanks again!
Try this:
let cpathtofile = (pathtofile as NSString).utf8String! // Unwraps!
...
inphone_call_params_set_record_file(currentcallparameters, cpathtofile)
Related
I have a library parsing FIT file in swift using an externally provided c library. The parsing function takes as argument a void * data.
To call the function, I was converting the data using data.withUnsafeBytes( { (ptr: UnsafePointer<UInt8>) in ...} to build the argument to the c function and it was working fine.
After the upgrade of Xcode to swift 5, I now get a deprecated warning
'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead
I couldn't work out how to fix the code to remove the deprecated warning. The code has been working fine and without warning in swift 4
I tried to change the argument in the closure to take UnsafeRawBufferPointer instead of the UnsafePointer but this resulted in an error in calling the function: Cannot convert 'UnsafeRawBufferPointer' to expected argument type 'UnsafeRawPointer?'
This is a small swift file to show the problem:
import Foundation
// Create sample data (Typically would be read from a file
let data = Data(repeating: 1, count: 10)
data.withUnsafeBytes( { (ptr : UnsafePointer<UInt8>) in
// call the c function with the void* argument
let value = readFITfile(ptr)
print( value )
})
And an example c function
unsigned readFITfile(const void * data){
//Silly example to show it works, just returning the value of pointer as int
//Typically would parse the data and return a structure
return (unsigned)data;
}
I saved a small repo with the above code here https://github.com/roznet/swift2c and the full scale project with the parsing of the file is here https://github.com/roznet/fit-sdk-swift
You have to change the closure argument to UnsafeRawBufferPointer and then take its baseAdress (which is a UnsafeRawPointer?, the Swift equivalent of void * in C):
data.withUnsafeBytes( { (ptr : UnsafeRawBufferPointer) in
let value = readFITfile(ptr.baseAddress)
// ...
})
The Swift compiler can also infer the closure argument type automatically:
data.withUnsafeBytes( { ptr in
let value = readFITfile(ptr.baseAddress)
// ...
})
For more information about this problem, see withUnsafeBytes Data API confusion in the Swift forum.
To get UnsafePointer now you should do something like that
data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
if let ptrAddress = ptr.baseAddress, ptr.count > 0 {
let pointer = ptrAddress.assumingMemoryBound(to: UInt8.self) // here you got UnsafePointer<UInt8>
let value = readFITfile(ptr)
print( value )
} else {
// Here you should provide some error handling if you want ofc
}
}
I am trying to save a simple array of objects in the persistent memory by executing the following code:
let fileManager=NSFileManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if urls.count>0{
let localDocumentsDirectory=urls[0]
let archivePath=localDocumentsDirectory.URLByAppendingPathExtension("meditations.archive")
NSKeyedArchiver.archiveRootObject(self.meditationsArray, toFile: archivePath.path!)
let restored=NSKeyedUnarchiver.unarchiveObjectWithFile(archivePath.path!)
print("restored \(restored)")
}
}
Yet, when I print the restored date as in the code I find nil.
Conversely, if I use the CachesDirectory the array is soon after restored fine,
but when I reopen the app and try to load the data, it is lost. What is correct way to persistently save data?
I think the problem is that your are using URLByAppendingPathExtension, when you should be using URLByAppendingPathComponent. The "path extension" is the file extension, so your archivePath is "~/Documents.meditations.archive". It might be temporarily working with the CachesDirectory, because it's putting the data into a temporary file somewhere, or maybe just reading it back from memory. This should fix it:
let fileManager = NSFileManager()
let documentDirectoryUrls = fileManager.URLsForDirectory(.DocumentDirectory, .UserDomainMask)
if let documentDirectoryUrl = documentDirectoryUrls.first {
let fileUrl = documentDirectoryUrl.URLByAppendingPathComponent("meditations.archive")
// Also, take advantage of archiveRootObject's return value to check if
// the file was saved successfully, and safely unwrap the `path` property
// of the URL. That will help you catch any errors.
if let path = fileUrl.path {
let success = NSKeyedArchiver.archiveRootObject(meditationArray, toFile: path)
if !success {
print("Unable to save array to \(path)")
}
} else {
print("Invalid path")
}
} else {
print("Unable to find DocumentDirectory for the specified domain mask.")
}
I faced the same issue, I was unable to archive and unarchive array of objects using NSKeyedArchiver, I think the issue is that I'm using the below method :
NSKeyedArchiver.archiveRootObject(arrayOfItems, toFile: FileManager.getFileURL("My-File-Name")!)
I think this method is for archiving Objects, not array of Objects.
Anyway, I found a solution to my problem, by wrapping the whole array in an object, check below :
let myArrayItemsContainer = ArrayItemsContainer()
myArrayItemsContainer.allItems = arrayOfItems
NSKeyedArchiver.archiveRootObject(myArrayItemsContainer, toFile: FileManager.getFileURL("My-File-Name")!)
and I used the below code to unarchive my object :
NSKeyedUnarchiver.unarchiveObject(withFile: FileManager.getFileURL("My-File-Name")!) as? ArrayItemsContainer
Also I used this extension for using FileManager.getFileURL
public extension FileManager {
/// Returns the URL of the file given a name
///
/// - Parameter fileName: The file name of the file + extension
/// - Returns: The URL as String
static func getFileURL(_ fileName: String) -> String? {
let fileURL = FileManager().urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
return (fileURL?.appendingPathComponent(fileName).path)
}
}
I'm trying and failing to create an instance of AudioServicesSystemSoundCompletionProc for an argument in AudioServicesAddSystemSoundCompletion using Swift in Xcode.
Here's what I've got so far
func completionCallback(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void {
}
var foo:(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void = completionCallback;
AudioServicesAddSystemSoundCompletion(soundID, nil, nil, foo, nil);
I wrote this with the help of some guides explaining how to write equivalent C Function Pointers in Swift, but this throws this error:
'(ssID: SystemSoundID, clientData: UnsafeMutablePointer<Void>) -> Void' is not convertible to 'AudioServicesSystemSoundCompletionProc'
The documentation shows the Objective-C declaration:
typedef void (*AudioServicesSystemSoundCompletionProc) ( SystemSoundID ssID, void *clientData );
This is declaration shown when using Xcode:
typealias AudioServicesSystemSoundCompletionProc = CFunctionPointer<((SystemSoundID, UnsafeMutablePointer<Void>) -> Void)>
I'm not sure how to implement AudioServicesSystemSoundCompletionProc correctly in Swift.
You could do it as a closure, as of Swift 2.0.
AudioServicesAddSystemSoundCompletion(soundID, nil, nil, { (soundID, clientData) -> Void in
// Your completion callback...
AudioServicesDisposeSystemSoundID(soundID)
}, nil)
Further info from Apple (scroll down to Function Pointers):
C function pointers are imported into Swift as closures
David's answer is correct. But just to clarify, AudioServicesSystemSoundCompletionProc needs to be done in objective-c, then bridged across to swift. You can either write an implementation yourself, or... it is already done for you here: https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0 (as mentioned by David)
You need to go to that link, download the files FunctionPointer.h and FunctionPointer.c, put it in your project folder, then link it to swift using bridging header.
To do that:
Go to your project's build settings, scroll down to where it says Objective-C Bridging Header, edit the content to "YourProject/Bridging-Header.h" (where YourProject is the name of your project)
Create an objective-c header file in your project, and name it Bridging-Header.h
Inside the header file, add #import "FunctionPointer.h"
Now you can access FunctionPointer anywhere inside your swift project... sweet
Back to the compeletion handler, in your code, where you want to use AudioServicesAddSystemSoundCompletion, do something like this:
// at the top of the class
var functionPointer: AudioServicesCompletionFunctionPointer?
// in the code
var me = self
let userData = withUnsafePointer(&me) { ptr in
return unsafeBitCast(ptr, UnsafeMutablePointer<Void>.self)
}
self.functionPointer = AudioServicesCompletionFunctionPointer(systemSoundID: soundId, block: {(systemSoundID: SystemSoundID, userData: UnsafeMutablePointer<Void>) -> () in
// sound has ended, do your stuff here
}, userData: userData)
AudioServicesAddSystemSoundCompletion(soundId, CFRunLoopGetMain(), kCFRunLoopDefaultMode, AudioServicesCompletionFunctionPointer.completionHandler(), userData)
where you'll have to change NSObject to TheClassYou'reUsingThisIn and soundId to your soundId
let myData = unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
AudioServicesAddSystemSoundCompletion(YOUR_SOUND_ID, CFRunLoopGetMain(), kCFRunLoopDefaultMode,{ (mSound, mVoid) in
let me = unsafeBitCast(mVoid, YOURCURRENTCLASS.self)
//me it is your current object so if yo have a variable like
// var someVar you can do
print(me.someVar)
}, myData)
The answer for this can be seen here: https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0
Solution by Julius Parishy (https://stackoverflow.com/users/1164263/julius-parishy)
This works in Swift 3:
let weakSelf = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
let error = AudioServicesAddSystemSoundCompletion(soundId,
nil,
nil,
{soundId, weakSelfPointer in
guard let weakSelfPointer = weakSelfPointer else {
return
}
let weakSelfValue = Unmanaged<NAME_OF_SELF_CLASS>.fromOpaque(weakSelfPointer).takeUnretainedValue()
// Then you can use `weakSelfValue` as you would do with `self`.
weakSelfValue.A_METHOD_OF_SELF
}, weakSelf)
I am writing binding for a C library, I'd like to call std::io::File::open as it's got error handling already. I then intend to pass the fd to C function.
I have looked at std::io::fs, but the fd field is nothing like what I would have thought.
After some more digging I found native::io::file::FileDesc, which indeed has fn fd(&self) -> fd_t, but doesn't seem like this is something I can access from an instance of std::io::File.
There appear to be fs_from_raw_fd method, it's the exact opposite to what I need.
use std::os::unix::io::AsRawFd;
let path = Path::new("my.txt");
let display = path.display();
let mut file = match File::open(&path) {
// The `description` method of `io::Error` returns a string that
// describes the error
Err(why) => panic!("couldn't open {}: {}", display,
why.description()),
Ok(file) => file,
};
println!("file descriptor: {}",file.as_raw_fd());
The closest you can get in the current version of Rust is via native::io::file::open.
use native::io::file::open;
use std::rt::rtio::{Open, Read};
let file = match open(&path.to_c_str(), Open, Read) {
Ok(file) => file,
Err(_) => return,
}
let fd = file.fd();
is there any function in go that is similar like "_file_" or "_line_" in go, to know who is calling a specific function during run time? In C we have the "_file_" line that can be called as macros. How to do this in go?
If you're using the log package, you can instruct the logger to prefix the entries with various information. You'll likely be most interested in the Lshortfile constant, which will result in prefixes along the lines of d.go:23. Alternatively, there is Llongfile which prints the file's full path (such as /a/b/c/d.go:23).
If you don't want to use the log package, you could also use runtime.Caller(), which is what the log package uses internally. It's not as straight forward as the C macros, but you could hide it behind a function (and specify the correct call depth). You can see how the log package is implemented for an example (line 140).
(1) Write a brief function that calls runtime.Caller()
(2) Call that function everywhere you want to access the source code file and line number at run time.
Example:
import "runtime"
func file_line() string {
_, fileName, fileLine, ok := runtime.Caller(1)
var s string
if ok {
s = fmt.Sprintf("%s:%d", fileName, fileLine)
} else {
s = ""
}
return s
}
Note: pass 1 to Caller() so that it returns the number of the line where file_line() is called, instead of where runtime.Caller() is called.
fmt.Println(file_line()) // Prints this file and line number.
See the runtime and runtime.debug packages and in particular the Stack, PrintStack or Callerfunctions.
Stack formats a stack trace of the calling goroutine into buf and returns the number of bytes written to buf. If all is true, Stack formats stack traces of all other goroutines into buf after the trace for the current goroutine.
If you are compiling with debug information, then this will should contain the line number in source
//import ( "log" "strings" "bytes" "path/filepath" )
func SRC_FILE() string {
var buf bytes.Buffer
log.New(&buf, "", log.Lshortfile).Output(2, "")//2=>CALLER
__FILE_LINE__ := strings.TrimSpace(buf.String())
__FILE__ := strings.TrimRight(__FILE_LINE__, ":1234567890")
return __FILE__
}
func SRC_DIR() string {
var buf bytes.Buffer
log.New(&buf, "", log.Llongfile).Output(2, "")//2=>CALLER
__FILE_LINE__ := strings.TrimSpace(buf.String())
__FILE__ := strings.TrimRight(__FILE_LINE__, ":1234567890")
__DIR__ := filepath.Dir(__FILE__)
return __DIR__
}
func main() {
fmt.Println(SRC_FILE())//analogous to __FILE__
fmt.Println(SRC_DIR()) //analogous to __DIR__
}
https://go.dev/play/p/3nvgknX96RO
When you run it on go.dev/play your code is run as prog.go