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)
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
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
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)
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)