ohollo commited on
Commit
cdd58eb
·
1 Parent(s): 05021ce

Convert from file

Browse files
Files changed (3) hide show
  1. app.py +46 -39
  2. src/convert.py +29 -0
  3. src/neighbours.py +0 -1
app.py CHANGED
@@ -7,7 +7,7 @@ from datasets import load_dataset
7
  import json
8
  import os
9
 
10
- from src.convert import get_embeddings_from_chord_sequences
11
  from src.analysis import EmbeddingsAnalysis
12
 
13
  # Configuration
@@ -57,62 +57,70 @@ def neighbours_to_dict(neighbours_list):
57
  result.append(group_result)
58
  return result
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  def analyze_chord_sequence(chord_text):
61
  """Analyze a chord sequence from text input"""
62
  try:
63
  chords = parse_chord_input(chord_text)
64
 
65
  if not chords:
66
- return "Please enter some chords!", "", ""
67
 
68
  # Get embeddings
69
  embeddings = get_embeddings_from_chord_sequences([chords])
70
 
71
- # Get scores
72
- scores = ea.get_scores(embeddings, [len(chords)])
73
-
74
- # Get neighbours
75
- neighbours = ea.get_neighbours(embeddings, limit=10)
76
-
77
- # Format results
78
- # Simple originality score display
79
- score = scores[0]
80
- scores_text = f"**Originality Score:** {score:.4f}"
81
-
82
- # Format neighbours
83
- neighbours_text = "**Similar Songs:**\n"
84
- if neighbours and len(neighbours) > 0 and len(neighbours[0]) > 0:
85
- for i, neighbor in enumerate(neighbours[0][:5], 1): # Show top 5
86
- title = neighbor.metadata.get('title', 'Unknown')
87
- artist = neighbor.metadata.get('artist', 'Unknown')
88
- distance = neighbor.distance
89
- neighbours_text += f"{i}. {title} by {artist} (distance: {distance:.3f})\n"
90
- else:
91
- neighbours_text += "No similar songs found."
92
-
93
- return scores_text, neighbours_text
94
 
95
  except Exception as e:
96
  return f"Error: {str(e)}", ""
97
 
98
  def analyze_music_file(audio_file):
99
- """Analyze music file - placeholder for now"""
100
  if audio_file is None:
101
  return "Please upload a music file!", "", ""
102
 
103
- # This is a placeholder - you'll need to implement audio processing
104
- # For now, we'll return a message about future implementation
105
- return (
106
- "**File uploaded:** " + audio_file.name,
107
- "Audio analysis feature coming soon! Currently, this would:\n"
108
- "1. Extract chords from the audio file\n"
109
- "2. Convert them to embeddings\n"
110
- "3. Analyze originality and find similar songs",
111
- "Please use the 'Text Input' tab for now to analyze chord sequences directly."
112
- )
 
 
 
 
113
 
114
  # Create Gradio interface
115
- with gr.Blocks(title="Harmonic Analysis Tool") as app:
116
  gr.Markdown("# 🎵 Harmonic Analysis Tool")
117
  gr.Markdown("Analyze chord progressions for originality and find similar songs in the database.")
118
 
@@ -191,6 +199,5 @@ if __name__ == "__main__":
191
  server_name="0.0.0.0",
192
  server_port=7860,
193
  share=False,
194
- show_error=True,
195
- theme=gr.themes.Soft()
196
  )
 
7
  import json
8
  import os
9
 
10
+ from src.convert import get_embeddings_from_chord_sequences, get_embedding_from_filepaths
11
  from src.analysis import EmbeddingsAnalysis
12
 
13
  # Configuration
 
57
  result.append(group_result)
58
  return result
59
 
60
+ def _perform_analysis(embeddings, sequence_lengths):
61
+ """Private function to perform analysis on embeddings and format results"""
62
+ # Get scores
63
+ scores = ea.get_scores(embeddings, sequence_lengths)
64
+
65
+ # Get neighbours
66
+ neighbours = ea.get_neighbours(embeddings, limit=10)
67
+
68
+ # Format results
69
+ score = scores[0]
70
+ scores_text = f"**Originality Score:** {score:.4f}"
71
+
72
+ # Format neighbours
73
+ neighbours_text = "**Similar Songs:**\n"
74
+ if neighbours and len(neighbours) > 0 and len(neighbours[0]) > 0:
75
+ for i, neighbor in enumerate(neighbours[0][:5], 1): # Show top 5
76
+ title = neighbor.metadata.get('title', 'Unknown')
77
+ artist = neighbor.metadata.get('artist', 'Unknown')
78
+ distance = neighbor.distance
79
+ neighbours_text += f"{i}. {title} by {artist} (distance: {distance:.3f})\n"
80
+ else:
81
+ neighbours_text += "No similar songs found."
82
+
83
+ return scores_text, neighbours_text
84
+
85
  def analyze_chord_sequence(chord_text):
86
  """Analyze a chord sequence from text input"""
87
  try:
88
  chords = parse_chord_input(chord_text)
89
 
90
  if not chords:
