<commit_before>import os
import subprocess
from tempfile import TemporaryFile, NamedTemporaryFile
from StringIO import StringIO
import wave
import audioop

from .utils import _fd_or_path_or_tempfile, db_to_float
from .exceptions import UnsupportedOuputFormat, TooManyMissingFrames, InvalidDuration


AUDIO_FILE_EXT_ALIASES = {
    "m4a": "mp4"
}

class AudioSegment(object):
    """
    Note: AudioSegment objects are immutable
    
    slicable using milliseconds. for example: Get the first second of an mp3...
    
    a = AudioSegment.from_mp3(mp3file)
    first_second = a[:1000]
    """
    
    def __init__(self, data=None, *args, **kwargs):
        if kwargs.get('metadata', False):
            # internal use only
            self._data = data
            for attr, val in kwargs.pop('metadata').items():
                setattr(self, attr, val)
        else:
            # normal construction
            data = data if isinstance(data, basestring) else data.read()
            
            raw = wave.open(StringIO(data), 'rb')
            
            raw.rewind()
            self.channels = raw.getnchannels()
            self.sample_width = raw.getsampwidth() 
            self.frame_rate = raw.getframerate()
            self.frame_width = self.channels * self.sample_width
            
            
            raw.rewind()
            self._data = raw.readframes(float('inf'))
        
        super(AudioSegment, self).__init__(*args, **kwargs)
            
    
    def __len__(self):
        """
        returns the length of this audio segment in milliseconds
        """
        return round(1000 * (self.frame_count() / self.frame_rate))
    
    
    def __iter__(self):
        return (self[i] for i in xrange(len(self)))
    
    def __getitem__(self, millisecond):
        if isinstance(millisecond, slice):
            start = millisecond.start if millisecond.start is not None else 0
            end = millisecond.stop if millisecond.stop is not None else len(self)
            
            start = min(start, len(self))
            end = min(end, len(self))
        else:
            start = millisecond
            end = millisecond + 1
        
        start = self._parse_position(start) * self.frame_width
        end = self._parse_position(end) * self.frame_width
        data = self._data[start:end]
        
        # ensure the output is as long as the requester is expecting
        expected_length = end - start
        missing_frames = (expected_length - len(data)) / self.frame_width
        if missing_frames:
            if missing_frames > self.frame_count(ms=2):
                raise TooManyMissingFrames("You should never be filling in more than 2 ms with silence here, missing frames: %s" % missing_frames)
            silence = audioop.mul(data[:self.frame_width], self.sample_width, 0)
            data += (silence * missing_frames)
        
        return self._spawn(data)


    def __add__(self, arg):
        if isinstance(arg, AudioSegment):
            return self.append(arg, crossfade=0)
        else:
            return self.apply_gain(arg)
        
        
    def __sub__(self, arg):
        if isinstance(arg, AudioSegment):
            raise TypeError("AudioSegment objects can't be subtracted from each other")
        else:
            return self.apply_gain(-arg)
        
    
    def __mul__(self, arg):
        """
        If the argument is an AudioSegment, overlay the multiplied audio 
        segment.
        
        
        If it's a number, just use the string multiply operation to repeat the 
        audio so the following would return an AudioSegment that contains the 
        audio of audio_seg eight times
        
        audio_seg * 8
        """
        if isinstance(arg, AudioSegment):
            return self.overlay(arg, position=0, loop=True)
        else:
            return self._spawn(data=self._data*arg)
    
    
    def _spawn(self, data, overrides={}):
        """
        Creates a new audio segment using the meta data from the current one
        and the data passed in. Should be used whenever an AudioSegment is 
        being returned by an operation that alters the current one, since
        AudioSegment objects are immutable.
        """
        # accept lists of data chunks 
        if isinstance(data, list):
            data = ''.join(data)
            
        # accept file-like objects
        if hasattr(data, 'read'):
            if hasattr(data, 'seek'): 
                data.seek(0)
            data = data.read()
            
        metadata = {
                    'sample_width': self.sample_width,
                    'frame_rate': self.frame_rate,
                    'frame_width': self.frame_width,
                    'channels': self.channels
                    }
        metadata.update(overrides)
        return AudioSegment(data=data, metadata=metadata)
    
    
    @classmethod
    def _sync(cls, seg1, seg2):
        s1_len, s2_len = len(seg1), len(seg2)
        
        channels = max(seg1.channels, seg2.channels)
        seg1 = seg1.set_channels(channels)
        seg2 = seg2.set_channels(channels)
        
        frame_rate = max(seg1.frame_rate, seg2.frame_rate)
        seg1 = seg1.set_frame_rate(frame_rate)
        seg2 = seg2.set_frame_rate(frame_rate)
        
        assert(len(seg1) == s1_len)
        assert(len(seg2) == s2_len)
        
        return seg1, seg2
    
    
    def _parse_position(self, val):
        if val < 0:
            val = len(self) - abs(val)
        val = self.frame_count(ms=len(self)) if val == float("inf") else self.frame_count(ms=val)
        return int(val)
    
    @classmethod
    def from_file(cls, file, format):
        file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False)
        format = AUDIO_FILE_EXT_ALIASES.get(format, format)
                
        input = NamedTemporaryFile(mode='wb')
        input.write(file.read())
        input.flush()
        
        output = NamedTemporaryFile(mode="rb")
        
        ffmpeg_call = ['ffmpeg', 
                       '-y', # always overwrite existing files
                       ]
        if format: ffmpeg_call += ["-f", format]
        ffmpeg_call += [
                        "-i", input.name, # input options (filename last)
                        "-f", "wav", # output options (filename last) 
                        output.name
                        ]
        
        subprocess.call(ffmpeg_call, 
                        stderr=open(os.devnull)
                        )
        input.close()
        return cls.from_wav(output)
    
    
    @classmethod
    def from_mp3(cls, file):
        return cls.from_file(file, 'mp3')
    
    
    @classmethod<commit_msg>fix a bug that was causing files with a blank video stream as the first channel to fail (flv files mainly)<commit_after>import os
