def save(self, *args, **kwargs):
        """Overriden save method to automatically convert the audio to an mp3"""
        duration = kwargs.pop('duration', None)
        needs_conversion = self.audio and not self.audio.name.lower().endswith(
            '.mp3')

        # If we have an audio file and it's not an mp3, make it one
        if duration or needs_conversion:
            if not os.path.exists(self.audio.path):
                # If it doesn't already exist, save the old audio first so that we can re-encode it
                # This is needed if it's newly uploaded
                self.audio.save(self.audio.name, File(self.audio), save=False)

            audio_helper = AudioHelper()

            if needs_conversion:
                mp3_filename = audio_helper.make_mp3(self.audio.path)
                mp3_file = open(mp3_filename, 'rb')
                self.audio.save(os.path.basename(mp3_file.name),
                                File(mp3_file),
                                save=False)

            if duration:
                self.audio_duration = audio_helper.get_audio_duration(
                    self.audio.path)

        # Call the original model save to do everything
        super(AudioMP3Mixin, self).save(*args, **kwargs)
Exemple #2
0
 def forwards(self, orm):
     for recording in orm['speeches.Recording'].objects.all():
         timestamps = recording.timestamps
         if timestamps.count():
             recording.start_datetime = timestamps.all()[0].timestamp
         if recording.audio:
             audio_helper = AudioHelper()
             duration = audio_helper.get_audio_duration(recording.audio.path)
             recording.audio_duration = duration
         recording.save()
    def create_or_update_speeches(self, instance):
        created_speeches = []

        # Split the recording's audio files
        audio_helper = AudioHelper()
        audio_files = audio_helper.split_recording(self)
        sorted_timestamps = self.timestamps.order_by("timestamp")

        for index, audio_file in enumerate(audio_files):
            new = True
            speech = Speech(
                instance=instance,
                public=False,
                type='speech',
            )
            timestamp = None
            if sorted_timestamps and len(sorted_timestamps) > 0:
                # We assume that the files are returned in order of timestamp
                timestamp = sorted_timestamps[index]
                if timestamp.speech:
                    speech = timestamp.speech
                    new = False
                    try:
                        speech.audio.delete(save=False)
                    except:
                        pass
                        # shouldn't happen, but we're going to recreate anyway
                        # so not critical

                speech.speaker = timestamp.speaker
                speech.start_date = timestamp.timestamp.date()
                speech.start_time = timestamp.timestamp.time()
                # If there's another one we can work out the end too
                if index < len(sorted_timestamps) - 1:
                    next_timestamp = sorted_timestamps[index + 1]
                    speech.end_date = next_timestamp.timestamp.date()
                    speech.end_time = next_timestamp.timestamp.time()

            audio_file = open(audio_file, 'rb')
            speech.audio = File(audio_file,
                                name=os.path.basename(audio_file.name))
            speech.save()

            if new:
                created_speeches.append(speech)
                if timestamp:
                    timestamp.speech = speech
                    timestamp.save()

        return created_speeches
Exemple #4
0
    def create_or_update_speeches(self, instance):
        created_speeches = []

        # Split the recording's audio files
        audio_helper = AudioHelper()
        audio_files = audio_helper.split_recording(self)
        sorted_timestamps = self.timestamps.order_by("timestamp")

        for index, audio_file in enumerate(audio_files):
            new = True
            speech = Speech(
                instance=instance,
                public=False,
                type='speech',
            )
            timestamp = None
            if sorted_timestamps and len(sorted_timestamps) > 0:
                # We assume that the files are returned in order of timestamp
                timestamp = sorted_timestamps[index]
                if timestamp.speech:
                    speech = timestamp.speech
                    new = False
                    try:
                        speech.audio.delete(save=False)
                    except:
                        pass
                        # shouldn't happen, but we're going to recreate anyway
                        # so not critical

                speech.speaker = timestamp.speaker
                speech.start_date = timestamp.timestamp.date()
                speech.start_time = timestamp.timestamp.time()
                # If there's another one we can work out the end too
                if index < len(sorted_timestamps) - 1:
                    next_timestamp = sorted_timestamps[index + 1]
                    speech.end_date = next_timestamp.timestamp.date()
                    speech.end_time = next_timestamp.timestamp.time()

            audio_file = open(audio_file, 'rb')
            speech.audio = File(audio_file)
            speech.save()

            if new:
                created_speeches.append(speech)
                if timestamp:
                    timestamp.speech = speech
                    timestamp.save()

        return created_speeches