91
+ return "Please enter some chords!", ""
92
 
93
  # Get embeddings
94
  embeddings = get_embeddings_from_chord_sequences([chords])
95
 
96
+ # Perform analysis using shared logic
97
+ return _perform_analysis(embeddings, [len(chords)])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  except Exception as e:
100
  return f"Error: {str(e)}", ""
101
 
102
  def analyze_music_file(audio_file):
103
+ """Analyze music file by extracting chords and computing embeddings"""
104
  if audio_file is None:
105
  return "Please upload a music file!", "", ""
106
 
107
+ try:
108
+ # Get embeddings from the audio file
109
+ embeddings, chord_lens = get_embedding_from_filepaths([audio_file])
110
+
111
+ # Perform analysis using shared logic
112
+ scores_text, neighbours_text = _perform_analysis(embeddings, chord_lens)
113
+
114
+ # Add file info
115
+ file_info = f"**File analyzed:** {os.path.basename(audio_file)}"
116
+
117
+ return file_info, scores_text, neighbours_text
118
+
119
+ except Exception as e:
120
+ return f"**Error processing file:** {str(e)}", "", ""
121
 
122
  # Create Gradio interface
123
+ with gr.Blocks(title="Harmonic Analysis Tool", theme=gr.themes.Soft()) as app:
124
  gr.Markdown("# 🎵 Harmonic Analysis Tool")
125
  gr.Markdown("Analyze chord progressions for originality and find similar songs in the database.")
126
 
 
199
  server_name="0.0.0.0",
200
  server_port=7860,
201
  share=False,
202
+ show_error=True
 
203
  )
src/convert.py CHANGED
@@ -3,8 +3,12 @@ from gradio_client import Client
3
  import os
4
  import json
5
 
 
 
 
6
  _CONSTANT_GAP_SECS = 2
7
  _SEQ_EMBED_SPACE = 'ohollo/chord-seq-embed'
 
8
 
9
 
10
  _client = Client(_SEQ_EMBED_SPACE)
@@ -26,3 +30,28 @@ def get_embeddings_from_chord_sequences(chord_sequences: list[list[str]], consta
26
  for chord_sequence in chord_sequences
27
  ]
28
  return np.array(_call_embedding_service(chords_w_timestamps)['embeddings'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import os
4
  import json
5
 
6
+ from chord_extractor.extractors import Chordino
7
+ from chord_extractor import clear_conversion_cache, LabelledChordSequence
8
+
9
  _CONSTANT_GAP_SECS = 2
10
  _SEQ_EMBED_SPACE = 'ohollo/chord-seq-embed'
11
+ _POST_PROCESS_CHORD_LEN_RATIO = 0.7
12
 
13
 
14
  _client = Client(_SEQ_EMBED_SPACE)
 
30
  for chord_sequence in chord_sequences
31
  ]
32
  return np.array(_call_embedding_service(chords_w_timestamps)['embeddings'])
33
+
34
+
35
+ def get_embedding_from_filepaths(file_paths: list[str]) -> tuple[np.ndarray, list[int]]:
36
+ """
37
+ Reads chord sequences from a given filepath and converts them into embeddings.
38
+
39
+ :param file_paths: List of paths to the audio files. Can be anything supported by chord-extractor - .mid, .wav, .mp3, .ogg
40
+ :return: 2-d numpy array of embeddings per chord sequence.
41
+ """
42
+ chords_w_timestamps = []
43
+ chord_lengths = []
44
+ chordino = Chordino()
45
+ for file_path in file_paths:
46
+ if not os.path.isfile(file_path):
47
+ raise FileNotFoundError(f"File not found: {file_path}")
48
+
49
+ conversion_file_path = chordino.preprocess(file_path)
50
+
51
+ chords = chordino.extract(conversion_file_path if conversion_file_path else file_path)
52
+ chords_w_timestamps.append({
53
+ 'label': [chord.chord for chord in chords],
54
+ 'timestamp': [chord.timestamp for chord in chords]
55
+ })
56
+ chord_lengths.append(int(len(chords) * _POST_PROCESS_CHORD_LEN_RATIO))
57
+ return np.array(_call_embedding_service(chords_w_timestamps)['embeddings']), chord_lengths
src/neighbours.py CHANGED
@@ -35,7 +35,6 @@ class EmbeddingClosestNeighbours:
35
  self._close_threshold = close_threshold
36
 
37
  def get(self, embeddings: np.ndarray, limit: int = None) -> list[list[Neighbour]]:
38
- # lims, D, I = self._index.range_search(embeddings, self._close_threshold)
39
  all_neighbours = []
40
  for indices_, distances_ in indices_distances_gen(embeddings, self._close_threshold, self._index):
41
  lengths_ = self._lengths[indices_]
 
35
  self._close_threshold = close_threshold
36
 
37
  def get(self, embeddings: np.ndarray, limit: int = None) -> list[list[Neighbour]]:
 
38
  all_neighbours = []
39
  for indices_, distances_ in indices_distances_gen(embeddings, self._close_threshold, self._index):
40
  lengths_ = self._lengths[indices_]