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