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

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

ただいまの
回答率

88.79%

Python2.7 バイナリデータ?を整数型に変えたい

解決済

回答 1

投稿 編集

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

slimat

score 53

こんにちは。

MacOS Mojave・Python2.7環境でマイクから音を拾って、拾った音をwavファイルに書き込むというプログラムを書こうとしてる途中です。下のコードを実行すると画像のように、dataにはBytes型?のオブジェクトが入っているようなのですけど、その次の行で以下のようなエラーが発生しました。これを適切に整数型に変換してfor文などで要素を一つづつ取り出すなどの処理をしたいのですが、どのように書き直したら上手くいくでしょうか。なお、下記のようにしてpyaudioでは16bitで収録するようにしています。

stream = audio.open(format=pyaudio.paInt16,  # 量子化ビット数16bit
                        channels=int(channel),   # チャンネル数
                        rate=int(fs),            # サンプリング周波数
                        input=True,              # 録音のときは input=True
                        frames_per_buffer=size,   # バッファのサイズを設定
                        stream_callback = adCallback)  # コールバック関数の設定

print data
intData = struct.unpack("h", data)


イメージ説明

Traceback (most recent call last):
  File "5-4.py", line 103, in <module>
    wavOut(filename)
  File "5-4.py", line 23, in wavOut
    intData = struct.unpack("h", data)
struct.error: unpack requires a string argument of length 2


下はコード全体です。(古いコード)

#coding: utf-8
import pyaudio as pyaudio
import wave
import time
import numpy as np
import matplotlib.pyplot as plt
import struct

fs = 16000   # サンプリング周波数 [Hz]
channel = 1  # チャンネル数

####### wavファイル保存関数
def wavOut(filename):
    # 書き出し用オブジェクトの生成(ファイル名の決定)
    out = wave.Wave_write(filename)

    out.setnchannels(channel)  # チャンネル数を設定
    out.setsampwidth(2)        # 量子化ビット数16bits(2byte)を設定
    out.setframerate(fs)       # サンプリング周波数を設定

    print data
    intData = struct.unpack("h", data)
    for i in intData:
        if i > 10000:
            print i
        if i < 10000:
            print u"10000未満:" + i

    # dataを結合してwrite
    out.writeframes(b''.join(data)) # dataはbytes型
    out.close()

####### プロット関数
def plotOut(plot_data):

    plt.clf()
    plt.ylim(-1*2**15, 2**15) # 上下15bit分を表示
    plt.plot(plot_data)
    plt.pause(0.01) # plotして0.01秒pause

####### 録音用コーバック関数(別スレッドで動作)
def adCallback(in_data, frame_count, time_info, status):
    # dataに録音バッファ(in_data)を追加
    data.append(in_data) # in_dataは何型?

    # 代入する前にグローバル変数であることを宣言
    global buf
    # 取得した録音バッファ(in_data)をint16に変換
    buf = np.frombuffer(in_data, dtype="int16")

    return (None, pyaudio.paContinue)


####### ここからメイン
if __name__ == '__main__':
    # バッファのサイズ
    size = 2**10
    audio = pyaudio.PyAudio()
    # オーディオストリームstreamをopen
    stream = audio.open(format=pyaudio.paInt16,  # 量子化ビット数16bit
                        channels=int(channel),   # チャンネル数
                        rate=int(fs),            # サンプリング周波数
                        input=True,              # 録音のときは input=True
                        frames_per_buffer=size,   # バッファのサイズを設定
                        stream_callback = adCallback)  # コールバック関数の設定

    # 初期化
    data = []
    buf = []
    start_time = ''
    flag = True

    # 録音の開始
    stream.start_stream() # 別スレッドで録音を開始
    print ("Recording.")

    # メインスレッド処理
    while flag == True:
        # プロット
        plotOut(buf) # プロット

        # buf中振幅の絶対値が 10000 を超えたら
        if np.max(np.absolute(buf)) > 10000:
            start_time = time.time() # 音圧が10000を超えた時刻を取得
            print ("Time reset.") # 10000を超えたことをメッセージング
            flag = False

    while flag == False: # 音圧が10000を超えたら
        plotOut(buf)
        if time.time() - start_time > 2: # 録音を停止する
            print ("Finished.")
            stream.close()
            audio.terminate()
            flag = True

        else: # 停止までの残りの秒数をメッセージング
            print ("Remaining time [s]: " + str(5 - int(time.time() - start_time)))

    # ファイルに保存
    filename = "record.wav" # 保存ファイル名
    print ("Write: " + filename)
    wavOut(filename)


修正したコード(新しいコード)

#coding: utf-8
import pyaudio as pyaudio
import wave
import time
import numpy as np
import matplotlib.pyplot as plt
import struct

fs = 16000   # サンプリング周波数 [Hz]
channel = 1  # チャンネル数

####### wavファイル保存関数
def wavOut(filename):
    # 書き出し用オブジェクトの生成(ファイル名の決定)
    out = wave.Wave_write(filename)

    out.setnchannels(channel)  # チャンネル数を設定
    out.setsampwidth(2)        # 量子化ビット数16bits(2byte)を設定
    out.setframerate(fs)       # サンプリング周波数を設定

    global packed
    packed = b''.join(data)
    print u"type(packed): " + str(type(packed)) # str
    print packed
    print type()
    # intData = struct.unpack("h", packed)



    for i in intData:
        if i > 10000:
            print i
        if i < 10000:
            print u"10000未満:" + i

    # dataを結合してwrite
    out.writeframes(packed) # dataはbytes型
    out.close()

####### プロット関数
def plotOut(plot_data):

    plt.clf()
    plt.ylim(-1*2**15, 2**15) # 上下15bit分を表示
    plt.plot(plot_data)
    plt.pause(0.01) # plotして0.01秒pause

