質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • iOS

    4078questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Swift 2

    1334questions

    Swift 2は、Apple社が独自に開発を行っている言語「Swift」のアップグレード版です。iOSやOS X、さらにLinuxにも対応可能です。また、throws-catchベースのエラーハンドリングが追加されています。

CMSampleBufferクラスで44100hzの音声データを取り込んでも5hzほどでしかとれない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,138

tetsutail

score 68

前提・実現したいこと

CMSampleBufferクラスで44100hzの音声データを取り込んでも5hzほどでしかとれない

発生している問題・エラーメッセージ

swiftで音楽データを取り込んでみようと思い、CMSampleBufferクラスを使ってデータサンプルをとってみたのですが、本来の音声データ(mp3)のサンプリング周波数が44100hzに対して、取り込んだ音声データは5hzほどしかないという状況です。海外のサイトも閲覧しましたが、わかりませんでした。詳しい方、ご教授お願いします。

エラーメッセージ

該当のソースコード

swift

//mp3データを取得
            let sineURL = NSBundle.mainBundle().URLForResource("SUPER", withExtension: "mp3")!
            let asset = AVAsset(URL: sineURL)
            var assetReader:AVAssetReader

            do{
                assetReader = try AVAssetReader(asset: asset)
            }catch{
                fatalError("Unable to read Asset: \(error) : \(#function).")
            }

            let track = asset.tracksWithMediaType(AVMediaTypeAudio).first
            let outputSettings: [String:Int] =
                [ AVFormatIDKey: Int(kAudioFormatLinearPCM),
                  AVLinearPCMIsBigEndianKey: 0,
                  AVLinearPCMIsFloatKey: 0,
                  AVLinearPCMBitDepthKey: 16,
                  AVLinearPCMIsNonInterleaved: 0]

            let trackOutput = AVAssetReaderTrackOutput(track: track!, outputSettings: outputSettings)

            assetReader.addOutput(trackOutput)

            assetReader.startReading()

            let sampleData = NSMutableData()

            //サンプリングしたデータを格納する配列
            var arraySamples = [Int16]()
            //読み込むデータが無くなるまで、つまり終端のデータまで読み込みつづける。
            while assetReader.status == AVAssetReaderStatus.Reading {
//copyNextSampleBuffer:次のサンプルに進む?
                if let sampleBufferRef = trackOutput.copyNextSampleBuffer() {
//samplebufferRefのprint結果については後述。
                    print(sampleBufferRef)

                    if let blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef) {

                        let bufferLength = CMBlockBufferGetDataLength(blockBufferRef)

                        let data = NSMutableData(length: bufferLength)

                        CMBlockBufferCopyDataBytes(blockBufferRef, 0, bufferLength, data!.mutableBytes)
                        let samples = UnsafeMutablePointer<Int16>(data!.mutableBytes)
                        sampleData.appendBytes(samples, length: bufferLength)
                        CMSampleBufferInvalidate(sampleBufferRef)

                        if(samples.memory < -32766 || samples.memory > 32766){
                            arraySamples.append(32767)
                        }
                        else{
                            arraySamples.append(samples.memory)
                        }
                    }
                }
            }

試したこと

arraySamples.append(samples.memory)の回数を計算すると、240秒のデータ量でだいたい1200個のsamplesが得られました。これではたったの5hzしかサンプリングできていないのです。

補足情報(言語/FW/ツール等のバージョンなど)

print(sampleBufferRef)の結果です。

CMSampleBuffer 0x7fcbe3e215f0 retainCount: 6 allocator: 0x10dbeca40
    invalid = NO
    dataReady = YES
    makeDataReadyCallback = 0x0
    makeDataReadyRefcon = 0x0
    formatDescription = <CMAudioFormatDescription 0x7fcbe3e4b290 [0x10dbeca40]> {
    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)} 
    } 
    extensions: {(null)}
}
    sbufToTrackReadiness = 0x0
    numSamples = 8192
    sampleTimingArray[1] = {
        {PTS = {0/44100 = 0.000}, DTS = {INVALID}, duration = {1/44100 = 0.000}},
    }
    sampleSizeArray[1] = {
        sampleSize = 4,
    }
    dataBuffer = 0x7fcbe3e55020

この下から8行目のnumSamples = 8912の分がまとめて一つのデータとして入っているのではないかと考えられるのですが、この値を変える方法がわかりません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

