0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Swift] スペクトログラム③ ArrayからCGImageを作る

Posted at

SwiftとSpriteKitで、スペクトログラムを描写するプログラムを書いています。
手順としては

①オーディオファイル読み込み ここ
②FFTでスペクトラル分析 非常に古いですがここ
③周波数領域データを[UInt8]データに格納
④[UInt8]をCGImageに変換 ←この記事
⑤CGImageをtextureとしてShaderに送る
⑥Shaderでスペクトログラム描写

完成イメージ
(Gif埋め込みが上手く行かなかったためYoutubeです。)

動画のように、軽い動作でスペクトログラムを拡大縮小・スクロールできるようにしつつ、せっかくだからAppleのライブラリを使いたいというのが趣旨です。


var buffer:[UInt8]! // spectrogram data
var texture:CGImage!

texture = createTextureFromArray(src: &buffer,
                                 w: bufferWidth,
                                 h: bufferHeight,
                                 toW: textureWidth,
                                 toH: textureH)

buffer はFFTでスペクトラル分析した周波数領域データになります。
サイズはデフォルトでは([FFTフレーム数]x [FFTSIZE / 2])になりますが、スペクトラムとして表示する周波数幅を小さくする際は([FFTフレーム数]x [FFTSIZE / 4])など適宜調整します。

bufferサイズはスペクトラル分析データに依存しますが、それをそのままShaderメモリ領域に送ると都合が悪いので、適宜サイズ調整しつつ、それに合わせてスケールアップ・ダウン処理を行います。

texture size はSpriteKitのSceneサイズと同じにすることで、スペクトラルを効率よく画面に描写することが可能になります。


func createTextureFromArray(src:inout [UInt8], w:Int, h:Int, toW:Int, toH:Int)->CGImage?{
        
        let src = array2vImageBuffer(src: &src, w: w, h: h)
        
        var dst = try! vImage_Buffer(size: CGSize(width: CGFloat(toW), height: CGFloat(toH)), bitsPerPixel: 8)
        
        scaleBuffer(source: src, destination: &dst)
        
        guard let format = vImage_CGImageFormat(bitsPerComponent: 8,
                                                bitsPerPixel: 8,
                                                colorSpace: CGColorSpaceCreateDeviceGray(),
                                                bitmapInfo: CGBitmapInfo(rawValue: 0)) else{
            return nil
        }
        
        let img:CGImage? = try? dst.createCGImage(format: format)
        dst.free()
        
        return img
    }

[UInt8]型のArrayをAccerelateライブラリを使用してvImage_Buffer型に変換し、スケール処理を実行、その後でCGImageに変換しています。
vImage_Bufferはプログラマが明示的にメモリ解放する必要があるため、dst.free()を読んでいます。

この処理よって、スペクトラルデータが、bufferサイズからテクスチャーサイズに変換されたCGImage型として保存されました。

最後にUniformを介してShaderへTextureデータを送ります。


    private var uniformBuffer:SKUniform!

    self.uniformBuffer = SKUniform(name: "buffer",
                                   texture:SKTexture(cgImage: texture))

    self.shaderSource.addUniform(self.uniformBuffer)


自分向けメモレベルで書いてしまいました。
もう少し時間ができましたら、丁寧にまとめたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?