/*
 * Wave.cs
 * Copyright (c) 2008-2009 kbinani
 *
 * This file is part of Boare.Lib.Media.
 *
 * Boare.Lib.Media is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * Boare.Lib.Media is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
#if JAVA
package org.kbinani.media;

import java.io.*;
import org.kbinani.*;
#else
using System;
using System.Collections.Generic;
using bocoree;
using bocoree.java.io;

namespace Boare.Lib.Media {
    using boolean = System.Boolean;
#endif

#if !JAVA
    /// <summary>
    /// Parameters of first formanto detection algorithm
    /// </summary>
    public class FormantoDetectionArguments {
        private double m_peak_detection_threashold = 0.2;
        private double m_moving_average_width = 25.0;

        /// <summary>
        /// ピークを検出する閾値。フーリエ変換後の周波数の強度分布関数の値がこの値よりも大きい箇所で
        /// 密度関数の導関数の符号が正から負へ変わる位置をピークとみなす。周波数密度関数は、最大値で
        /// 規格化したのを使う。従って、0～1の値を指定する。
        /// </summary>
        public double PeakDetectionThreshold {
            get {
                return m_peak_detection_threashold;
            }
            set {
                m_peak_detection_threashold = value;
            }
        }

        /// <summary>
        /// フーリエ変換した生の周波数の強度分布を平滑化する幅を2で割った値。Hzで指定。
        /// </summary>
        public double MovingAverageWidth {
            get {
                return m_moving_average_width;
            }
            set {
                m_moving_average_width = value;
            }
        }
    }
#endif

#if JAVA
    public class Wave
#else
    public class Wave : IDisposable
#endif
 {
        public enum Channel {
            Right,
            Left,
        }

        public enum WaveChannel {
            Monoral,
            Stereo
        }

        private WaveChannel m_channel;
        private int m_bit_per_sample;
        private long m_sample_rate;
        private long m_total_samples;
        private byte[] L8;
        private byte[] R8;
        private short[] L16;
        private short[] R16;

#if !JAVA
#if DEBUG
        private static boolean s_test = false;
        public static boolean TestEnabled {
            get {
                return s_test;
            }
            set {
                s_test = value;
            }
        }
#endif
#endif

#if !JAVA
        public unsafe double TEST_GetF0( uint time, double[] window_function ) {
            int window_size = window_function.Length;
            double[] formanto = GetFormanto( time, window_function );
#if DEBUG
            if ( s_test ) {
                BufferedWriter sw = null;
                try {
                    sw = new BufferedWriter( new FileWriter( @"C:\TEST_GetF0_formanto.txt" ) );
                    for ( int i = 0; i < formanto.Length; i++ ) {
                        sw.write( Math.Abs( formanto[i] ) + "" );
                        sw.newLine();
                    }
                } catch ( Exception ex ) {
                } finally {
                    if ( sw != null ) {
                        try {
                            sw.close();
                        } catch ( Exception ex2 ) {
                        }
                    }
                }
            }
#endif
            int size = formanto.Length;
            double[] wv = new double[size + 1];
            for ( int i = 0; i < size; i++ ) {
                wv[i] = formanto[i];
            }
            int nmaxsqrt = (int)Math.Sqrt( size );
            int[] ip_ = new int[nmaxsqrt + 2];
            double[] w_ = new double[size * 5 / 4];
            ip_[0] = 0;

            fixed ( int* ip = &ip_[0] )
            fixed ( double* w = &w_[0] )
            fixed ( double* a = &wv[0] ) {
                fft.rdft( size, 1, a, ip, w );
            }
#if DEBUG
            if ( s_test ) {
                BufferedWriter sw = null;
                try {
                    sw = new BufferedWriter( new FileWriter( @"C:\TEST_GetF0_fft_formanto.txt" ) );
                    for ( int i = 0; i < wv.Length; i++ ) {
                        sw.write( Math.Abs( wv[i] ) + "" );
                        sw.newLine();
                    }
                } catch ( Exception ex ) {
                } finally {
                    if ( sw != null ) {
                        try {
                            sw.close();
                        } catch {
                        }
                    }
                }
            }
#endif
            return 0;
        }
#endif

#if !JAVA
        public double GetF0( uint time, double[] window_function ) {
            return GetF0( time, window_function, new FormantoDetectionArguments() );
        }
#endif

#if !JAVA
        /// <summary>
        /// 第timeサンプルにおけるフォルマントを取得する
        /// </summary>
        /// <param name="time"></param>
        /// <param name="window_function"></param>
        /// <returns></returns>
        public double GetF0( uint time, double[] window_function, FormantoDetectionArguments argument ) {
            int window_size = window_function.Length;
            double[] formanto = GetFormanto( time, window_function );
            int length = formanto.Length;
            double max = 0.0;
            for ( int i = 0; i < length; i++ ) {
                formanto[i] = Math.Abs( formanto[i] );
                max = Math.Max( max, formanto[i] );
            }
            double inv_max = 1.0 / max;
            for ( int i = 0; i < length; i++ ) {
                formanto[i] = formanto[i] * inv_max;
            }

            double hz_from_index = 1.0 / (double)window_size * m_sample_rate * 0.5;
#if DEBUG
            Console.WriteLine( "Wave+GetF0" );
            if ( s_test ) {
                BufferedWriter sw = null;
                try {
                    sw = new BufferedWriter( new FileWriter( "formanto.txt" ) );
                    for ( int i = 0; i < length; i++ ) {
                        sw.write( (i * hz_from_index) + "\t" + formanto[i] );
                        sw.newLine();
                    }
                } catch ( Exception ex ) {
                } finally {
                    if ( sw != null ) {
                        try {
                            sw.close();
                        } catch ( Exception ex2 ) {
                        }
                    }
                }
            }
#endif
            // 移動平均を計算
            double ma_width = argument.MovingAverageWidth; // 移動平均を取るHzを2で割ったもの
            int ma_width_sample = (int)(window_size * 2 * ma_width / (double)m_sample_rate);
            if ( ma_width_sample < 1 ) {
                ma_width_sample = 1;
            }
#if DEBUG
            Console.WriteLine( "ma_width_sample=" + ma_width_sample );
#endif
            double[] ma = new double[length];
            for ( int i = 0; i < length; i++ ) {
                int count = 0;
                double sum = 0.0;
                for ( int j = i - ma_width_sample; j < i + 2 * ma_width_sample; j++ ) {
                    if ( 0 <= j && j < length ) {
                        sum += formanto[j];
                        count++;
                    }
                }
                ma[i] = sum / (double)count;
            }
#if DEBUG
            if ( s_test ) {
                BufferedWriter sw = null;
                try {
                    sw = new BufferedWriter( new FileWriter( "ma.txt" ) );
                    for ( int i = 0; i < length; i++ ) {
                        sw.write( (i * hz_from_index) + "\t" + ma[i] );
                        sw.newLine();
                    }
                } catch ( Exception ex ) {
                } finally {
                    if ( sw != null ) {
                        try {
                            sw.close();
                        } catch ( Exception ex2 ) {
                        }
                    }
                }
            }
#endif
            // ピークの位置を探す
            double threshold = argument.PeakDetectionThreshold;
            double last_slope = 0.0;
            int index = -1;
            List<double> peak_positions = new List<double>();
            boolean first = true;
            for ( int i = 1; i < window_size - 1; i++ ) {
                double slope = ma[i + 1] - ma[i - 1];
                if ( ma[i] > threshold && slope <= 0.0 && 0.0 < last_slope ) {
                    if ( first ) {
                        index = i;
                        first = false;
                    }
                    //break;
                    peak_positions.Add( i * hz_from_index );
                }
                last_slope = slope;
            }
#if DEBUG
            if ( s_test ) {
                BufferedWriter sw = null;
                try {
                    sw = new BufferedWriter( new FileWriter( "peak_positions.txt" ) );
                    for ( int i = 0; i < peak_positions.Count; i++ ) {
                        sw.write( peak_positions[i].ToString() );
                        sw.newLine();
                    }
                } catch ( Exception ex ) {
                } finally {
                    if ( sw != null ) {
                        try {
                            sw.close();
                        } catch ( Exception ex2 ) {
                        }
                    }
                }
            }
#endif
            if ( index > 0 ) {
                List<double> peaks = new List<double>();
                double peak_distance_tolerance = index * hz_from_index / 5.0; //最小の周波数の5分の1
                double last_peak_pos = index * hz_from_index;
                peaks.Add( last_peak_pos );
                for ( int i = 1; i < peak_positions.Count; i++ ) {
                    if ( peak_positions[i] - last_peak_pos > peak_distance_tolerance ) {
                        peaks.Add( peak_positions[i] );
                        last_peak_pos = peak_positions[i];
                    }
                }
#if DEBUG
                if ( s_test ) {
                    BufferedWriter sw = null;
                    try {
                        sw = new BufferedWriter( new FileWriter( "peaks.txt" ) );
                        for ( int i = 0; i < peaks.Count; i++ ) {
                            sw.write( peaks[i] + "" );
                            sw.newLine();
                        }
                    } catch ( Exception ex ) {
                    } finally {
                        if ( sw != null ) {
                            try {
                                sw.close();
                            } catch ( Exception ex2 ) {
                            }
                        }
                    }
                }
#endif
                double min_peak_distance = index * hz_from_index * 2.0 / 3.0;
                /*for ( int i = 1; i < peaks.Count; i++ ) {
                    min_peak_distance = Math.Min( min_peak_distance, peaks[i] - peaks[i - 1] );
                }*/