Exemple #5
0
    def save(self, *args, **kwargs):
        """Overriden save method to automatically convert the audio to an mp3"""
        duration = kwargs.pop('duration', None)
        needs_conversion = self.audio and not self.audio.name.lower().endswith('.mp3')

        # If we have an audio file and it's not an mp3, make it one
        if duration or needs_conversion:
            if not os.path.exists(self.audio.path):
                # If it doesn't already exist, save the old audio first so that we can re-encode it
                # This is needed if it's newly uploaded
                self.audio.save(self.audio.name, File(self.audio), save=False)

            audio_helper = AudioHelper()

            if needs_conversion:
                mp3_filename = audio_helper.make_mp3(self.audio.path)
                mp3_file = open(mp3_filename, 'rb')
                self.audio.save(mp3_file.name, File(mp3_file), save=False)

            if duration:
                self.audio_duration = audio_helper.get_audio_duration(self.audio.path)

        # Call the original model save to do everything
        super(AudioMP3Mixin, self).save(*args, **kwargs)
 def setUp(self):
     super(AudioHelperTests, self).setUp()
     self.helper = AudioHelper()
     self.tmp_filename = None
     self.remove_tmp_filename = False
class AudioHelperTests(InstanceTestCase):
    @classmethod
    def setUpClass(cls):
        cls._in_fixtures = os.path.join(os.path.abspath(speeches.__path__[0]), "fixtures", "test_inputs")
        cls._expected_fixtures = os.path.join(os.path.abspath(speeches.__path__[0]), "fixtures", "expected_outputs")
        super(AudioHelperTests, cls).setUpClass()

    def setUp(self):
        super(AudioHelperTests, self).setUp()
        self.helper = AudioHelper()
        self.tmp_filename = None
        self.remove_tmp_filename = False

    def tearDown(self):
        if self.tmp_filename is not None and self.remove_tmp_filename:
            os.remove(self.tmp_filename)

    def expected_output_file(self, filename):
        _, extension = os.path.splitext(filename)
        return os.path.join(self._expected_fixtures, extension[1:], filename)

    def assertSameAudioLength(self, filename_a, filename_b):
        files = (filename_a, filename_b)
        # Use FFMPEG directly as CoreAudio returns incorrect durations for the generated MP3s
        af1 = audioread.ffdec.FFmpegAudioFile(filename_a)
        af2 = audioread.ffdec.FFmpegAudioFile(filename_b)
        message = "The audio files %s (%.3fs) and %s (%.3fs) were of different lengths"
        message = message % (files[0], af1.duration, files[1], af2.duration)
        # Make sure the lengths are within 0.2s of each other - this is
        # intended to be imprecise enough to ignore any differences in
        # frame padding in the MP3:
        self.assertAlmostEqual(af1.duration, af2.duration, delta=0.2, msg=message)

    def convert(self, known_input, method, expected_output):
        self.tmp_filename = getattr(self.helper, method)(os.path.join(self._in_fixtures, known_input))
        expected = self.expected_output_file(expected_output)
        # Check that the type of the file is exactly the same first of all:
        self.assertEqual(file_info(self.tmp_filename), file_info(expected))
        # Now check that the files are identical:
        self.assertSameAudioLength(self.tmp_filename, expected)

    def test_wav_file_creation_from_mp3(self):
        self.convert("lamb.mp3", "make_wav", "lamb_from_mp3.wav")

    def test_wav_file_creation_from_android_3gp(self):
        self.convert("lamb.3gp", "make_wav", "lamb_from_3gp.wav")

    def test_wav_file_creation_from_iphone_wav(self):
        self.convert("lamb_iphone.wav", "make_wav", "lamb_from_iphone.wav")

    def test_wav_file_creation_from_stereo_wav(self):
        self.convert("lamb_stereo.wav", "make_wav", "lamb_from_stereo.wav")

    def test_mp3_file_creation_from_stereo_wav(self):
        self.convert("lamb_stereo.wav", "make_mp3", "lamb_mp3_from_stereo.mp3")

    def test_mp3_file_creation_from_iphone_wav(self):
        self.convert("lamb_iphone.wav", "make_mp3", "lamb_mp3_from_iphone.mp3")

    def test_mp3_file_creation_from_android_3gp(self):
        self.convert("lamb.3gp", "make_mp3", "lamb_mp3_from_3gp.mp3")

    def test_recording_splitting_no_timestamps(self):
        audio = open(os.path.join(self._in_fixtures, "lamb.mp3"), "rb")
        recording = Recording.objects.create(audio=File(audio, "lamb.mp3"), instance=self.instance)

        files_created = self.helper.split_recording(recording)

        self.assertEqual(len(files_created), 1)
        self.assertSameAudioLength(files_created[0], self.expected_output_file("lamb_whole.mp3"))

    def test_recording_splitting_one_timestamp(self):
        speaker = Speaker.objects.create(name="Steve", instance=self.instance)
        timestamp = RecordingTimestamp.objects.create(speaker=speaker, timestamp=timezone.now(), instance=self.instance)
        audio = open(os.path.join(self._in_fixtures, "lamb.mp3"), "rb")
        recording = Recording.objects.create(
            audio=File(audio, "lamb.mp3"), instance=self.instance, start_datetime=timestamp.timestamp
        )
        recording.timestamps.add(timestamp)
        recording.save()

        files_created = self.helper.split_recording(recording)

        self.assertEqual(len(files_created), 1)
        self.assertSameAudioLength(files_created[0], self.expected_output_file("lamb_whole.mp3"))

    def test_recording_splitting_several_timestamps(self):
        speakers = []
        for i in range(3):
            speakers.append(Speaker.objects.create(name="Steve %d" % i, instance=self.instance))

        start = timezone.now()
        timestamps = [0, 3, 4]
        for i in range(3):
            start_i = start + timedelta(seconds=timestamps[i])
            timestamps[i] = RecordingTimestamp.objects.create(
                speaker=speakers[i], timestamp=start_i, instance=self.instance
            )

        audio = open(os.path.join(self._in_fixtures, "lamb.mp3"), "rb")
        recording = Recording.objects.create(
            audio=File(audio, "lamb.mp3"), instance=self.instance, start_datetime=timestamps[0].timestamp
        )
        for i in range(3):
            recording.timestamps.add(timestamps[i])
        recording.save()

        files_created = self.helper.split_recording(recording)

        self.assertEqual(len(files_created), 3)
        files = [
            "lamb_first_three_seconds.mp3",
            "lamb_from_three_to_four_seconds.mp3",
            "lamb_from_four_seconds_onwards.mp3",
        ]
        for i in range(3):
            self.assertSameAudioLength(files_created[i], self.expected_output_file(files[i]))

    def test_audio_length(self):
        audio_path = os.path.join(self._in_fixtures, "lamb.mp3")
        duration = self.helper.get_audio_duration(audio_path)
        self.assertEqual(duration, 5)
 def check_audio_durations(recording, durations):
     audio_helper = AudioHelper()
     for (rt, d) in zip(recording.timestamps.all(), durations):
         self.assertEqual(
             audio_helper.get_audio_duration(rt.speech.audio.path), d)
 def check_audio_durations(recording, durations):
     audio_helper = AudioHelper()
     for (rt, d) in zip(recording.timestamps.all(), durations):
         self.assertEqual(
             audio_helper.get_audio_duration(rt.speech.audio.path),
             d)
 def setUp(self):
     super(AudioHelperTests, self).setUp()
     self.helper = AudioHelper()
     self.tmp_filename = None
     self.remove_tmp_filename = False
