{"cells":[{"metadata":{},"cell_type":"markdown","source":"_**```author 'Kashirin Alex <kashirin.alex@gmail.com>'```**_\n\n# Multi Band Audio Extractor\n\n## Main-Functionalities\n\n* Read Audio Fle\n* Extract desired Bands from an Audio Data\n* Preview plot the Audio Data  (wave-amplitude)\n* Write Audio Data to file \n* Recognize Dense Peaks of the Audio Data of specified samples duration\n\n\n## Conclusion - Result\nThe 60 seconds of noise/polluted audio-data summarised in couple(`r.recognize_dense_peaks(data, n_samples=48000 * 3)` the n_samples ) of seconds of pure-sound.\n\n\n\n\n\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import IPython.display as ipd # for playing audio","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## The MultiBandAudioExtractor class\n\n### Fields/Attributes\n* filepath -- the audio file-path\n* data -- the original audio-data\n* samplerate -- the audio-data sample-rate\n* milliseconds -- the audio-data duration\n* total_samples -- the audio-data total-samples\n\n\n### Methods\n* extract_ifft -- returns the extracted audio-data in ndarray\n* write1 -- write audio data to file with SoundFile\n* write2 -- write audio data with `wave` module `.wav` in int24 bits-width\n* preview -- plot (show/write) audio-data, x-samples y-amplitude \n\n### Static Methods\n* preamp -- Preamplify the signal (it is as well used within the class)\n* recognize_dense_peaks -- returns data-index in tuple(start, finish)\n* get_ranges_name -- helper to create file/name from ranges ```[(start, finish), ]```\n* \n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# -- coding: utf-8 --\n__author__ = 'Kashirin Alex <kashirin.alex@gmail.com>'\n\n\nimport soundfile as sf\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom scipy import fftpack\nimport math\n#\nimport wave\nimport struct\n#\nimport os\nimport warnings\nwarnings.simplefilter('ignore', np.ComplexWarning)\n#\n\n\nclass MultiBandAudioExtractor(object):\n\n    POW_23 = math.pow(2, 23)\n\n    def __init__(self, _filepath):\n        self.filepath = _filepath\n\n        with open(_filepath, 'rb') as f:\n            self.data, self.samplerate = sf.read(f)\n        self.total_samples = len(self.data)\n        self.preamp(self.data)\n\n        self.milliseconds = int(float(self.total_samples/self.samplerate)*1000)\n        #\n\n    @staticmethod\n    def preamp(_data):\n        _preamp = 0.99 / (1 - min([1.0 - max(_data), 1.0 + min(_data)]))\n        if _preamp > 1.0:\n            for n in range(0, len(_data)):\n                _data[n] += 1.0\n                _data[n] *= _preamp\n                _data[n] -= _preamp\n            mid = sum(_data) / len(_data)\n            for n in range(0, len(_data)):\n                _data[n] -= mid\n            _post_preamp = 0.99 / (1 - min([1.0 - max(_data), 1.0 + min(_data)]))\n            print('preamp:', _preamp, 'mid:', mid, 'remain-preamp:', _post_preamp)\n        #\n\n    def extract_ifft(self, _freq_bands=None):\n        fft = fftpack.fft(self.data)\n        if _freq_bands is None:\n            _data = fftpack.ifft(fft)\n            self.preamp(_data)\n            return _data\n\n        sample_freq = fftpack.fftfreq(len(fft), d=1.0 / self.samplerate)\n        _data = None\n        for start, finish in _freq_bands:\n            fft_tmp = fft.copy()\n            if start:\n                fft_tmp[np.abs(sample_freq) < start] = 0.0\n            if finish:\n                fft_tmp[np.abs(sample_freq) > finish] = 0.0\n            if _data is None:\n                _data = fftpack.ifft(fft_tmp)\n            else:\n                tmp = fftpack.ifft(fft_tmp)\n                for n in range(0, len(_data)):\n                    _data[n] += tmp[0]\n        if _data is None:\n            return []\n        self.preamp(_data)\n        return _data\n        #\n\n    @staticmethod\n    def get_ranges_name(_ranges):\n        return str(_ranges if _ranges is None else '_'.join([str(_s)+'-'+str(_f) for _s, _f in _ranges]))\n        #\n\n    def write1(self, _filename, _data, _samples_range=None):\n        _s, _f = (0, len(_data) - 1) if _samples_range is None else _samples_range\n        with sf.SoundFile(_filename, 'w+', channels=1, samplerate=self.samplerate) as fd:\n            fd.write([float(_data[n]) for n in range(_s, _f+1)])\n        #\n\n    def write2(self, _filename, _data, _samples_range=None):\n        _s, _f = (0, len(_data) - 1) if _samples_range is None else _samples_range\n        wf = wave.open(_filename, mode='w')\n        wf.setnchannels(1)\n        wf.setsampwidth(3)\n        wf.setframerate(self.samplerate)\n        buffer = bytearray()\n        for n in range(_s, _f + 1):\n            buffer += bytearray(struct.pack('<i', int(float(_data[n]) * self.POW_23))[:-1])\n        wf.writeframes(bytes(buffer))\n        wf.close()\n        #\n\n    def preview(self, _filename, _data, _samples_range=None):\n        if _samples_range is None:\n            _s = 0\n            _f = len(_data)\n            _width = int(_f/(self.samplerate/2))\n            _f -= 1\n        else:\n            _width = 60\n            _s, _f = _samples_range\n            _f1 = len(_data)\n            if _f >= _f1:\n                _f = _f1 - 1\n\n        plt.rcParams['agg.path.chunksize'] = (_f-_s) * 2\n        plt.figure(figsize=(_width, 10))\n        x = np.linspace(0.00, _f-_s, num=_f-_s, endpoint=False)\n\n        plt.plot(x, _data[_s:_f])\n        plt.xlabel('Time (samples)')\n        plt.ylabel('Amplitude ($Unit$)')\n        if _filename:\n            plt.savefig(_filename)\n        else:\n            plt.show()\n        plt.close()\n        #\n\n    @staticmethod\n    def recognize_dense_peaks(_data, n_samples=800):\n        offset = int(n_samples/2)\n        last_idx = len(_data) - 1\n\n        tmp = np.absolute(_data)\n        _mean = tmp.mean()\n        _max = tmp.max()\n        idx = None\n        density = 0\n        for n in range(0, len(tmp)):\n            if _mean < tmp[n] < _max:\n                _s = (n - offset) if n > offset else 0\n                _f = last_idx if last_idx <= n + offset else (n + offset)\n                _sum = tmp[_s:_f].sum()\n                if density < _sum:\n                    density = _sum\n                    idx = n\n        return None if idx is None \\\n            else ((idx - offset) if idx > offset else 0,\n                  last_idx if last_idx <= (idx + offset) else (idx + offset))\n        #\n\n#","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n\n## Process Extraction\n\n### Define the Actions for Proceeding of the Name and the Frequency-Bands"},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndef process_extraction(r, name, freq_bands):\n    filepath = r.filepath.split('/')[-1].split('.')[0] + '-'\n    filepath += name + '_bands(' + r.get_ranges_name(freq_bands) + ')'\n    \n    print('Extracting ', filepath)\n    \n    ## Extract the corresponding frequency-bands\n    data = r.extract_ifft(freq_bands)\n    # Write\n    r.write2(filepath + '.wav', data)\n    \n    # Play\n    ipd.Audio(filepath + '.wav')\n   \n    # Plot\n    r.preview(None, data, [0, 2048])\n        \n    \n    ## Recognize Dense Peaks\n    dense_range = r.recognize_dense_peaks(data, n_samples=48000 * 3)\n    \n    filepath += '_dense-samples(' + r.get_ranges_name([dense_range]) + ')'\n    \n    print('making ', filepath, \"dense_range:\", dense_range, \"second-start:\", dense_range[0]/r.samplerate)\n    \n    if dense_range:\n        # Write\n        r.write2(filepath + '.wav', data, dense_range)\n        \n        # Play\n        ipd.Audio(filepath + '.wav')\n\n        # Plot\n        r.preview(None, data, dense_range)\n    \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n\n## Process File\n\n#### Initialize the ```MultiBandAudioExtractor``` with the filepath\n#### Data summary\n#### Define & Proceed to Extract:\n* ##### Birds\n   "},{"metadata":{"trusted":true},"cell_type":"code","source":"\n\ndef process_file(filepath):\n    print(\"Processing\", filepath)\n    \n    r = MultiBandAudioExtractor(filepath)\n\n    print(r.filepath,\n          'sample-rate:', r.samplerate,\n          'milliseconds:', r.milliseconds,\n          'total-samples:', r.total_samples)\n    \n    # Extract Birds\n    process_extraction(r, 'Birds', [\n        (2900, 3100),\n        (4700, 5000),\n        (7100, 7300),\n        (10800, 11100)\n    ])\n    \n    \n    # for name, freq_bands in [\n        # ('200HzTest', [(200, 201)]),\n        #('Frogs', [\n        #    (1200, 1300),\n        #    (1700, 1800),\n        #    (2900, 2920),\n        #])\n    # ]:\n        \n#","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Read the Directories for Audio Samples"},{"metadata":{"trusted":true},"cell_type":"code","source":"\nnumfiles = 0\nfiles = []\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    numfiles += len(filenames)\n    for filename in filenames:\n        if filename.endswith(\".flac\"):\n            files.append(os.path.join(dirname, filename))\n\nprint(\"Total Audio Samples\", numfiles)\nfor filepath in files[0:10]:\n    process_file(filepath)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}