#if DEBUG
                Console.WriteLine( "min_peak_distance=" + min_peak_distance );
                if ( s_test ) {
                    BufferedWriter sw = null;
                    try {
                        sw = new BufferedWriter( new FileWriter( "evaluation.txt" ) );
                        for ( int i = (int)min_peak_distance; i < (int)(4 * min_peak_distance); i++ ) {
                            sw.write( i + "\t" + GetFormantoGetEvaluationValue( peaks, i ) );
                            sw.newLine();
                        }
                    } catch ( Exception ex ) {
                    } finally {
                        if ( sw != null ) {
                            try {
                                sw.close();
                            } catch ( Exception ex2 ) {
                            }
                        }
                    }
                }
#endif
                int smallest = (int)min_peak_distance;
                double min_eval = GetFormantoGetEvaluationValue( peaks, smallest );
                for ( int i = (int)min_peak_distance; i < (int)(4 * min_peak_distance); i++ ) {
                    double eval = GetFormantoGetEvaluationValue( peaks, i );
                    if ( min_eval > eval ) {
                        min_eval = eval;
                        smallest = i;
                    }
                }
                return smallest;
            } else {
                return 0;
            }
        }
#endif

#if !JAVA
        private static double GetFormantoGetEvaluationValue( List<double> peaks, double t ) {
            double ret = 0.0;
            for ( int i = 0; i < peaks.Count; i++ ) {
                int n_i = (int)(peaks[i] / t + 0.5);
                double dt_i = peaks[i] - n_i * t;
                ret += Math.Abs( dt_i );
            }
            return ret / t;
        }
#endif

#if !JAVA
        /// <summary>
        /// 第timeサンプルにおけるフォルマントを取得する
        /// </summary>
        /// <param name="time"></param>
        /// <param name="window_function"></param>
        /// <returns></returns>
        public unsafe double[] GetFormanto( uint time, double[] window_function ) {
            int size = window_function.Length;
            double[] wv = getNormalizedWave( (int)(time - size / 2), (uint)(size + 1) );
            // 窓関数をかける
            for ( int i = 0; i < size; i++ ) {
                wv[i] = wv[i] * window_function[i];
            }
            int nmaxsqrt = (int)Math.Sqrt( size );
            int[] ip_ = new int[nmaxsqrt + 2];
            double[] w_ = new double[size * 5 / 4];
            ip_[0] = 0;

            fixed ( int* ip = &ip_[0] )
            fixed ( double* w = &w_[0] )
            fixed ( double* a = &wv[0] ) {
                fft.rdft( size, 1, a, ip, w );
            }
            return wv;
        }
#endif

#if !JAVA
        public double GetVolume( int start_sample, double[] window_function ) {
            int window_size = window_function.Length;
            double[] conv = new double[window_size];
            getNormalizedWave( start_sample - window_size / 2, conv );
            double ret = 0.0;
            for ( int i = 0; i < window_size; i++ ) {
                ret += Math.Abs( conv[i] ) * window_function[i];
            }
            return ret / (double)window_size;
        }
#endif

