Пример #1
0
	def test_that_get_lyrics_works(self):
		"""
		Test that get_lyrics function works
		"""
		self.assertEqual(get_lyrics("Faded", "Alan Walker")[:9], "[Verse 1]")
		self.assertEqual(get_lyrics("Radioactive", "Imagine Dragons")[:7], "[Intro]")
		self.assertEqual(get_lyrics("Battle Symphony", "Linkin Park")[:9], "[Verse 1]")
    def test_database_for_unsupported_song(self):
        """
		test that the database set on pythonanywhere is working and giving strippers for unsupported songs
		"""
        self.assertEqual(
            get_lyrics("Bitch Lasagna", "Party in Backyard")[:7], "[Intro]")
        self.assertEqual(
            get_lyrics("Let Me Hold You (Turn Me On)", "Cheat Codes")[:9],
            "[Verse 1]")
    def test_that_get_lyrics_does_not_break_with_wrong_data(self, fake_get):
        """
		Test that get_lyrics function does not break with wrong data
		"""
        fake_resp = requests.Response()
        fake_resp.status_code = 404
        fake_get.return_value = fake_resp
        self.assertEqual(get_lyrics("xyzzy", "Yeet"), None)
        self.assertEqual(get_lyrics("aifone", "Muhmello"), None)
        self.assertEqual(get_lyrics("Pixel2XL", "Goog-el"), None)
Пример #4
0
	def test_that_get_lyrics_does_not_break_with_wrong_data(self):
		"""
		Test that get_lyrics function does not break with wrong data
		"""
		self.assertEqual(get_lyrics(
			"Battle Symphony", "One Direction", False), "Couldn't get lyrics for Battle Symphony by One Direction.\n")
		self.assertEqual(get_lyrics("Faded", "Muhmello", False), "Couldn't get lyrics for Faded by Muhmello.\n")
		self.assertEqual(get_lyrics("Battle Symphony", "Drake", False), "Couldn't get lyrics for Battle Symphony by Drake.\n")

		# Deleting above songs and artists from unsupported.txt
		with open("unsupported.txt", "r") as f:
			lines = f.readlines()
		with open("unsupported.txt", "w") as f:
			for line in lines:
				if line not in [" Battle Symphony by One Direction \n", " Faded by Muhmello \n", " Battle Symphony by Drake \n"]:
					f.write(line)
Пример #5
0
	def test_that_get_lyrics_does_not_break_with_wrong_data(self):
		"""
		Test that get_lyrics function does not break with wrong data
		"""
		self.assertEqual(get_lyrics(
			"xyzzy", "Yeet", False), "Couldn't get lyrics for xyzzy by Yeet.\n")
		self.assertEqual(get_lyrics("wiuegi", "Muhmello", False), "Couldn't get lyrics for wiuegi by Muhmello.\n")
		self.assertEqual(get_lyrics("Pixel2XL", "Elgoog", False), "Couldn't get lyrics for Pixel2XL by Elgoog.\n")

		# Deleting above songs and artists from unsupported.txt
		with open("unsupported.txt", "r") as f:
			lines = f.readlines()
		with open("unsupported.txt", "w") as f:
			for line in lines:
				if line not in ["xyzzy by Yeet \n", "wiuegi by Muhmello \n", "Pixel2XL by Elgoog \n"]:
					f.write(line)
 def get_lyrics(song, artist):
     """
     Fetches lyrics using the swaglyrics library
     """
     lyrics = swaglyrics.get_lyrics(song, artist)
     if not lyrics:
         raise LyricsNotFound(f"Lyrics for {song} by {artist} not found on Genius.")
     return lyrics
    def test_that_get_lyrics_works(self):
        """
		Test that get_lyrics function works
		"""
        self.assertEqual(get_lyrics('果てるまで', 'ハゼ馳せる'),
                         None)  # song and artist non-latin
        self.assertEqual(get_lyrics('Hello', 'ハゼ馳せる'),
                         None)  # artist non-latin
        self.assertEqual(get_lyrics('ハゼ馳せる果てるまで', 'ZUTOMAYO'),
                         None)  # song non-latin
        self.assertEqual(get_lyrics("Faded", "Alan Walker")[:9], "[Verse 1]")
        self.assertEqual(
            get_lyrics("Radioactive", "Imagine Dragons")[:7], "[Intro]")
        self.assertEqual(
            get_lyrics("Battle Symphony", "Linkin Park")[:9], "[Verse 1]")