import subprocess
from tempfile import TemporaryFile, NamedTemporaryFile
from StringIO import StringIO
import wave
import audioop

from .utils import _fd_or_path_or_tempfile, db_to_float
from .exceptions import UnsupportedOuputFormat, TooManyMissingFrames, InvalidDuration


AUDIO_FILE_EXT_ALIASES = {
    "m4a": "mp4"
}

class AudioSegment(object):
    """
    Note: AudioSegment objects are immutable
    
    slicable using milliseconds. for example: Get the first second of an mp3...
    
    a = AudioSegment.from_mp3(mp3file)
    first_second = a[:1000]
    """
    
    def __init__(self, data=None, *args, **kwargs):
        if kwargs.get('metadata', False):
            # internal use only
            self._data = data
            for attr, val in kwargs.pop('metadata').items():
                setattr(self, attr, val)
        else:
            # normal construction
            data = data if isinstance(data, basestring) else data.read()
            
            raw = wave.open(StringIO(data), 'rb')
            
            raw.rewind()
            self.channels = raw.getnchannels()
            self.sample_width = raw.getsampwidth() 
            self.frame_rate = raw.getframerate()
            self.frame_width = self.channels * self.sample_width
            
            
            raw.rewind()
            self._data = raw.readframes(float('inf'))
        
        super(AudioSegment, self).__init__(*args, **kwargs)
            
    
    def __len__(self):
        """
        returns the length of this audio segment in milliseconds
        """
        return round(1000 * (self.frame_count() / self.frame_rate))
    
    
    def __iter__(self):
        return (self[i] for i in xrange(len(self)))
    
    def __getitem__(self, millisecond):
        if isinstance(millisecond, slice):
            start = millisecond.start if millisecond.start is not None else 0
            end = millisecond.stop if millisecond.stop is not None else len(self)
            
            start = min(start, len(self))
            end = min(end, len(self))
        else:
            start = millisecond
            end = millisecond + 1
        
        start = self._parse_position(start) * self.frame_width
        end = self._parse_position(end) * self.frame_width
        data = self._data[start:end]
        
        # ensure the output is as long as the requester is expecting
        expected_length = end - start
        missing_frames = (expected_length - len(data)) / self.frame_width
        if missing_frames:
            if missing_frames > self.frame_count(ms=2):
                raise TooManyMissingFrames("You should never be filling in more than 2 ms with silence here, missing frames: %s" % missing_frames)
            silence = audioop.mul(data[:self.frame_width], self.sample_width, 0)
            data += (silence * missing_frames)
        
        return self._spawn(data)


    def __add__(self, arg):
        if isinstance(arg, AudioSegment):
            return self.append(arg, crossfade=0)
        else:
            return self.apply_gain(arg)
        
        
    def __sub__(self, arg):
        if isinstance(arg, AudioSegment):
            raise TypeError("AudioSegment objects can't be subtracted from each other")
        else:
            return self.apply_gain(-arg)
        
    
    def __mul__(self, arg):
        """
        If the argument is an AudioSegment, overlay the multiplied audio 
        segment.
        
        
        If it's a number, just use the string multiply operation to repeat the 
        audio so the following would return an AudioSegment that contains the 
        audio of audio_seg eight times
        
        audio_seg * 8
        """
        if isinstance(arg, AudioSegment):
            return self.overlay(arg, position=0, loop=True)
        else:
            return self._spawn(data=self._data*arg)
    
    
    def _spawn(self, data, overrides={}):
        """
        Creates a new audio segment using the meta data from the current one
        and the data passed in. Should be used whenever an AudioSegment is 
        being returned by an operation that alters the current one, since
        AudioSegment objects are immutable.
        """
        # accept lists of data chunks 
        if isinstance(data, list):
            data = ''.join(data)
            
        # accept file-like objects
        if hasattr(data, 'read'):
            if hasattr(data, 'seek'): 
                data.seek(0)
            data = data.read()
            
        metadata = {
                    'sample_width': self.sample_width,
                    'frame_rate': self.frame_rate,
                    'frame_width': self.frame_width,
                    'channels': self.channels
                    }
        metadata.update(overrides)
        return AudioSegment(data=data, metadata=metadata)
    
    
    @classmethod
    def _sync(cls, seg1, seg2):
        s1_len, s2_len = len(seg1), len(seg2)
        
        channels = max(seg1.channels, seg2.channels)
        seg1 = seg1.set_channels(channels)
        seg2 = seg2.set_channels(channels)
        
        frame_rate = max(seg1.frame_rate, seg2.frame_rate)
        seg1 = seg1.set_frame_rate(frame_rate)
        seg2 = seg2.set_frame_rate(frame_rate)
        
        assert(len(seg1) == s1_len)
        assert(len(seg2) == s2_len)
        
        return seg1, seg2
    
    
    def _parse_position(self, val):
        if val < 0:
            val = len(self) - abs(val)
        val = self.frame_count(ms=len(self)) if val == float("inf") else self.frame_count(ms=val)
        return int(val)
    
    @classmethod
    def from_file(cls, file, format):
        file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False)
        format = AUDIO_FILE_EXT_ALIASES.get(format, format)
                
        input = NamedTemporaryFile(mode='wb')
        input.write(file.read())
        input.flush()
        
        output = NamedTemporaryFile(mode="rb")
        
        ffmpeg_call = ['ffmpeg', 
                       '-y', # always overwrite existing files
                       ]
        if format: ffmpeg_call += ["-f", format]
        ffmpeg_call += [
                        "-i", input.name, # input options (filename last)
                        "-vn", # Drop any video streams if there are any
                        
                        "-f", "wav", # output options (filename last) 
                        output.name
                        ]
        
        subprocess.call(ffmpeg_call, 
                        stderr=open(os.devnull)
                        )
        input.close()
        return cls.from_wav(output)
    
    
    @classmethod
    def from_mp3(cls, file):
        return cls.from_file(file, 'mp3')
    
    
    @classmethod
{'fraction_non_alphanumeric': 0.08871498573831639, 'fraction_numerical': 0.007167410224530096, 'mean_word_length': 1.7629824206910487, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7449837', 'n_tokens_mistral': 3865, 'n_tokens_neox': 3766, 'n_words': 1008}