#if !JAVA
        /// <summary>
        /// 指定したサンプル位置における音量を計算します
        /// </summary>
        /// <param name="start_sample"></param>
        /// <param name="window_size"></param>
        /// <param name="window_function_type"></param>
        /// <returns></returns>
        public double GetVolume( int start_sample, int window_size, math.WindowFunctionType window_function_type ) {
            double[] conv = new double[window_size];
            getNormalizedWave( start_sample - window_size / 2, conv );
            double ret = 0.0;
            for ( int i = 0; i < window_size; i++ ) {
                double x = i / (double)window_size;
                ret += Math.Abs( conv[i] ) * math.window_func( window_function_type, x );
            }
            return ret / (double)window_size;
        }
#endif

#if !JAVA
        /// <summary>
        /// 音量の時間変化を取得します
        /// </summary>
        /// <param name="window_size">窓の幅（サンプル数）</param>
        /// <param name="window_function_type">使用する窓関数</param>
        /// <param name="resulution">解像度（サンプル数）</param>
        /// <returns></returns>
        public double[] GetVolume( int window_size, math.WindowFunctionType window_function_type ) {
            double[] conv = getNormalizedWave();
            for ( int i = 0; i < conv.Length; i++ ) {
                conv[i] = Math.Abs( conv[i] );
            }
            // 最初に重み関数を取得
            double[] w = new double[window_size];
            for ( int i = 0; i < window_size; i++ ) {
                double x = i / (double)window_size;
                w[i] = math.window_func( window_function_type, x );
            }
            double[] ret = new double[(int)m_total_samples];
            double inv = 1.0 / (double)window_size;
            int ws2 = window_size / 2;
            for ( int i = 0; i < m_total_samples; i++ ) {
                int start0 = i - ws2;
                int start = start0;
                int end = i + ws2;
                double tinv = inv;
                if ( start < 0 ) {
                    start = 0;
                    tinv = 1.0 / (double)(end - start + 1);
                }
                if ( m_total_samples <= end ) {
                    end = (int)m_total_samples - 1;
                    tinv = 1.0 / (double)(end - start + 1);
                }
                ret[i] = 0.0;
                for ( int j = start; j <= end; j++ ) {
                    ret[i] += conv[j] * w[i - start0];
                }
                ret[i] = ret[i] * tinv;
            }
            return ret;
        }