Пример #8
0
    def process(self, statement, additional_response_selection_parameters=None):
        if (
            self.normalized[0] == "get"
            and self.normalized[1] == "lyrics"
            and self.normalized[2] == "for"
        ):
            self.normalized[0:3] = []

        elif self.normalized[0] == "lyrics" and self.normalized[1] == "for":
            self.normalized[0:2] = []
        elif self.normalized[0] == "lyrics":
            self.normalized[0:1] = []

        stripped_message = " ".join(self.normalized).strip()
        try:
            song, artist = stripped_message.split("$ by")
        except Exception as e:
            selected_statement = SugaroidStatement(
                "Usage: _Hello $by Adele_ or " "_get lyrics for The Nights $by Avicii_",
                chatbot=True,
            )
            selected_statement.confidence = 1

            emotion = Emotion.lol
            selected_statement.emotion = emotion
            return selected_statement

        try:
            lyrics = get_lyrics(song, artist)
            if not lyrics or not lyrics.strip():
                raise LyricsNotFound

        except LyricsNotFound:
            lyrics = "I couldn't find the lyrics for '{}' by '{}'.".format(song, artist)

        selected_statement = SugaroidStatement(lyrics, chatbot=True)
        selected_statement.confidence = 1

        emotion = Emotion.lol
        selected_statement.emotion = emotion
        return selected_statement
Пример #9
0
	def test_that_lyrics_works_for_unsupported_songs(self):
		"""
		Test that lyrics function gives 'unsupported' message to unsupported files
		"""
		get_lyrics("Hello", "World", False)
		self.assertEqual(lyrics("Hello", "World"), "Lyrics unavailable for Hello by World.\n")
		get_lyrics("Foo", "Bar", False)
		self.assertEqual(lyrics("Foo", "Bar"), "Lyrics unavailable for Foo by Bar.\n")
		get_lyrics("Fantastic", "Beasts", False)
		self.assertEqual(lyrics("Fantastic", "Beasts"), "Lyrics unavailable for Fantastic by Beasts.\n")

		# Deleting above songs and artists from unsupported.txt
		with open("unsupported.txt", "r") as f:
			lines = f.readlines()
		with open("unsupported.txt", "w") as f:
			for line in lines:
				if line not in [" Hello by World \n", " Foo by Bar \n", " Fantastic by Beasts \n"]:
					f.write(line)
Пример #10
0
	def test_that_lyrics_works_for_unsupported_songs(self):
		"""
		Test that lyrics function gives 'unsupported' message to unsupported files
		"""
		get_lyrics("xyzzy", "Yeet", False)
		self.assertEqual(lyrics("xyzzy", "Yeet"), "Lyrics unavailable for xyzzy by Yeet.\n")
		get_lyrics("wiuegi", "Muhmello", False)
		self.assertEqual(lyrics("wiuegi", "Muhmello"), "Lyrics unavailable for wiuegi by Muhmello.\n")
		get_lyrics("Pixel2XL", "Elgoog", False)
		self.assertEqual(lyrics("Pixel2XL", "Elgoog"), "Lyrics unavailable for Pixel2XL by Elgoog.\n")

		# Deleting above songs and artists from unsupported.txt
		with open("unsupported.txt", "r") as f:
			lines = f.readlines()
		with open("unsupported.txt", "w") as f:
			for line in lines:
				if line not in ["xyzzy by Yeet \n", "wiuegi by Muhmello \n", "Pixel2XL by Elgoog \n"]:
					f.write(line)
Пример #11
0
	def test_that_get_lyrics_does_not_break_with_request_giving_wrong_status_code(self, mock_requests):
		"""
		Test the get_lyrics does not break with requests giving wrong status code
		"""
		self.assertEqual(get_lyrics("Ki", "Ki", True), "Couldn\'t get lyrics for Ki by Ki.\n")
Пример #12
0
	def test_that_get_lyrics_calls_requests(self, mock_requests):
		"""
		Test that get_lyrics calls requests
		"""
		self.assertEqual(get_lyrics(
			"Pixel2XL", "Elgoog", True), "Couldn't get lyrics for Pixel2XL by Elgoog.\nPhone is dope")
Пример #13
0
 def test_that_wrong_song_or_artist_does_not_break_stuff(self):
     self.assertEqual(get_lyrics('Get Schwifty', 'lol'),
                      'Couldn\'t get lyrics for Get Schwifty by lol.')
     self.assertFalse(
         get_lyrics('Get Schwifty', 'Rick Sanchez') ==
         'Couldn\'t get lyrics for Get Schwifty by Rick Sanchez.')
Пример #14
0
	def test_that_get_lyrics_calls_requests(self, mock_requests):
		"""
		Test that get lyrics calls requests
		"""
		self.assertEqual(get_lyrics("River", "Dale", True), "Couldn't get lyrics for River by Dale.\nSeason 3 is supernatural")