sampleDataには正しい読み出しデータが入っているように見えます。

Simulatorで試したところ、32768バイトづつ読み込まれるようなので、
240sec * 44100 * sizeof(Int16) * 2(チャンネル) = 42336000 bytes
42336000 / 32768 = 1292 回のループ
1200と言っているのはループ回数でしょう。
samples.memoryは最初のInt16を表しますので、arraySamplesに意味があるデータが入るとは思えません。

回答のピントがずれていたら、質問を変えてみてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/05 00:04 編集

    回答ありがとうございます。
    ループ回数についても理解できたのでとても助かりました。
    また、samples.memoryやarraySampleに関してですが、何回か実行しても、arraySamplesに同じデータが入ったため、意味のあるデータなのかと思っていました。ループごとにsamplesが更新されているはずなのですが、やはりsamples.memoryはかわらないのでしょうか?
    そして、質問の意図がわかりづらく申し訳ありません。
    sampleDataから44100hzのサンプリング周波数でサンプリングした音声データを配列に入れたいのですが、どうすればよいでしょうか?(とりあえず、メモリの問題は考えないとします)

    キャンセル

  • 2016/05/05 01:06 編集

    sampleDataにはすでに44100Hzの周波数でサンプリングした音声データが入っています。NSMutableDataなのでバイトデータ列ですが。Int16の配列にしたいということでしょうか?
    samples = UnsafeMutablePointer<Int16>
    はいわゆるC言語でいうポインタですからInt16の型が32768/2個並んだメモリの先頭アドレスを指しており、samples.memoryでは32768/2個分の最初の1個のInt16での値を表しています。残りの(32768/2)-1個のデータを見るには、samples.advancedBy(1).memory、samples.advancedBy(2).memoryのように見ていき全て(32768/2)個分appendしていけば期待するものは得られると思います。
    あと、データはmp3ファイルから取り出したものなので、-32766,32766でclipする意味はありません。

    キャンセル

  • 2016/05/05 02:43 編集

    お早い回答本当にありがとうございます。
    既存のコードから見よう見まねで作ったプログラムだったため、NSMutableData型をよくわかっておらず、またなぜUnsafeMutablePointer、つまりポインタを使うべきなのかもよくわかっていませんでしたが、ポインタの先頭から準に複数データがまとまって入ると考えれば納得ですね。確かにそう考えれば、おっしゃる通りの方法でポインタの中の値を利用できそうです。
    が、これではsampleDataが絡んでおらず、sampleDataの意味がないような気がしてきました。という認識であっているのでしょうか?そうすると、sampleDataは必要ないのでしょうか?
    お気づきでしょうが、なぜかsampleDataにappendByteしたデータを使っていませんでした。そこで、このバイトデータ列を利用する場合はどのように扱えば良いのでしょうか?もし、質問者様の言われるようにInt16に変換できるのであれば、その方法もご教授願いたいです。ちなみに、print文を使ってsampleDataの中身を見たところ、バイトデータ列は具体的には16進数8桁の列(例:e4e4e0e2 )となっていました。
    また、最後の[ -32766, 32766 ]でclipするのは意味がないというのはどういう意味でしょうか?mp3ではなく、waveファイルなどであれば変わるのでしょうか?
    質問が多く申し訳ないですが、お時間があれば回答よろしくお願いします。

    キャンセル

  • 2016/05/05 10:04

    >質問者様の言われるようにInt16に変換できるのであれば、その方法もご教授願いたいです。
    その方法を回答したつもりです。
    繰り返しになりますが、UnsafeMutablePointerのadvancedByを使いましょう。

    >最後の[ -32766, 32766 ]でclipするのは意味がないというのはどういう意味でしょうか?
    waveでも同じです。
    元々音データですから、音として有効なデータであるはずだからです。
    それにInt16型ですから-32768〜32767の範囲の値しかとることはできません。

    キャンセル

  • 2016/05/05 11:50

    わかりました。一度得たデータを音声ファイルとして書き出しして確かめてみたいと思います。何度もご返答いただきありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • iOS

    4078questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Swift 2

    1334questions

    Swift 2は、Apple社が独自に開発を行っている言語「Swift」のアップグレード版です。iOSやOS X、さらにLinuxにも対応可能です。また、throws-catchベースのエラーハンドリングが追加されています。