#endif

        public void getNormalizedWave( int start_index, double[] conv ) {
            int count = conv.Length;
            int cp_start = start_index;
            int cp_end = start_index + count;
            if ( start_index < 0 ) {
                for ( int i = 0; i < cp_start - start_index; i++ ) {
                    conv[i] = 0.0;
                }
                cp_start = 0;
            }
            if ( m_total_samples <= cp_end ) {
                for ( int i = cp_end - start_index; i < count; i++ ) {
                    conv[i] = 0.0;
                }
                cp_end = (int)m_total_samples - 1;
            }
            if ( m_channel == WaveChannel.Monoral ) {
                if ( m_bit_per_sample == 8 ) {
                    for ( int i = cp_start; i < cp_end; i++ ) {
                        conv[i - start_index] = (L8[i] - 64.0) / 64.0;
                    }
                } else {
                    for ( int i = cp_start; i < cp_end; i++ ) {
                        conv[i - start_index] = L16[i] / 32768.0;
                    }
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    for ( int i = cp_start; i < cp_end; i++ ) {
                        conv[i - start_index] = ((L8[i] + R8[i]) * 0.5 - 64.0) / 64.0;
                    }
                } else {
                    for ( int i = cp_start; i < cp_end; i++ ) {
                        conv[i - start_index] = (L16[i] + R16[i]) * 0.5 / 32768.0;
                    }
                }
            }
        }

        public double[] getNormalizedWave() {
            return getNormalizedWave( 0, m_total_samples );
        }

        /// <summary>
        /// -1から1までに規格化された波形を取得します
        /// </summary>
        /// <returns></returns>
        public double[] getNormalizedWave( int start_index, long count ) {
            double[] conv = new double[(int)count];
            getNormalizedWave( start_index, conv );
            return conv;
        }

        private void set( int index, Channel channel, int value ) {
            if ( m_channel == WaveChannel.Monoral || channel == Channel.Left ) {
                if ( m_bit_per_sample == 8 ) {
                    L8[index] = (byte)value;
                } else {
                    L16[index] = (short)value;
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    R8[index] = (byte)value;
                } else {
                    R16[index] = (short)value;
                }
            }
        }

        private int get( int index, Channel channel ) {
            if ( m_channel == WaveChannel.Monoral || channel == Channel.Left ) {
                if ( m_bit_per_sample == 8 ) {
                    return L8[index];
                } else {
                    return L16[index];
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    return R8[index];
                } else {
                    return R16[index];
                }
            }
        }

        private void set( int index, int value ) {
            set( index, Channel.Left, value );
            if ( m_channel == WaveChannel.Stereo ) {
                set( index, Channel.Right, value );
            }
        }

        public double getDouble( int index ) {
            if ( m_channel == WaveChannel.Monoral ) {
                if ( m_bit_per_sample == 8 ) {
                    return (L8[index] - 64.0) / 64.0;
                } else {
                    return L16[index] / 32768.0;
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    return ((L8[index] + R8[index]) * 0.5 - 64.0) / 64.0;
                } else {
                    return (L16[index] + R16[index]) * 0.5 / 32768.0;
                }
            }
        }

        public long getSampleRate() {
            return m_sample_rate;
        }

        private void setTotalSamples( long value ) {
            m_total_samples = value;
            if ( m_channel == WaveChannel.Monoral ) {
                if ( m_bit_per_sample == 8 ) {
                    if ( L8 == null ) {
                        L8 = new byte[(int)m_total_samples];
                    } else {
                        L8 = resizeArray( L8, (int)m_total_samples );
                    }
                } else {
                    if ( L16 == null ) {
                        L16 = new short[(int)m_total_samples];
                    } else {
                        L16 = resizeArray( L16, (int)m_total_samples );
                    }
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    if ( L8 == null ) {
                        L8 = new byte[(int)m_total_samples];
                        R8 = new byte[(int)m_total_samples];
                    } else {
                        L8 = resizeArray( L8, (int)m_total_samples );
                        R8 = resizeArray( R8, (int)m_total_samples );
                    }
                } else {
                    if ( L16 == null ) {
                        L16 = new short[(int)m_total_samples];
                        R16 = new short[(int)m_total_samples];
                    } else {
                        L16 = resizeArray( L16, (int)m_total_samples );
                        R16 = resizeArray( R16, (int)m_total_samples );
                    }
                }
            }
        }

        public long getTotalSamples() {
            return m_total_samples;
        }

        public void replace( Wave srcWave, int srcStart, int destStart, int count ) {
#if DEBUG
            Console.WriteLine( "Wave.Replace(Wave,uint,uint,uint)" );
#endif
            if ( m_channel != srcWave.m_channel || m_bit_per_sample != srcWave.m_bit_per_sample ) {
                return;
            }
            if ( m_channel == WaveChannel.Monoral ) {
                if ( m_bit_per_sample == 8 ) {
                    if ( L8 == null || srcWave.L8 == null ) {
                        return;
                    }
                } else {
                    if ( L16 == null || srcWave.L16 == null ) {
                        return;
                    }
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    if ( L8 == null || R8 == null || srcWave.L8 == null || srcWave.R8 == null ) {
                        return;
                    }
                } else {
                    if ( L16 == null || R16 == null || srcWave.L16 == null || srcWave.R16 == null ) {
                        return;
                    }
                }
            }

            count = (int)((count > srcWave.getTotalSamples() - srcStart) ? srcWave.getTotalSamples() - srcStart : count);
            long new_last_index = destStart + count;
            if ( m_total_samples < new_last_index ) {
                if ( m_channel == WaveChannel.Monoral ) {
                    if ( m_bit_per_sample == 8 ) {
                        L8 = resizeArray( L8, (int)new_last_index );
                    } else {
                        L16 = resizeArray( L16, (int)new_last_index );
                    }
                } else {
                    if ( m_bit_per_sample == 8 ) {
                        L8 = resizeArray( L8, (int)new_last_index );
                        R8 = resizeArray( R8, (int)new_last_index );
                    } else {
                        L16 = resizeArray( L16, (int)new_last_index );
                        R16 = resizeArray( R16, (int)new_last_index );
                    }
                }
                m_total_samples = new_last_index;
            }
            if ( m_channel == WaveChannel.Monoral ) {
                if ( m_bit_per_sample == 8 ) {
                    copyArray( srcWave.L8, srcStart, L8, destStart, count );
                } else {
                    copyArray( srcWave.L16, srcStart, L16, destStart, count );
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    copyArray( srcWave.L8, srcStart, L8, destStart, count );
                    copyArray( srcWave.R8, srcStart, R8, destStart, count );
                } else {
                    copyArray( srcWave.L16, srcStart, L16, destStart, count );
                    copyArray( srcWave.R16, srcStart, R16, destStart, count );
                }
            }
        }


        public void replace( byte[] data, int start_index ) {
            if ( m_channel != WaveChannel.Monoral || m_bit_per_sample != 8 || L8 == null ) {
                return;
            }
            long new_last_index = (long)(start_index + data.Length);
            if ( m_total_samples < new_last_index ) {
                L8 = resizeArray( L8, (int)new_last_index );
                m_total_samples = new_last_index;
            }
            for ( int i = 0; i < data.Length; i++ ) {
                L8[i + start_index] = data[i];
            }
        }


        public void replace( short[] data, int start_index ) {
            if ( m_channel != WaveChannel.Monoral || m_bit_per_sample != 16 || L16 == null ) {
                return;
            }
            long new_last_index = (long)(start_index + data.Length);
            if ( m_total_samples < new_last_index ) {
                L16 = resizeArray( L16, (int)new_last_index );
                m_total_samples = new_last_index;
            }
            for ( int i = 0; i < data.Length; i++ ) {
                L16[i + start_index] = data[i];
            }
        }


        public void replace( byte[] left, byte[] right, int start_index ) {
            if ( m_channel != WaveChannel.Stereo || m_bit_per_sample != 8 || L8 == null || R8 == null ) {
                return;
            }
            long new_last_index = (long)(start_index + left.Length);
            if ( m_total_samples < new_last_index ) {
                L8 = resizeArray( L8, (int)new_last_index );
                R8 = resizeArray( R8, (int)new_last_index );
                m_total_samples = new_last_index;
            }
            for ( int i = 0; i < left.Length; i++ ) {
                L8[i + start_index] = left[i];
                R8[i + start_index] = right[i];
            }
        }


        public void replace( short[] left, short[] right, int start_index ) {
            if ( m_channel != WaveChannel.Stereo || m_bit_per_sample != 16 || L16 == null || R16 == null ) {
                return;
            }
            long new_last_index = (long)(start_index + left.Length);
            if ( m_total_samples < new_last_index ) {
                L16 = resizeArray( L16, (int)new_last_index );
                R16 = resizeArray( R16, (int)new_last_index );
                m_total_samples = new_last_index;
            }
            for ( int i = 0; i < left.Length; i++ ) {
                L16[i + start_index] = left[i];
                R16[i + start_index] = right[i];
            }
        }

        public void replace( float[] left, float[] right, int start_index ) {
            long new_last_index = (long)(start_index + left.Length);
            if ( m_total_samples < new_last_index ) {
                if ( m_channel == WaveChannel.Monoral ) {
                    if ( m_bit_per_sample == 8 ) {
                        if ( L8 == null ) {
                            return;
                        }
                        L8 = resizeArray( L8, (int)new_last_index );
                    } else {
                        if ( L16 == null ) {
                            return;
                        }
                        L16 = resizeArray( L16, (int)new_last_index );
                    }
                } else {
                    if ( m_bit_per_sample == 8 ) {
                        if ( L8 == null || R8 == null ) {
                            return;
                        }
                        L8 = resizeArray( L8, (int)new_last_index );
                        R8 = resizeArray( R8, (int)new_last_index );
                    } else {
                        if ( L16 == null || R16 == null ) {
                            return;
                        }
                        L16 = resizeArray( L16, (int)new_last_index );
                        R16 = resizeArray( R16, (int)new_last_index );
                    }
                }
                m_total_samples = new_last_index;
            }
            if ( m_channel == WaveChannel.Monoral ) {
                float[] mono = new float[left.Length];
                for ( int i = 0; i < left.Length; i++ ) {
                    mono[i] = (left[i] + right[i]) / 2f;
                }
                if ( m_bit_per_sample == 8 ) {
                    for ( int i = 0; i < mono.Length; i++ ) {
                        L8[i + start_index] = (byte)((mono[i] + 1.0f) / 2f * 255f);
                    }
                } else {
                    for ( int i = 0; i < mono.Length; i++ ) {
                        L16[i + start_index] = (short)(mono[i] * 32768f);
                    }
                }
            } else {
                if ( m_bit_per_sample == 8 ) {
                    for ( int i = 0; i < left.Length; i++ ) {
                        L8[i + start_index] = (byte)((left[i] + 1.0f) / 2f * 255f);
                        R8[i + start_index] = (byte)((right[i] + 1.0f) / 2f * 255f);
                    }
                } else {
                    for ( int i = 0; i < left.Length; i++ ) {
                        L16[i + start_index] = (short)(left[i] * 32768f);
                        R16[i + start_index] = (short)(right[i] * 32768f);
                    }
                }
            }
        }

        public void printToText( String path ) {
            BufferedWriter sw = null;
            try {
                sw = new BufferedWriter( new FileWriter( path ) );
                if ( m_channel == WaveChannel.Monoral ) {
                    if ( m_bit_per_sample == 8 ) {
                        for ( int i = 0; i < m_total_samples; i++ ) {
                            sw.write( L8[i] + "" );
                            sw.newLine();
                        }
                    } else {
                        for ( int i = 0; i < m_total_samples; i++ ) {
                            sw.write( L16[i] + "" );
                            sw.newLine();
                        }
                    }
                } else {
                    if ( m_bit_per_sample == 8 ) {
                        for ( int i = 0; i < m_total_samples; i++ ) {
                            sw.write( L8[i] + "\t" + R8[i] );
                            sw.newLine();
                        }
                    } else {
                        for ( int i = 0; i < m_total_samples; i++ ) {
                            sw.write( L16[i] + "\t" + R16[i] );
                            sw.newLine();
                        }
                    }
                }
            } catch ( Exception ex ) {
            } finally {
                if ( sw != null ) {
                    try {
                        sw.close();
                    } catch ( Exception ex2 ) {
                    }
                }
            }
        }

        public void Dispose() {
            L8 = null;
            R8 = null;
            L16 = null;
            R16 = null;
#if JAVA
            System.gc();
#else
            GC.Collect();
#endif
        }

        /// <summary>
        /// サンプルあたりのビット数を8に変更する
        /// </summary>
        public void convertTo8Bit() {
            if ( m_bit_per_sample == 8 ) {
                return;
            }

            // 先ず左チャンネル
            L8 = new byte[L16.Length];
            for ( int i = 0; i < L16.Length; i++ ) {
                double new_val = (L16[i] + 32768.0) / 65535.0 * 255.0;
                L8[i] = (byte)new_val;
            }
            L16 = null;

            // 存在すれば右チャンネルも
            if ( m_channel == WaveChannel.Stereo ) {
                R8 = new byte[R16.Length];
                for ( int i = 0; i < R16.Length; i++ ) {
                    double new_val = (R16[i] + 32768.0) / 65535.0 * 255.0;
                    R8[i] = (byte)new_val;
                }
                R16 = null;
            }

            m_bit_per_sample = 8;
        }

        /// <summary>
        /// ファイルに保存
        /// </summary>
        /// <param name="file"></param>
        public void write( String file ) {
            RandomAccessFile fs = null;
            try {
                fs = new RandomAccessFile( file, "rw" );
                // RIFF
                fs.write( 0x52 );
                fs.write( 0x49 );
                fs.write( 0x46 );
                fs.write( 0x46 );

                // ファイルサイズ - 8最後に記入
                fs.write( 0x00 );
                fs.write( 0x00 );
                fs.write( 0x00 );
                fs.write( 0x00 );

                // WAVE
                fs.write( 0x57 );
                fs.write( 0x41 );
                fs.write( 0x56 );
                fs.write( 0x45 );

                // fmt 
                fs.write( 0x66 );
                fs.write( 0x6d );
                fs.write( 0x74 );
                fs.write( 0x20 );

                // fmt チャンクのサイズ
                fs.write( 0x12 );
                fs.write( 0x00 );
                fs.write( 0x00 );
                fs.write( 0x00 );

                // format ID
                fs.write( 0x01 );
                fs.write( 0x00 );

                // チャンネル数
                if ( m_channel == WaveChannel.Monoral ) {
                    fs.write( 0x01 );
                    fs.write( 0x00 );
                } else {
                    fs.write( 0x02 );
                    fs.write( 0x00 );
                }

                // サンプリングレート
                byte[] buf = PortUtil.getbytes_uint32_le( m_sample_rate );
                writeByteArray( fs, buf, 4 );

                // データ速度
                int ichannel = (m_channel == WaveChannel.Monoral) ? 1 : 2;
                int block_size = (int)(m_bit_per_sample / 8 * ichannel);
                long data_rate = m_sample_rate * block_size;
                buf = PortUtil.getbytes_uint32_le( data_rate );
                writeByteArray( fs, buf, 4 );

                // ブロックサイズ
                buf = PortUtil.getbytes_uint16_le( block_size );
                writeByteArray( fs, buf, 2 );

                // サンプルあたりのビット数
                buf = PortUtil.getbytes_uint16_le( m_bit_per_sample );
                writeByteArray( fs, buf, 2 );

                // 拡張部分
                fs.write( 0x00 );
                fs.write( 0x00 );

                // data
                fs.write( 0x64 );
                fs.write( 0x61 );
                fs.write( 0x74 );
                fs.write( 0x61 );

                // size of data chunk
                long size = block_size * m_total_samples;
                buf = PortUtil.getbytes_uint32_le( size );
                writeByteArray( fs, buf, 4 );

                // 波形データ
                for ( int i = 0; i < m_total_samples; i++ ) {
                    if ( m_bit_per_sample == 8 ) {
                        fs.write( L8[i] );
                        if ( m_channel == WaveChannel.Stereo ) {
                            fs.write( R8[i] );
                        }
                    } else {
                        buf = PortUtil.getbytes_int16_le( L16[i] );
                        writeByteArray( fs, buf, 2 );
                        if ( m_channel == WaveChannel.Stereo ) {
                            buf = PortUtil.getbytes_int16_le( R16[i] );
                            writeByteArray( fs, buf, 2 );
                        }
                    }
                }

                // 最後にWAVEチャンクのサイズ
                long position = fs.getFilePointer();
                fs.seek( 4 );
                buf = PortUtil.getbytes_uint32_le( position - 8 );
                writeByteArray( fs, buf, 4 );
            } catch ( Exception ex ) {
            } finally {
                if ( fs != null ) {
                    try {
                        fs.close();
                    } catch ( Exception ex2 ) {
                    }
                }
            }
        }

        private static void writeByteArray( RandomAccessFile fs, byte[] dat, int limit )