Пример #15
0
	def test_that_get_lyrics_do_not_break_with_error_in_request(self, mock_requests):
		"""
		Test the get_lyrics does not break with error in requests
		"""
		self.assertEqual(get_lyrics("Ki", "Ki", True), "Couldn\'t get lyrics for Ki by Ki.\n")
Пример #16
0
	def test_database_for_unsupported_song(self):
		"""
		test that the database set on pythonanywhere is working and giving strippers for unsupported songs
		"""
		self.assertEqual(get_lyrics("Bitch Lasagna", "Party in Backyard")[:7], "[Intro]")
Пример #17
0
def line_align(songs,
               dump_dir,
               boundary_algorithm='olda',
               label_algorithm='fmc2d',
               do_twinnet=False):
    """
    Aligns given audio with lyrics by line. If dump_dir is None, no timestamp
    yml is created.

    :param songs: Song metadata in dict with keys 'song', 'artist', 'path' and \
                  'genre'. Key 'path' is audio file path. Key 'genre' optional.
    :type songs: list[dict{}] | dict{}
    :param dump_dir: Directory to store timestamp ymls.
    :type dump_dir: file-like | None
    :param boundary_algorithm: Segmentation algorithm for MSAF.
    :type boundary_algorithm: str
    :param label_algorithm: Labelling algorithm for MSAF.
    :type label_algorithm: str
    :param do_twinnet: Flag for performing vocal isolation.
    :type do_twinnet: bool
    :return align_data: List of alignment data. See below for formatting.
    :rtype: list[dict{}]
    """

    logging.info('Beginning alignment...')

    if isinstance(songs, dict):
        songs = [songs]

    # Module initializations
    snd = SND(silencedb=-15)
    sc = SyllableCounter()

    # Perform MaD TwinNet in one batch
    if do_twinnet:
        paths = [song['path'] for song in songs]
        twinnet.twinnet_process(paths)
    else:
        #logging.info('Skipping MaD TwinNet')
        print('Performing source separation using spleeter..')
        audio_path = songs[0]['path']
        destination = os.path.splitext(audio_path)[0]
        if not os.path.exists(destination):
            separator = Separator('spleeter:2stems')
            separator.separate_to_file(audio_descriptor=audio_path,
                                       destination=destination)

    total_align_data = []

    for song in songs:

        logging.info('Processing {} by {}'.format(song['song'],
                                                  song['artist']))

        start_time = time.time()

        # Get file names
        mixed_path = song['path']
        voice_path = os.path.splitext(song['path'])[0] + '_voice.wav'
        if not do_twinnet:
            voice_path = os.path.join(destination, 'vocals.wav')

        # Get lyrics from Genius
        lyrics = get_lyrics(song['song'], song['artist'])

        # Get syllable count from lyrics
        formatted_lyrics = sc.build_lyrics(lyrics)
        syl_lyrics = sc.get_syllable_count_lyrics(formatted_lyrics)
        sc_syllables = sc.get_syllable_count_per_section(syl_lyrics)

        # Get syllable count from SND
        snd_syllables = snd.run(voice_path)

        # Structural segmentation analysis on original audio
        sections, labels = msaf.process(mixed_path,
                                        boundaries_id=boundary_algorithm,
                                        labels_id=label_algorithm)

        # Save instrumental section indices
        instrumentals = []

        # Get SND counts, densities per label
        max_count = 0

        labels_density = {}
        i_s = 0
        for i, section in enumerate(zip(labels, sections[:-1], sections[1:])):
            count = 0
            while i_s < len(snd_syllables) and snd_syllables[i_s] < section[2]:
                count += 1
                i_s += 1
            max_count = max(max_count, count)

            duration = section[2] - section[1]
            density = count / duration

            # TODO: Improve instrumental categorization
            if density < 0.4:
                instrumentals.append(i)
            else:
                if section[0] not in labels_density:
                    labels_density[section[0]] = [[], []]
                labels_density[section[0]][0].append(count)
                labels_density[section[0]][1].append(density)
            # if section[0] not in labels_density:
            #     labels_density[section[0]] = [[], []]
            # labels_density[section[0]][0].append(count)
            # labels_density[section[0]][1].append(density)

        # Normalize SND syllable counts
        for label in labels_density:
            labels_density[label][0] = [
                count / max_count for count in labels_density[label][0]
            ]

        # Normalize SSA syllable counts
        gt_max_syl = max(section[1] for section in sc_syllables)
        gt_chorus_syl = mean(section[1] / gt_max_syl
                             for section in sc_syllables
                             if section[0] == 'chorus')

        # Find label most similar to chorus
        min_label = labels[0]
        min_distance = float('inf')
        for label in labels_density:
            if len(labels_density[label][0]) < 2:
                continue

            # TODO: Fix distance scales
            mean_syl = mean(labels_density[label][0])
            std_den = stdev(labels_density[label][1])
            distance = sqrt(((mean_syl - gt_chorus_syl) / gt_chorus_syl)**2 +
                            std_den**2)

            if distance < min_distance:
                min_distance = distance
                min_label = label

        # Relabel
        relabels = [''] * len(labels)

        temp = defaultdict(list)
        for i, label in enumerate(labels):
            temp[label].append(i)
        for label in temp:
            for i in temp[label]:
                if i in instrumentals:
                    continue
                elif label == min_label:
                    relabels[i] = 'chorus'
                elif len(temp[label]) > 1:
                    relabels[i] = 'verse'
                else:
                    relabels[i] = 'other'
        del temp

        relabels = [label for label in relabels if label]

        if not relabels:
            logging.error('Whole song tagged as instrumental! Skipping...')
            continue

        # Calculate accumulated error matrix
        dp = [[-1 for j in range(len(relabels))]
              for i in range(len(sc_syllables))]
        for i in range(len(sc_syllables)):
            for j in range(len(relabels)):
                dp[i][j] = dp_err_matrix[sc_syllables[i][0]][relabels[j]]
                if i == 0 and j == 0:
                    pass
                elif i == 0:
                    dp[i][j] += dp[i][j - 1]
                elif j == 0:
                    dp[i][j] += dp[i - 1][j]
                else:
                    dp[i][j] += min(dp[i - 1][j], dp[i][j - 1],
                                    dp[i - 1][j - 1])

        # Backtrack
        i, j = len(sc_syllables) - 1, len(relabels) - 1
        path = []
        while True:
            path.append((i, j))
            if (i, j) == (0, 0):
                break
            elif i == 0:
                j -= 1
            elif j == 0:
                i -= 1
            else:
                min_dir = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
                if dp[i - 1][j] == min_dir:
                    i -= 1
                elif dp[i][j - 1] == min_dir:
                    j -= 1
                else:
                    i -= 1
                    j -= 1
        path.reverse()

        # Process alignment and write to file
        alignment = [[] for i in range(len(labels))]
        for i in instrumentals:
            alignment[i].append('instrumental')

        section_id = 0
        j_prev = 0
        for (i, j) in path:
            if j != j_prev:
                section_id += 1
                j_prev = j
            while 'instrumental' in alignment[section_id]:
                section_id += 1
            alignment[section_id].append(i)

        end_time = time.time()

        align_data = {
            'song': song['song'],
            'artist': song['artist'],
            'process time': end_time - start_time,
            'duration': round((sections[-1] - sections[0]).item(), 2),
            'align': []
        }

        if 'genre' in song:
            align_data['genre'] = song['genre']

        cur_lyric_section = -1
        for i, section in enumerate(alignment):
            for n, lyric_section in enumerate(section):
                if lyric_section != cur_lyric_section:
                    break_point = round((
                        sections[i] + n *
                        (sections[i + 1] - sections[i]) / len(section)).item(),
                                        2)
                    if cur_lyric_section != 'instrumental' and align_data[
                            'align']:
                        align_data['align'][-1]['end'] = break_point
                    if lyric_section != 'instrumental':
                        align_data['align'].append({
                            'label':
                            sc_syllables[lyric_section][0],
                            'syllables':
                            sc_syllables[lyric_section][1],
                            'start':
                            break_point,
                            'lines': []
                        })
                    cur_lyric_section = lyric_section

        if 'end' not in align_data['align'][-1]:
            align_data['align'][-1]['end'] = break_point

        for i, section in enumerate(align_data['align']):
            duration = section['end'] - section['start']
            line_start = section['start']
            for j, line in enumerate(formatted_lyrics[i][1]):
                line_text = ' '.join(line)
                line_syls = sum(syl_lyrics[i][1][j])
                line_duration = line_syls / align_data['align'][i][
                    'syllables'] * duration

                align_data['align'][i]['lines'].append({
                    'end': line_start + line_duration,
                    'text': line_text
                })

                line_start += line_duration

        if dump_dir is not None:
            file_name = '{}_{}.yml'.format(song['artist'],
                                           song['song']).replace(' ', '')
            file_path = os.path.join(dump_dir, file_name)

            with open(file_path, 'w') as f:
                yaml.dump(align_data, f, default_flow_style=False)

        total_align_data.append(align_data)

    return total_align_data