####### 録音用コーバック関数(別スレッドで動作)
def adCallback(in_data, frame_count, time_info, status):

    # print u"type(in_data): " + str(type(in_data)) # str
    data.append(in_data) # 文字列のリスト型オブジェクト
    print data # \xb6\xff\xba\xff\xb6\xff\xb7の形の出力が得られる

    # 代入する前にグローバル変数であることを宣言
    global buf

    # 取得した録音バッファ(in_data)をint16に変換
    # bufはリスト型なので、リストのインデックス0番にndarrayが入るということ?
    # 4文字の一つの数字に変換していく
    buf = np.frombuffer(in_data, dtype="int16") # bufはndarrayで、extendsは使えない。
    # print u"type(buf): " + str(type(buf)) # 再代入してるからndarrayに変化

    return (None, pyaudio.paContinue)

####### ここからメイン
if __name__ == '__main__':
    # バッファのサイズ
    size = 2**10
    audio = pyaudio.PyAudio()
    # オーディオストリームstreamをopen
    stream = audio.open(format=pyaudio.paInt16,  # 量子化ビット数16bit
                        channels=int(channel),   # チャンネル数
                        rate=int(fs),            # サンプリング周波数
                        input=True,              # 録音のときは input=True
                        frames_per_buffer=size,   # バッファのサイズを設定
                        stream_callback = adCallback)  # コールバック関数の設定

    # 初期化
    data = []
    packed = 1
    buf = False
    print u"初期化時はtype(buf): " + str(type(buf)) # list
    start_time = ''
    flag = True
    s = 2

    # 録音の開始
    stream.start_stream() # 別スレッドで録音を開始
    print "Recording."

    # メインスレッド処理
    while flag == True:
        plotOut(buf) # プロット

        # buf中振幅の絶対値が 10000 を超えたら
        if np.max(np.absolute(buf)) > 10000:
            start_time = time.time() # その時刻を取得
            flag = False # このwhileループを抜け出し

    while flag == False: # そして、このループに入る
        plotOut(buf) # プロット
        if time.time() - start_time > s: # 検知してからs秒以上経っていたら
            print "Finished."
            stream.close()
            audio.terminate()
            flag = True # 録音終了

        else: # 停止までの残りの秒数をメッセージング
            print "Remaining time [s]: " + str(5 - int(time.time() - start_time))

    # ファイルに保存
    filename = "record.wav" # 保存ファイル名
    print "Write down wav format: " + filename
    print u"type(data): " + str(type(data)) # list
    wavOut(filename)


文字化けした出力の画像
イメージ説明

よろしくお願い致します。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • tiitoi

    2019/04/03 17:22

    バイナリデータをパースするにはバイナリデータがどのようなフォーマットかわからないとできないと思いますよ。

    キャンセル

  • slimat

    2019/04/03 17:27

    今、質問文を修正したのですが、この情報だと不足でしょうか。

    キャンセル

  • tiitoi

    2019/04/03 17:37 編集

    data という変数はどのような変数なのでしょうか?

    キャンセル

  • slimat

    2019/04/03 17:44

    コード全体を追記いたしました。

    キャンセル

回答 1

checkベストアンサー

+1

提示コードの動作が確認できないためdataの内容がいまひとつ分かりませんが、struct.unpack("h", data)を利用しているということは、バイト列dataの先頭から2バイトずつ符号有のshortに変換したいということでしょうか。
であれば以下のようなループでできます。
なお、当然ながらdataの長さは2の倍数である必要があります。
参考:7.3. struct --- 文字列データをパックされたバイナリデータとして解釈する

import struct
data = 'd\xff\xe5\xff'
for i in range(0,len(data),2):
    n = struct.unpack("h", data[i:i+2])
    print n[0]
"""
-156
-27
"""

追記:data変数値の確認表示

import struct
data = 'd\xff\xe5\xff'

print len(data) # (13文字ではなく)4 byte
print type(data)# <type 'str'>

# バイト列をそのまま文字列として出力。
# ASCIIコードで文字として表現できない部分は文字化けする
print data       # d・ 

# バイト列をreprした結果を出力。
# ASCIIコードで文字として表現できない部分は16進(\x??)表示される
# (dataの中身は同じバイト列)
print repr(data) # 'd\xff\xe5\xff'

# バイト列をlist化した結果を出力。
# repr と同じように16進表示される
print list(data) # ['d', '\xff', '\xe5', '\xff']

# リスト型を出力。
# 各要素はrepr と同じように16進表示される
data = ['d\xff\xe5\xff']
print data # ['d\xff\xe5\xff']

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/09 17:42

    >先頭文字「d」は単なる1文字=1バイトデータを表します。よって合計4バイトになります。
    そういうことなのですか。ありがとうございます。

    struct.unpack("h", packed)を実行して、packed変数に代入されているバイト列をunpackしたいのですが、そのコード前でprint repr(packed)したところ、下の出力が得られました(一部)。これだと、フォーマットはhではだめなのでしょうか。print str(len(packed))を実行すると、106496と出力されました。

    \x00\x17\x00 \x00!\x00%\x00!\x00\x18\x00\x15\x00\x14\x00\x1d\x00"\x00\'\x00\'\x00(\x00+\x00/\x001\x00-\x00/\x001\x003\x00/\x00.\x007\x004\x00<\x00@\x00=\x00B\x00B\

    キャンセル

  • 2019/04/09 17:45

    すみません。一気にunpackしようとしてました。ご回答の通り、forで2バイトずつずらして、やってみます。

    キャンセル

  • 2019/04/09 18:21

    今、unpackすることに成功しました。
    フォーマットはhでいけました。

    本当にありがとうございました。

    キャンセル

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

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

関連した質問

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