#if JAVA
            throws IOException
#endif
 {
            fs.write( dat, 0, (dat.Length > limit) ? limit : dat.Length );
            if ( dat.Length < limit ) {
                for ( int i = 0; i < limit - dat.Length; i++ ) {
                    fs.write( 0x00 );
                }
            }
        }

        /// <summary>
        /// ステレオをモノラル化
        /// </summary>
        public void monoralize() {
            if ( m_channel != WaveChannel.Stereo ) {
                return;
            }
            if ( m_bit_per_sample == 8 ) {
                for ( int i = 0; i < L8.Length; i++ ) {
                    L8[i] = (byte)((L8[i] + R8[i]) / 2);
                }
                R8 = null;
                m_channel = WaveChannel.Monoral;
            } else {
                for ( int i = 0; i < L16.Length; i++ ) {
                    L16[i] = (short)((L16[i] + R16[i]) / 2);
                }
                R16 = null;
                m_channel = WaveChannel.Monoral;
            }
        }


        /// <summary>
        /// 前後の無音部分を削除します
        /// </summary>
        public void trimSilence() {
#if DEBUG
            Console.WriteLine( "Wave.TrimSilence" );
#endif
            if ( m_bit_per_sample == 8 ) {
                if ( m_channel == WaveChannel.Monoral ) {
                    int non_silence_begin = 1;
                    for ( int i = 1; i < L8.Length; i++ ) {
                        if ( L8[i] != 0x80 ) {
                            non_silence_begin = i;
                            break;
                        }
                    }
                    int non_silence_end = L8.Length - 1;
                    for ( int i = L8.Length - 1; i >= 0; i-- ) {
                        if ( L8[i] != 0x80 ) {
                            non_silence_end = i;
                            break;
                        }
                    }
                    int count = non_silence_end - non_silence_begin + 1;
                    R8 = new byte[count];
                    copyArray( L8, non_silence_begin, R8, 0, count );
                    L8 = null;
                    L8 = new byte[count];
                    copyArray( R8, 0, L8, 0, count );
                    R8 = null;
                } else {
                    int non_silence_begin = 1;
                    for ( int i = 1; i < L8.Length; i++ ) {
                        if ( L8[i] != 0x80 || R8[i] != 0x80 ) {
                            non_silence_begin = i;
                            break;
                        }
                    }
                    int non_silence_end = L8.Length - 1;
                    for ( int i = L8.Length - 1; i >= 0; i-- ) {
                        if ( L8[i] != 0x80 || R8[i] != 0x80 ) {
                            non_silence_end = i;
                            break;
                        }
                    }
                    int count = non_silence_end - non_silence_begin + 1;
                    byte[] buf = new byte[count];
                    copyArray( L8, non_silence_begin, buf, 0, count );
                    L8 = null;
                    L8 = new byte[count];
                    copyArray( buf, 0, L8, 0, count );
                    copyArray( R8, non_silence_begin, buf, 0, count );
                    R8 = null;
                    R8 = new byte[count];
                    copyArray( buf, 0, R8, 0, count );
                    buf = null;
                }
            } else {
                if ( m_channel == WaveChannel.Monoral ) {
                    int non_silence_begin = 1;
                    for ( int i = 1; i < L16.Length; i++ ) {
                        if ( L16[i] != 0 ) {
                            non_silence_begin = i;
                            break;
                        }
                    }
                    int non_silence_end = L16.Length - 1;
                    for ( int i = L16.Length - 1; i >= 0; i-- ) {
                        if ( L16[i] != 0 ) {
                            non_silence_end = i;
                            break;
                        }
                    }
                    int count = non_silence_end - non_silence_begin + 1;
                    R16 = new short[count];
                    copyArray( L16, non_silence_begin, R16, 0, count );
                    L16 = resizeArray( L16, count );
                    copyArray( R16, 0, L16, 0, count );
                    R16 = null;
                } else {
                    int non_silence_begin = 1;
                    for ( int i = 1; i < L16.Length; i++ ) {
                        if ( L16[i] != 0 || R16[i] != 0 ) {
                            non_silence_begin = i;
                            break;
                        }
                    }
                    int non_silence_end = L16.Length - 1;
                    for ( int i = L16.Length - 1; i >= 0; i-- ) {
                        if ( L16[i] != 0 || R16[i] != 0 ) {
                            non_silence_end = i;
                            break;
                        }
                    }
#if DEBUG
                    Console.WriteLine( "    non_silence_beign=" + non_silence_begin );
                    Console.WriteLine( "    non_silence_end=" + non_silence_end );
#endif
                    int count = non_silence_end - non_silence_begin + 1;
                    short[] buf = new short[count];
                    copyArray( L16, non_silence_begin, buf, 0, count );
                    L16 = resizeArray( L16, count );
                    copyArray( buf, 0, L16, 0, count );

                    copyArray( R16, non_silence_begin, buf, 0, count );
                    R16 = resizeArray( R16, count );
                    copyArray( buf, 0, R16, 0, count );
                    buf = null;
                }
            }

            if ( m_bit_per_sample == 8 ) {
                m_total_samples = (long)L8.Length;
            } else {
                m_total_samples = (long)L16.Length;
            }
        }

        public Wave() {
            m_channel = WaveChannel.Stereo;
            m_bit_per_sample = 16;
            m_sample_rate = 44100;
        }

        public Wave( WaveChannel channels, int bit_per_sample, int sample_rate, int initial_capacity ) {
            m_channel = channels;
            m_bit_per_sample = bit_per_sample;
            m_sample_rate = sample_rate;
            m_total_samples = initial_capacity;
            if ( m_bit_per_sample == 8 ) {
                L8 = new byte[(int)m_total_samples];
                if ( m_channel == WaveChannel.Stereo ) {
                    R8 = new byte[(int)m_total_samples];
                }
            } else if ( m_bit_per_sample == 16 ) {
                L16 = new short[(int)m_total_samples];
                if ( m_channel == WaveChannel.Stereo ) {
                    R16 = new short[(int)m_total_samples];
                }
            }
        }

        public void append( double[] L, double[] R ) {
            int length = Math.Min( L.Length, R.Length );
            int old_length = L16.Length;
            if ( m_bit_per_sample == 16 && m_channel == WaveChannel.Stereo ) {
                L16 = resizeArray( L16, (int)(m_total_samples + length) );
                R16 = resizeArray( R16, (int)(m_total_samples + length) );
                m_total_samples += (long)length;
                for ( int i = old_length; i < m_total_samples; i++ ) {
                    L16[i] = (short)(L[i - old_length] * 32768f);
                    R16[i] = (short)(R[i - old_length] * 32768f);
                }
            } //else ... TODO: Wave+Append他のbitpersec, channelのとき
        }

        public Wave( String path ) {
            read( path );
        }

        private boolean parseAiffHeader( RandomAccessFile fs ) {
            try {
                byte[] buf = new byte[4];
                fs.read( buf, 0, 4 );
                long chunk_size_form = PortUtil.make_uint32_be( buf );
                fs.read( buf, 0, 4 ); // AIFF
                String tag = new String( new char[] { (char)buf[0], (char)buf[1], (char)buf[2], (char)buf[3] } );
                if ( !tag.Equals( "AIFF" ) ) {
#if DEBUG
                    Console.WriteLine( "Wave#parseAiffHeader; error; tag=" + tag + " and must be AIFF" );
#endif
                    return false;
                }
                fs.read( buf, 0, 4 ); // COMM
                tag = new String( new char[] { (char)buf[0], (char)buf[1], (char)buf[2], (char)buf[3] } );
                if ( !tag.Equals( "COMM" ) ) {
#if DEBUG
                    Console.WriteLine( "Wave#parseAiffHeader; error; tag=" + tag + " and must be COMM" );
#endif
                    return false;
                }
                fs.read( buf, 0, 4 ); // COMM chunk size
                long chunk_size_comm = PortUtil.make_uint32_be( buf );
                long chunk_loc_comm = fs.getFilePointer();
                fs.read( buf, 0, 2 ); // number of channel
                int num_channel = PortUtil.make_uint16_be( buf );
                if ( num_channel == 1 ) {
                    m_channel = WaveChannel.Monoral;
                } else {
                    m_channel = WaveChannel.Stereo;
                }
                fs.read( buf, 0, 4 ); // number of samples
                m_total_samples = PortUtil.make_uint32_be( buf );
#if DEBUG
                Console.WriteLine( "Wave#parseAiffHeader; m_total_samples=" + m_total_samples );
#endif
                fs.read( buf, 0, 2 ); // block size
                m_bit_per_sample = PortUtil.make_uint16_be( buf );
                byte[] buf10 = new byte[10];
                fs.read( buf10, 0, 10 ); // sample rate
                m_sample_rate = (long)make_double_from_extended( buf10 );
#if DEBUG
                Console.WriteLine( "Wave#parseAiffHeader; m_sample_rate=" + m_sample_rate );
#endif
                fs.seek( chunk_loc_comm + (long)chunk_size_comm );

                fs.read( buf, 0, 4 ); // SSND
                tag = new String( new char[] { (char)buf[0], (char)buf[1], (char)buf[2], (char)buf[3] } );
                if ( !tag.Equals( "SSND" ) ) {
#if DEBUG
                    Console.WriteLine( "Wave#parseAiffHeader; error; tag=" + tag + " and must be SSND" );
#endif
                    return false;
                }
                fs.read( buf, 0, 4 ); // SSND chunk size
                long chunk_size_ssnd = PortUtil.make_uint32_be( buf );
            } catch ( Exception ex ) {
                return false;
            }
            return true;
        }

        private static double make_double_from_extended( byte[] bytes ) {
            double f;
            long hiMant, loMant;

            int expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
            hiMant = ((long)(bytes[2] & 0xFF) << 24)
                   | ((long)(bytes[3] & 0xFF) << 16)
                   | ((long)(bytes[4] & 0xFF) << 8)
                   | ((long)(bytes[5] & 0xFF));
            loMant = ((long)(bytes[6] & 0xFF) << 24)
                   | ((long)(bytes[7] & 0xFF) << 16)
                   | ((long)(bytes[8] & 0xFF) << 8)
                   | ((long)(bytes[9] & 0xFF));

            if ( expon == 0 && hiMant == 0 && loMant == 0 ) {
                f = 0;
            } else {
                if ( expon == 0x7FFF ) {
                    f = double.MaxValue;
                } else {
                    expon -= 16383;
                    //f = ldexp( double_from_uint( hiMant ), expon -= 31 );
                    f = Math.Pow( 2.0, expon - 31 ) * double_from_uint( hiMant );
                    //f += ldexp( double_from_uint( loMant ), expon -= 32 );
                    f += Math.Pow( 2.0, expon - 32 ) * double_from_uint( loMant );
                }
            }

            if ( (bytes[0] & 0x80) != 0 ) {
                return -f;
            } else {
                return f;
            }
        }

        private static double double_from_uint( long u ) {
            return (((double)((int)(u - 2147483647 - 1))) + 2147483648.0);
        }

        private boolean parseWaveHeader( RandomAccessFile fs ) {
            try {
                byte[] buf = new byte[4];
                // detect size of RIFF chunk
                fs.read( buf, 0, 4 );
                long riff_chunk_size = PortUtil.make_uint32_le( buf );
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "riff_chunk_size=" + riff_chunk_size );
#endif

                // check wave header
                fs.seek( 8 );
                fs.read( buf, 0, 4 );
                if ( buf[0] != 0x57 ||
                    buf[1] != 0x41 ||
                    buf[2] != 0x56 ||
                    buf[3] != 0x45 ) {
#if DEBUG
                    System.Diagnostics.Debug.WriteLine( "invalid wave header" );
#endif
                    fs.close();
                    return false;
                }

                // check fmt chunk header
                fs.read( buf, 0, 4 );
                if ( buf[0] != 0x66 ||
                    buf[1] != 0x6d ||
                    buf[2] != 0x74 ||
                    buf[3] != 0x20 ) {
#if DEBUG
                    System.Diagnostics.Debug.WriteLine( "invalid fmt header" );
#endif
                    fs.close();
                    return false;
                }

                // detect size of fmt chunk
                long fmt_chunk_bytes;
                fs.read( buf, 0, 4 );
                fmt_chunk_bytes = PortUtil.make_uint32_le( buf );
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "fmt_chunk_bytes=" + fmt_chunk_bytes );
#endif

                // get format ID
                fs.read( buf, 0, 2 );
                int format_id = PortUtil.make_uint16_le( buf );
                if ( format_id != 1 ) {
                    fs.close();
                    return false;
                }
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "format_id=" + format_id );
#endif

                // get the number of channel(s)
                fs.read( buf, 0, 2 );
                int num_channels = PortUtil.make_uint16_le( buf );
                if ( num_channels == 1 ) {
                    m_channel = WaveChannel.Monoral;
                } else if ( num_channels == 2 ) {
                    m_channel = WaveChannel.Stereo;
                } else {
                    fs.close();
                    return false;
                }
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "num_channels=" + num_channels );
#endif

                // get sampling rate
                fs.read( buf, 0, 4 );
                m_sample_rate = PortUtil.make_uint32_le( buf );
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "m_sample_rate=" + m_sample_rate );
#endif

                // get bit per sample
                fs.seek( 0x22 );
                fs.read( buf, 0, 2 );
                m_bit_per_sample = PortUtil.make_uint16_le( buf );
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "m_bit_per_sample=" + m_bit_per_sample );
#endif
                if ( m_bit_per_sample != 0x08 && m_bit_per_sample != 0x10 ) {
                    fs.close();
                    return false;
                }

                // move to the end of fmt chunk
                fs.seek( 0x14 + fmt_chunk_bytes );

                // move to the top of data chunk
                fs.read( buf, 0, 4 );
                String tag = new String( new char[] { (char)buf[0], (char)buf[1], (char)buf[2], (char)buf[3] } );
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "tag=" + tag );
#endif
                while ( tag != "data" ) {
                    fs.read( buf, 0, 4 );
                    long size = PortUtil.make_uint32_le( buf );
                    fs.seek( fs.getFilePointer() + size );
                    fs.read( buf, 0, 4 );
                    tag = new String( new char[] { (char)buf[0], (char)buf[1], (char)buf[2], (char)buf[3] } );
#if DEBUG
                    System.Diagnostics.Debug.WriteLine( "tag=" + tag );
#endif
                }

                // get size of data chunk
                fs.read( buf, 0, 4 );
                long data_chunk_bytes = PortUtil.make_uint32_le( buf );
                m_total_samples = (long)(data_chunk_bytes / (num_channels * m_bit_per_sample / 8));
