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

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

ただいまの
回答率

90.76%

  • Java

    13154questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Wavファイルの並列合成でノイズが乗る

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 236

teraterm

score 3

Java8にて、2つのWavファイルを並列に合成したいと考え色々調べたところ、データを足すと合成されるとあったので、単純に足してみたところ、一応ファイルは合成されたのですが、音が重なった部分に「サー」というノイズが乗ってしまい、きれいな合成がされません。

原因は何でしょうか。

Wavファイルは2つとも
frameSize:2byte
Fs:11025.0Hz
16bit
1ch
LittleEndian
SignedPCM
です。

宜しくお願い致します。 

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

public class WavMix {

    public static void main(String[] args) throws Exception {

        byte[] data1 = new WavMix().getByte("AAA.wav");
        byte[] data2 = new WavMix().getByte("BBB.wav");

        System.out.println("data1.length = " + data1.length);
        System.out.println("data2.length = " + data2.length);

        int activeLength = data1.length > data2.length ? data1.length : data2.length;

        // 並列合成
        byte[] data = new byte[activeLength];
        for (int i = 0; i < activeLength; i = i + 2) {
            if (data1.length > i && data2.length > i) {
                short n1 = (short) ((data1[i + 1] << 8) + data1[i]);
                short n2 = (short) ((data2[i + 1] << 8) + data2[i]);

                // 足す
                short n = (short) ((n1 + n2) * 0.5);

                String hex = String.format("%04x", n);
                byte z1 = (byte) Integer.parseInt(hex.substring(0, 2), 16);
                byte z2 = (byte) Integer.parseInt(hex.substring(2, 4), 16);
                data[i] = z2;
                data[i + 1] = z1;
            } else if (data1.length > i) {
                data[i] = data1[i];
                data[i + 1] = data1[i + 1];
            } else {
                data[i] = data2[i];
                data[i + 1] = data2[i + 1];
            }
        }

        // 書き出し
        AudioFormat af = new AudioFormat(22050, 16, 1, true, false);
        InputStream in = new ByteArrayInputStream(data);
        AudioInputStream newAis = new AudioInputStream(in, af, data.length);
        File soundFile = new File("newFile.wav");
        AudioSystem.write(newAis, AudioFileFormat.Type.WAVE, soundFile);

    }

    // データ取得
    byte[] getByte(String file) throws Exception {
        AudioInputStream ais = AudioSystem.getAudioInputStream(new File(file));
        byte[] data = new byte[ais.available()];
        ais.read(data);
        ais.close();
        format(ais);
        return data;

    }

    // ファイルフォーマット確認
    void format(AudioInputStream ais) {
        AudioFormat format = ais.getFormat();
        StringBuilder sb = new StringBuilder();
        sb.append("[AudioFormat] frameSize:" + format.getFrameSize() + "byte");
        sb.append(" Fs:" + format.getSampleRate() + "Hz");
        sb.append(" " + format.getSampleSizeInBits() + "bit");
        sb.append(" " + format.getChannels() + "ch");
        if (format.isBigEndian()) {
            sb.append(" BigEndian");
        } else {
            sb.append(" LittleEndian");
        }
        if (format.getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
            sb.append(" SignedPCM");
        } else if (format.getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED) {
            sb.append(" UnsignedPCM");
        } else {
            sb.append(" NoPCM");
        }
        System.out.println(sb.toString());
    }

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

javaのbyteはsignedです。演算(本件でいえば加算)の際にintに昇格されます。例えば16bit符号付のあるサンプル値が-2(LittleEndian)の場合低位バイトと高位バイトは次のようになります

16進数でのバイト列:0xFE(低位) 0xFF(高位)
それをjavaのbyteとして取り出すと...
低位 : -2
高位 : -1

つまりintに昇格される際に負号拡張されるのです

(byte)0xFF -> (int)0xFFFFFFFF

それゆえ以下の計算式では演算結果が意図したものとなりません。

short n1 = (short) ((data1[i + 1] << 8) + data1[i]);
=> short n1 = (short) (( ((byte)0xFF)      << 8) + ((byte)0xFF));
=> short n1 = (short) (( ((int)0xFFFFFFFE) << 8) + ((int)0xFFFFFFFF));
=> -258 (期待は-2)

こうした場合、低位のバイトは加算の前に0xFFとの論理積をとり負号拡張されないようにする必要があります。(いうまでもなくn1, n2の両方について)

誤:short n1 = (short) ((data1[i + 1] << 8) + data1[i]);
正:short n1 = (short) ((data1[i + 1] << 8) + (data1[i] & 0xFF));


補記: 問題の原因とは関係なく結果は間違ってませんが効率上の指摘を1点。

合成後のサンプルデータ(short型)をbyteへ分解する演算ですが、ご質問のコードでは16進数4桁のStringへ変換してその前後の2文字を16進数整数としてパースする方法を採っておられます。しかしその方法よりも整数演算を用いた方が効率的です。Stringのインスタンスを生成しないことでヒープの消費もありませんし、処理自体のスピードもずっと速いです。

short n = ...;
data[i] = (byte)n;
data[i] = (byte)(n >> 8);

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/03 12:06

    的確なご回答ありがとうございます。
    試してみたところ、きれいに合成できました。

    キャンセル

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

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

関連した質問

  • 解決済

    国旗を表示させたい(続き)

    前提・実現したいこと btn [0][1][2]をクリックすると、それに対応した国旗が表示されるようにしたい。 ソースコード import java.awt.*;   

  • 解決済

    Unicodeに変換できない

    Unicodeにうまく変換できません。文字コードの指定の仕方が悪いのかもしれませんが… private EditText unicodeText; p

  • 受付中

    プログラムソースを教えて欲しいです

    前提・実現したいこと しりとりプログラムを作ろうとしています。 CSVデータ中の情報を読み込みデータ内の単語のみでしりとりを行います。 ルールとして ・CPUとの対戦型

  • 解決済

    会員情報システム(Java)での作り方

    javaで会員情報システムのようなものを作りたいです。 下記の実行結果(コマンドプロンプトで実行)になるような、登録プログラムを作成したいのですが、作成方法が分かりませんので、教

  • 解決済

    タイムゾーンの時差計算

    前提・実現したいこと java7でプログラミングを行っています。 たとえばtimezone(Asia/Tokyo)と日付(new Date()で生成された時間)をもちいて 「

  • 解決済

    Javaのテキストを日付に変換したい

    Javaで西暦、年号がバラバラのデータ(格納されている日付データの型はString型)の フォーマットを整形したい場合の書き方を教えて頂きたいです。 例)2015年7月26日メ

  • 受付中

    java apllet 音源再生

    番号リスト 音楽をappletで再生したい. ボタンstart stopをつけ動作させたい。 実行したこと 形式wmv サイズ55.0 MB (57,710,783 バイト) 音

  • 解決済

    入力した日付から過去か未来かを表示する

    あと一歩だと思うのですが、思いつきません。助けてください。 Javaで、コマンドライン引数から適当な年月日を入力し、今日の日付と比べて過去か未来か今日か、を判断するプログラムを

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

  • Java

    13154questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。