swift - 通过 UIActivityViewController 将铃声分享到 Garageband
问题描述
我正在实现一个铃声应用程序,我想将音频文件分享到GarageBand让用户继续这个过程,
这是关于我如何共享媒体的代码
fileprivate func shareMedia(url: URL) {
let activityViewController:UIActivityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = saveAsRingtone
activityViewController.popoverPresentationController?.sourceRect = saveAsRingtone.frame
present(activityViewController, animated: true, completion: nil)
}
在这里回答我自己的问题是完整的答案
class AudioUtils {
class func trimAudio(audioToTrim: URL, startTime: Double, setExportSession: (AVAssetExportSession) -> Void, completion: @escaping (URL?, String?) -> Void) {
let audioFileInput = audioToTrim
let mixedAudio: String = "ringtone.aif"
let exportPathDirectory = NSTemporaryDirectory()
// let audioFileDirectory = URL(fileURLWithPath: exportPathDirectory).appendingPathComponent("Project").appendingPathExtension("band")
let projectBandDirectory = exportPathDirectory + "Project.band"
if (FileManager.default.fileExists(atPath: projectBandDirectory)) {
try! FileManager.default.removeItem(at: URL(fileURLWithPath: projectBandDirectory))
}
try! FileManager.default.createDirectory(atPath: projectBandDirectory, withIntermediateDirectories: true, attributes: nil)
let exportPath: String = projectBandDirectory + "/"
let bundlePath = Bundle.main.path(forResource: "projectData", ofType: "")
let fullDestPath = NSURL(fileURLWithPath: exportPath).appendingPathComponent("projectData")
let fullDestPathString = (fullDestPath?.path)!
try! FileManager.default.createDirectory(atPath: exportPath + "Media", withIntermediateDirectories: true, attributes: nil)
try! FileManager.default.createDirectory(atPath: exportPath + "Output", withIntermediateDirectories: true, attributes: nil)
let audioFileOutput = URL(fileURLWithPath: exportPathDirectory + mixedAudio)//.appendingPathExtension("band")
print("Will export to \(audioFileOutput.absoluteString)")
try? FileManager.default.removeItem(at: audioFileOutput)
let asset = AVAsset(url: audioFileInput)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough)
if (exportSession == nil) {
completion(nil, "ExportSession is nil")
return
}
setExportSession(exportSession!)
let startCMTime = CMTimeMakeWithSeconds(startTime, preferredTimescale: 1)
let stopCMtime = CMTimeMakeWithSeconds(startTime + 30, preferredTimescale: 1)
let exportTimeRange = CMTimeRangeFromTimeToTime(start: startCMTime, end: stopCMtime)
exportSession?.outputURL = audioFileOutput
exportSession?.outputFileType = AVFileType.caf
exportSession?.timeRange = exportTimeRange
exportSession?.exportAsynchronously {
switch (exportSession?.status) {
case .completed:
var options = AKConverter.Options()
// any options left nil will assume the value of the input file
options.format = "aif"
options.sampleRate = 48000
options.bitDepth = 24
let sourceUrl = audioFileOutput
let destUrl = URL(fileURLWithPath: exportPath + "Media/ringtone.aiff")
convertAudio(sourceUrl, outputURL: destUrl)
let fileData = try! Data.init(contentsOf: destUrl)
var fileStream:String = fileData.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
fileStream = String(fileStream.dropLast())
let playersDictionary = NSMutableDictionary(contentsOfFile: bundlePath!)
let playersNamesArray = (playersDictionary?.object(forKey: "$objects"))! as! NSMutableArray
let nsDataParentDictionary = (playersNamesArray.object(at: 4)) as! NSMutableDictionary
nsDataParentDictionary.setValue(fileStream, forKey: "NS.data")
playersNamesArray.removeObject(at: 4)
playersNamesArray.insert(nsDataParentDictionary, at: 4)
playersDictionary?.setValue(playersNamesArray, forKey: "$objects")
playersDictionary?.write(toFile: fullDestPathString, atomically: true)
do {
try FileManager.default.copyItem(atPath: bundlePath!, toPath: fullDestPathString)
completion(URL(fileURLWithPath: exportPath), nil)
} catch let exception {
completion(nil, exception.localizedDescription)
}
break
default:
completion(nil, exportSession?.error?.localizedDescription)
break
}
}
}
class func convertAudio(_ url: URL, outputURL: URL) {
var error : OSStatus = noErr
var destinationFile : ExtAudioFileRef? = nil
var sourceFile : ExtAudioFileRef? = nil
var srcFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
var dstFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
ExtAudioFileOpenURL(url as CFURL, &sourceFile)
var thePropertySize: UInt32 = UInt32(MemoryLayout.stride(ofValue: srcFormat))
ExtAudioFileGetProperty(sourceFile!,
kExtAudioFileProperty_FileDataFormat,
&thePropertySize, &srcFormat)
dstFormat.mSampleRate = 44100 //Set sample rate
dstFormat.mFormatID = kAudioFormatLinearPCM
dstFormat.mChannelsPerFrame = 1
dstFormat.mBitsPerChannel = 16
dstFormat.mBytesPerPacket = 2 * dstFormat.mChannelsPerFrame
dstFormat.mBytesPerFrame = 2 * dstFormat.mChannelsPerFrame
dstFormat.mFramesPerPacket = 1
dstFormat.mFormatFlags = kAudioFormatFlagIsBigEndian |
kAudioFormatFlagIsSignedInteger
// Create destination file
error = ExtAudioFileCreateWithURL(
outputURL as CFURL,
kAudioFileAIFFType,
&dstFormat,
nil,
AudioFileFlags.eraseFile.rawValue,
&destinationFile)
print("Error = \(error)")
error = ExtAudioFileSetProperty(sourceFile!,
kExtAudioFileProperty_ClientDataFormat,
thePropertySize,
&dstFormat)
print("Error = \(error)")
error = ExtAudioFileSetProperty(destinationFile!,
kExtAudioFileProperty_ClientDataFormat,
thePropertySize,
&dstFormat)
print("Error = \(error)")
let bufferByteSize : UInt32 = 32768
var srcBuffer = [UInt8](repeating: 0, count: 32768)
var sourceFrameOffset : ULONG = 0
while(true){
var fillBufList = AudioBufferList(
mNumberBuffers: 1,
mBuffers: AudioBuffer(
mNumberChannels: 2,
mDataByteSize: UInt32(srcBuffer.count),
mData: &srcBuffer
)
)
var numFrames : UInt32 = 0
if(dstFormat.mBytesPerFrame > 0){
numFrames = bufferByteSize / dstFormat.mBytesPerFrame
}
error = ExtAudioFileRead(sourceFile!, &numFrames, &fillBufList)
print("Error = \(error)")
if(numFrames == 0){
error = noErr;
break;
}
sourceFrameOffset += numFrames
error = ExtAudioFileWrite(destinationFile!, numFrames, &fillBufList)
print("Error = \(error)")
}
error = ExtAudioFileDispose(destinationFile!)
print("Error = \(error)")
error = ExtAudioFileDispose(sourceFile!)
print("Error = \(error)")
}
}
这里发生了什么?...首先,这里有一个修剪音频 30 秒的功能
我检查了一个现有的 .band 项目文件并获得了它的组件,并尝试以编程方式创建这些文件。请随时问我有关代码的任何问题
您应该使用 projectData 文件,这是在每个 .garageBand 文件夹中找到的文件 https://www.dropbox.com/s/7r7uh2ekzigyy3u/projectData?dl=0
解决方案
这种在 Garageband 上共享曲目的方法没有问题
只需确保您的共享文件扩展名是“band”
.appendingPathExtension("band")
不是别的
推荐阅读
- python-3.x - 如何在 Python 中向 DBUS 公开信号?
- c# - How do I stop and restart a looping animation when I use 'Input.GetKeyUp'?
- pandas - Grabbing an unordered index given a matching criteria
- oauth-2.0 - 使用链接的工作流权限问题登录
- angular - UserDataSource 在 Angular 中没有数据属性
- c# - Unable to parse JSON array API in Xamarin.Forms to ListView
- 8thwall-xr - 远程应用程序能否以更高的分辨率运行
- python - deep copy nested iterable (or improved itertools.tee for iterable of iterables)
- reactjs - Reactjs 显示隐藏多个组件
- java - 用于生成 Cron 表达式的类型转换