class AudioHelperTests(InstanceTestCase):
    @classmethod
    def setUpClass(cls):
        cls._in_fixtures = os.path.join(os.path.abspath(speeches.__path__[0]),
                                        'fixtures', 'test_inputs')
        cls._expected_fixtures = os.path.join(
            os.path.abspath(speeches.__path__[0]), 'fixtures',
            'expected_outputs')
        super(AudioHelperTests, cls).setUpClass()

    def setUp(self):
        super(AudioHelperTests, self).setUp()
        self.helper = AudioHelper()
        self.tmp_filename = None
        self.remove_tmp_filename = False

    def tearDown(self):
        if self.tmp_filename is not None and self.remove_tmp_filename:
            os.remove(self.tmp_filename)

    def expected_output_file(self, filename):
        _, extension = os.path.splitext(filename)
        return os.path.join(self._expected_fixtures, extension[1:], filename)

    def assertSameAudioLength(self, filename_a, filename_b):
        files = (filename_a, filename_b)
        # Use FFMPEG directly as CoreAudio returns incorrect durations for the generated MP3s
        af1 = audioread.ffdec.FFmpegAudioFile(filename_a)
        af2 = audioread.ffdec.FFmpegAudioFile(filename_b)
        message = 'The audio files %s (%.3fs) and %s (%.3fs) were of different lengths'
        message = message % (files[0], af1.duration, files[1], af2.duration)
        # Make sure the lengths are within 0.2s of each other - this is
        # intended to be imprecise enough to ignore any differences in
        # frame padding in the MP3:
        self.assertAlmostEqual(af1.duration,
                               af2.duration,
                               delta=0.2,
                               msg=message)

    def convert(self, known_input, method, expected_output):
        self.tmp_filename = getattr(self.helper, method)(os.path.join(
            self._in_fixtures, known_input))
        expected = self.expected_output_file(expected_output)
        # Check that the type of the file is exactly the same first of all:
        self.assertEqual(file_info(self.tmp_filename), file_info(expected))
        # Now check that the files are identical:
        self.assertSameAudioLength(self.tmp_filename, expected)

    def test_wav_file_creation_from_mp3(self):
        self.convert('lamb.mp3', 'make_wav', 'lamb_from_mp3.wav')

    def test_wav_file_creation_from_android_3gp(self):
        self.convert('lamb.3gp', 'make_wav', 'lamb_from_3gp.wav')

    def test_wav_file_creation_from_iphone_wav(self):
        self.convert('lamb_iphone.wav', 'make_wav', 'lamb_from_iphone.wav')

    def test_wav_file_creation_from_stereo_wav(self):
        self.convert('lamb_stereo.wav', 'make_wav', 'lamb_from_stereo.wav')

    def test_mp3_file_creation_from_stereo_wav(self):
        self.convert('lamb_stereo.wav', 'make_mp3', 'lamb_mp3_from_stereo.mp3')

    def test_mp3_file_creation_from_iphone_wav(self):
        self.convert('lamb_iphone.wav', 'make_mp3', 'lamb_mp3_from_iphone.mp3')

    def test_mp3_file_creation_from_android_3gp(self):
        self.convert('lamb.3gp', 'make_mp3', 'lamb_mp3_from_3gp.mp3')

    def test_recording_splitting_no_timestamps(self):
        audio = open(os.path.join(self._in_fixtures, 'lamb.mp3'), 'rb')
        recording = Recording.objects.create(audio=File(audio, 'lamb.mp3'),
                                             instance=self.instance)

        files_created = self.helper.split_recording(recording)

        self.assertEqual(len(files_created), 1)
        self.assertSameAudioLength(files_created[0],
                                   self.expected_output_file('lamb_whole.mp3'))

    def test_recording_splitting_one_timestamp(self):
        speaker = Speaker.objects.create(name='Steve', instance=self.instance)
        timestamp = RecordingTimestamp.objects.create(speaker=speaker,
                                                      timestamp=timezone.now(),
                                                      instance=self.instance)
        audio = open(os.path.join(self._in_fixtures, 'lamb.mp3'), 'rb')
        recording = Recording.objects.create(
            audio=File(audio, 'lamb.mp3'),
            instance=self.instance,
            start_datetime=timestamp.timestamp)
        recording.timestamps.add(timestamp)
        recording.save()

        files_created = self.helper.split_recording(recording)

        self.assertEqual(len(files_created), 1)
        self.assertSameAudioLength(files_created[0],
                                   self.expected_output_file('lamb_whole.mp3'))

    def test_recording_splitting_several_timestamps(self):
        speakers = []
        for i in range(3):
            speakers.append(
                Speaker.objects.create(name='Steve %d' % i,
                                       instance=self.instance))

        start = timezone.now()
        timestamps = [0, 3, 4]
        for i in range(3):
            start_i = start + timedelta(seconds=timestamps[i])
            timestamps[i] = RecordingTimestamp.objects.create(
                speaker=speakers[i], timestamp=start_i, instance=self.instance)

        audio = open(os.path.join(self._in_fixtures, 'lamb.mp3'), 'rb')
        recording = Recording.objects.create(
            audio=File(audio, 'lamb.mp3'),
            instance=self.instance,
            start_datetime=timestamps[0].timestamp)
        for i in range(3):
            recording.timestamps.add(timestamps[i])
        recording.save()

        files_created = self.helper.split_recording(recording)

        self.assertEqual(len(files_created), 3)
        files = [
            'lamb_first_three_seconds.mp3',
            'lamb_from_three_to_four_seconds.mp3',
            'lamb_from_four_seconds_onwards.mp3',
        ]
        for i in range(3):
            self.assertSameAudioLength(files_created[i],
                                       self.expected_output_file(files[i]))

    def test_audio_length(self):
        audio_path = os.path.join(self._in_fixtures, 'lamb.mp3')
        duration = self.helper.get_audio_duration(audio_path)
        self.assertEqual(duration, 5)