こうすればいいらしい。
extension DateFormatter { public static let standard: DateFormatter = { let obj = DateFormatter() obj.locale = Locale(identifier:"en_US_POSIX") return obj }() }
こうすればいいらしい。
extension DateFormatter { public static let standard: DateFormatter = { let obj = DateFormatter() obj.locale = Locale(identifier:"en_US_POSIX") return obj }() }
swiftでSingletonを書く場合、こうすればいいらしい。
class MySingleton { static let shared = MySingleton() private init() {} }
CoreAudioでよく使われるAudioStreamBasicDescription
これはC言語の構造体で使いづらいです。
macOS10.10, iOS8以降ではAVAudioFormatと相互変換できますので、これを経由するとコーディングが簡単になります。
まずは実例を見ていきます。
よく使われる.wavファイルのフォーマットを作ってみます。
let fmt = AVAudioFormat(commonFormat:.pcmFormatInt16, sampleRate:44100, channels:2, interleaved:true) print("\(fmt)") // <AVAudioFormat 0x100b06650: 2 ch, 44100 Hz, Int16, inter> let asbd:AudioStreamBasicDescription = fmt.streamDescription.pointee // AudioStreamBasicDescription(mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 12, mBytesPerPacket: 4, mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 2, mBitsPerChannel: 16, mReserved: 0) print("\(asbd)")
1819304813(=0x6c70636d)というのは kAudioFormatLinearPCM = 'lpcm'の10進値で、線形PCM形式であることを示しています。
フォーマットフラグの12は
12=8(=kAudioFormatFlagIsPacked)+4(kAudioFormatFlagIsSignedInteger)
を意味します。
例をもう一つ見て見ましょう。
import AVFoundation let fmt = AVAudioFormat(standardFormatWithSampleRate:44100, channels:2) //<AVAudioFormat 0x100a00bb0: 2 ch, 44100 Hz, Float32, non-inter> let asbd:AudioStreamBasicDescription = fmt.streamDescription.pointee print("\(asbd)") //AudioStreamBasicDescription( mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 41, mBytesPerPacket: 4, //mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 2, mBitsPerChannel: 32, mReserved: 0)
標準フォーマットとはfloat32のnon-interleavedの事だとわかります。
non-interleaveとはステレオの場合に、
LRの音データをLLL..., RRR...
のように別領域に割り当てることを指します。
interleaveの場合LRLR...のように交互に割り当てます。
フォーマットフラグの41は
41=32(kAudioFormatFlagsIsNonInterleaved)+8(kAudioFormatFlagsIsPacked)+1(kAudioFormatFlagIsFloat)
を意味します。
クラス構造を見てきます。
open class AVAudioFormat : NSObject, NSSecureCoding { public init(streamDescription asbd: UnsafePointer<AudioStreamBasicDescription>) public init(streamDescription asbd: UnsafePointer<AudioStreamBasicDescription>, channelLayout layout: AVAudioChannelLayout?) public init(standardFormatWithSampleRate sampleRate: Double, channels: AVAudioChannelCount) public init(standardFormatWithSampleRate sampleRate: Double, channelLayout layout: AVAudioChannelLayout) public init(commonFormat format: AVAudioCommonFormat, sampleRate: Double, channels: AVAudioChannelCount, interleaved: Bool) public init(commonFormat format: AVAudioCommonFormat, sampleRate: Double, interleaved: Bool, channelLayout layout: AVAudioChannelLayout) public init(settings: [String : Any]) public init(cmAudioFormatDescription formatDescription: CMAudioFormatDescription) open func isEqual(_ object: Any) -> Bool open var isStandard: Bool { get } open var commonFormat: AVAudioCommonFormat { get } open var channelCount: AVAudioChannelCount { get } open var sampleRate: Double { get } open var isInterleaved: Bool { get } open var streamDescription: UnsafePointer<AudioStreamBasicDescription> { get } open var channelLayout: AVAudioChannelLayout? { get } open var magicCookie: Data? open var settings: [String : Any] { get } open var formatDescription: CMAudioFormatDescription { get } } // モノラルなら1,ステレオなら2の値です public typealias AVAudioChannelCount = UInt32 // よく使うのはpcmFormatFloat32とpcmFormatInt16でしょう public enum AVAudioCommonFormat : UInt { case otherFormat case pcmFormatFloat32 case pcmFormatFloat64 case pcmFormatInt16 case pcmFormatInt32 }
formatDescriptionプロパティとsettingsプロパティに何が入っているのかも見てみましょう。
let fmt = AVAudioFormat(commonFormat:.pcmFormatInt16, sampleRate:44100, channels:2, interleaved:true) print("\(fmt.formatDescription)") print("\(fmt.settings)") <CMAudioFormatDescription 0x100a075a0 [0x7fffd7221d80]> { mediaType:'soun' mediaSubType:'lpcm' mediaSpecific: { ASBD: { mSampleRate: 44100.000000 mFormatID: 'lpcm' mFormatFlags: 0xc mBytesPerPacket: 4 mFramesPerPacket: 1 mBytesPerFrame: 4 mChannelsPerFrame: 2 mBitsPerChannel: 16 } cookie: {(null)} ACL: {(null)} FormatList Array: {(null)} } extensions: {(null)} } ["AVLinearPCMIsFloatKey": 0, "AVLinearPCMIsNonInterleaved": 0, "AVNumberOfChannelsKey": 2, "AVSampleRateKey": 44100, "AVLinearPCMBitDepthKey": 16, "AVLinearPCMIsBigEndianKey": 0, "AVFormatIDKey": 1819304813]
settingsには文字列キーのDictionaryが入っているようです。
iOS10で公開されたSpeechフレームワークを使って日本語PodCastをテキスト化してみました。 結果から言うと、テキスト化は可能でしたが、品質はかなり悪く実用として厳しかったです。 某IT系PodCastのファイルを食わせて見たんですが、 「ジャバスクリプト」とか「ディープラーニング」とか一般的でない語彙はほぼ認識できませんでした。
SFSpeechiRecognizerというクラスを使います。 音声ファイルを指定すると、iOSデバイスからAppleサーバへリクエストが投げられ、 結果のテキストが得られます。
受け付ける音声ファイルの長さは60sec以内にしないといけないので、 PodCastのmp3などの音声ファイルを20secで区切って順番に処理する戦略をとります。 この方法でAPI制限に引っかかることはありませんでした。
基本のソースコードはAppleのSampleCodeを見るか、WWDC2016のセッションを見るか、 ググるとわかりますので端折ります。
SpeakToMe: Using Speech Recognition with AVAudioEngine
ここでは、mp3ファイルを20secのファイルに区切る処理のソースだけを示しておきます。
import Foundation import AudioToolbox import AVFoundation open class AudioDivider { init(url:URL) { self.url = url } private var url:URL public final class func diskCachePath(cacheName: String) -> String { let dstPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! return (dstPath as NSString).appendingPathComponent(cacheName) } public func execute() -> [URL] { var urls:[URL] = [] var inref: ExtAudioFileRef? var outref: ExtAudioFileRef? var st:OSStatus st = ExtAudioFileOpenURL(url as CFURL, &inref) var index = 0 let pcmfmt:AVAudioFormat = AVAudioFormat.init(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 44100.0, channels: 1, interleaved: false) // read format setting guard let inref_unwrap = inref else { assert(false) } st = ExtAudioFileSetProperty(inref_unwrap, kExtAudioFileProperty_ClientDataFormat, UInt32(MemoryLayout<AudioStreamBasicDescription>.size), pcmfmt.streamDescription) let bufferByteSize:UInt32 = 1024 var srcBuffer = [UInt8](repeating:0, count: 1024) // file loop var eof:Bool = false while true { let cachepath:String = AudioDivider.diskCachePath(cacheName:"") let outpath:String = "\(cachepath)/divider\(index).wav" let outurl:URL = URL(fileURLWithPath: outpath) urls.append(outurl) st = ExtAudioFileCreateWithURL(outurl as CFURL, kAudioFileWAVEType, pcmfmt.streamDescription, nil, AudioFileFlags.eraseFile.rawValue, &outref) guard let outref_unwrap = outref else { assert(false) } var writeFrame:UInt32 = 0 // data loop while true { let ab = AudioBuffer(mNumberChannels: 1, mDataByteSize: bufferByteSize, mData: &srcBuffer) var abl = AudioBufferList(mNumberBuffers: 1, mBuffers: ab) var numFrames: UInt32 = 0 if pcmfmt.streamDescription.pointee.mBytesPerFrame > 0 { numFrames = bufferByteSize / pcmfmt.streamDescription.pointee.mBytesPerFrame } st = ExtAudioFileRead(inref_unwrap, &numFrames, &abl) if numFrames <= 0 { eof = true break } st = ExtAudioFileWrite(outref_unwrap, numFrames, &abl) writeFrame += numFrames // terminate the file in 20sec if writeFrame > 44100 * 20 { break } } ExtAudioFileDispose(outref_unwrap) index += 1 if eof { break } } ExtAudioFileDispose(inref_unwrap) return urls } }
Speech API - Speech Recognition | Google Cloud Platform
は精度がいいらしいですが、無料枠が1ヶ月で1hourしかないため、試す気になりませんでした。 ※1ヶ月で1hourまで無料 以降、$0.006/15sec (=$1.44/1hour)
タイトルそのままです。
struct Hoge { var a: Int32 var b: Int32 } MemoryLayout<Hoge>.size // 8