#if DEBUG
                System.Diagnostics.Debug.WriteLine( "m_total_samples=" + m_total_samples );
#endif

            } catch ( Exception ex ) {
#if DEBUG
                System.Diagnostics.Debug.Write( ex.StackTrace );
#endif
            }
            return true;
        }

        public boolean read( String path ) {
            RandomAccessFile fs = null;
            boolean ret = false;
            try {
                fs = new RandomAccessFile( path, "r" );
                // check RIFF header
                byte[] buf = new byte[4];
                fs.read( buf, 0, 4 );
                boolean change_byte_order = false;
                if ( buf[0] == 0x52 && buf[1] == 0x49 && buf[2] == 0x46 && buf[3] == 0x46 ) {
                    ret = parseWaveHeader( fs );
                } else if ( buf[0] == 0x46 && buf[1] == 0x4f && buf[2] == 0x52 && buf[3] == 0x4d ) {
                    ret = parseAiffHeader( fs );
                    change_byte_order = true;
                } else {
                    ret = false;
                }
                // prepare data
                if ( m_bit_per_sample == 8 ) {
                    L8 = new byte[(int)m_total_samples];
                    if ( m_channel == WaveChannel.Stereo ) {
                        R8 = new byte[(int)m_total_samples];
                    }
                } else {
                    L16 = new short[(int)m_total_samples];
                    if ( m_channel == WaveChannel.Stereo ) {
                        R16 = new short[(int)m_total_samples];
                    }
                }

                // read data
                // TODO: big endianのときの読込み。
                byte[] buf2 = new byte[2];
                for ( int i = 0; i < m_total_samples; i++ ) {
                    if ( m_bit_per_sample == 8 ) {
                        fs.read( buf, 0, 1 );
                        L8[i] = buf[0];
                        if ( m_channel == WaveChannel.Stereo ) {
                            fs.read( buf, 0, 1 );
                            R8[i] = buf[0];
                        }
                    } else {
                        fs.read( buf2, 0, 2 );
                        if ( change_byte_order ) {
                            byte b = buf2[0];
                            buf2[0] = buf2[1];
                            buf2[1] = b;
                        }
                        L16[i] = PortUtil.make_int16_le( buf2 );
                        if ( m_channel == WaveChannel.Stereo ) {
                            fs.read( buf2, 0, 2 );
                            if ( change_byte_order ) {
                                byte b = buf2[0];
                                buf2[0] = buf2[1];
                                buf2[1] = b;
                            }
                            R16[i] = PortUtil.make_int16_le( buf2 );
                        }
                    }
                }
            } catch ( Exception ex ) {
            } finally {
                if ( fs != null ) {
                    try {
                        fs.close();
                    } catch ( Exception ex2 ) {
                    }
                }
            }
            return ret;
        }

        private static short[] resizeArray( short[] src, int length ) {
            short[] ret = new short[length];
            if ( src.Length <= length ) {
                for ( int i = 0; i < src.Length; i++ ) {
                    ret[i] = src[i];
                }
            } else {
                for ( int i = 0; i < length; i++ ) {
                    ret[i] = src[i];
                }
            }
            return ret;
        }

        private static byte[] resizeArray( byte[] src, int length ) {
            byte[] ret = new byte[length];
            if ( src.Length <= length ) {
                for ( int i = 0; i < src.Length; i++ ) {
                    ret[i] = src[i];
                }
            } else {
                for ( int i = 0; i < length; i++ ) {
                    ret[i] = src[i];
                }
            }
            return ret;
        }

        private static void copyArray( short[] src, int src_start, short[] dest, int dest_start, int length ) {
            for ( int i = 0; i < length; i++ ) {
                dest[i + dest_start] = src[i + src_start];
            }
        }

        private static void copyArray( byte[] src, int src_start, byte[] dest, int dest_start, int length ) {
            for ( int i = 0; i < length; i++ ) {
                dest[i + dest_start] = src[i + src_start];
            }
        }
    }

#if !JAVA
}
#endif
