1use std::sync::mpsc;
2use std::thread;
3use std::time::Duration;
4
5use crate::config::Config;
6use crate::fingerprinting::algorithm::SignatureGenerator;
7use crate::fingerprinting::communication::{recognize_song_from_signature_with_config, recognize_song_from_signature};
8use crate::audio::recorder::AudioRecorder;
9use crate::audio::processor::AudioProcessor;
10use crate::{Result, SongRecError};
11
12pub struct SongRec {
14 config: Config,
15}
16
17#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
19pub struct RecognitionResult {
20 pub song_name: String,
21 pub artist_name: String,
22 pub album_name: Option<String>,
23 pub track_key: String,
24 pub release_year: Option<String>,
25 pub genre: Option<String>,
26 pub recognition_timestamp: chrono::DateTime<chrono::Utc>,
27 pub raw_response: serde_json::Value,
28}
29
30pub struct RecognitionStream {
32 receiver: mpsc::Receiver<Result<RecognitionResult>>,
33 _handles: Vec<thread::JoinHandle<()>>, }
35
36impl SongRec {
37 pub fn new(config: Config) -> Self {
39 Self { config }
40 }
41
42 pub fn recognize_from_file(&self, file_path: &str) -> Result<RecognitionResult> {
44 let signature = SignatureGenerator::make_signature_from_file(file_path)
46 .map_err(|e| SongRecError::FingerprintingError(e.to_string()))?;
47
48 let response = recognize_song_from_signature_with_config(&signature, &self.config)
50 .map_err(|e| SongRecError::NetworkError(e.to_string()))?;
51
52 self.parse_recognition_response(response)
54 }
55
56 pub fn recognize_from_samples(&self, samples: &[i16], sample_rate: u32) -> Result<RecognitionResult> {
58 let mut generator = SignatureGenerator::new();
60
61 for chunk in samples.chunks(128) {
63 generator.do_fft(chunk, sample_rate);
64 }
65
66 let signature = generator.get_signature();
67
68 let response = recognize_song_from_signature(&signature)
70 .map_err(|e| SongRecError::NetworkError(e.to_string()))?;
71
72 self.parse_recognition_response(response)
74 }
75
76 pub fn start_continuous_recognition(&self) -> Result<RecognitionStream> {
78 self.start_continuous_recognition_with_device(None)
79 }
80
81 pub fn start_continuous_recognition_with_device(&self, device_name: Option<String>) -> Result<RecognitionStream> {
83 let (result_tx, result_rx) = mpsc::channel();
84 let (_control_tx, control_rx) = mpsc::channel();
85
86 let config = self.config.clone();
87
88 let recorder_handle = {
90 let result_tx = result_tx.clone();
91 let config_for_thread = config.clone();
92
93 thread::spawn(move || {
94 let mut recorder = AudioRecorder::new(config_for_thread.clone());
95
96 match recorder.start_recording(device_name, control_rx) {
97 Ok(sample_rx) => {
98 let mut processor = AudioProcessor::with_config(config_for_thread.clone());
100
101 for samples in sample_rx {
102 match processor.process_samples(&samples) {
103 Ok(Some(signature)) => {
104 match recognize_song_from_signature_with_config(&signature, &config_for_thread) {
106 Ok(response) => {
107 match SongRec::parse_recognition_response_static(response) {
109 Ok(result) => {
110 if result_tx.send(Ok(result)).is_err() {
111 break; }
113 },
114 Err(e) => {
115 if result_tx.send(Err(e)).is_err() {
116 break;
117 }
118 }
119 }
120 },
121 Err(e) => {
122 let error = SongRecError::NetworkError(e.to_string());
123 if result_tx.send(Err(error)).is_err() {
124 break;
125 }
126 }
127 }
128 },
129 Ok(None) => {
130 },
132 Err(e) => {
133 let error = SongRecError::FingerprintingError(e.to_string());
134 if result_tx.send(Err(error)).is_err() {
135 break;
136 }
137 }
138 }
139 }
140 },
141 Err(e) => {
142 let error = SongRecError::AudioError(e.to_string());
143 let _ = result_tx.send(Err(error));
144 }
145 }
146 })
147 };
148
149 Ok(RecognitionStream {
150 receiver: result_rx,
151 _handles: vec![recorder_handle],
152 })
153 }
154
155 fn parse_recognition_response(&self, response: serde_json::Value) -> Result<RecognitionResult> {
157 Self::parse_recognition_response_static(response)
158 }
159
160 fn parse_recognition_response_static(response: serde_json::Value) -> Result<RecognitionResult> {
162 let matches = response.get("matches")
164 .and_then(|m| m.as_array())
165 .ok_or_else(|| SongRecError::NetworkError("Invalid response format: no matches array".to_string()))?;
166
167 if matches.is_empty() {
168 return Err(SongRecError::NetworkError("No track found in response".to_string()));
169 }
170
171 let track = response.get("track")
173 .ok_or_else(|| SongRecError::NetworkError("No track found in response".to_string()))?;
174
175 let song_name = track
177 .get("title")
178 .and_then(|v| v.as_str())
179 .unwrap_or("Unknown")
180 .to_string();
181
182 let artist_name = track
183 .get("subtitle")
184 .and_then(|v| v.as_str())
185 .unwrap_or("Unknown")
186 .to_string();
187
188 let album_name = track
189 .pointer("/sections/0/metadata/0/text")
190 .and_then(|v| v.as_str())
191 .map(|s| s.to_string());
192
193 let track_key = track
194 .get("key")
195 .and_then(|v| v.as_str())
196 .unwrap_or("")
197 .to_string();
198
199 let release_year = track
200 .pointer("/sections/0/metadata")
201 .and_then(|metadata| {
202 if let Some(metadata_array) = metadata.as_array() {
203 for item in metadata_array {
204 if let Some(title) = item.pointer("/title").and_then(|v| v.as_str()) {
205 if title == "Released" {
206 return item.pointer("/text").and_then(|v| v.as_str()).map(|s| s.to_string());
207 }
208 }
209 }
210 }
211 None
212 });
213
214 let genre = track
215 .pointer("/genres/primary")
216 .and_then(|v| v.as_str())
217 .map(|s| s.to_string());
218
219 Ok(RecognitionResult {
220 song_name,
221 artist_name,
222 album_name,
223 track_key,
224 release_year,
225 genre,
226 recognition_timestamp: chrono::Utc::now(),
227 raw_response: response,
228 })
229 }
230}
231
232impl RecognitionStream {
233 pub fn next(&self) -> Option<Result<RecognitionResult>> {
235 self.receiver.recv().ok()
236 }
237
238 pub fn try_next(&self) -> Option<Result<RecognitionResult>> {
240 self.receiver.try_recv().ok()
241 }
242
243 pub fn next_timeout(&self, timeout: Duration) -> Option<Result<RecognitionResult>> {
245 self.receiver.recv_timeout(timeout).ok()
246 }
247}
248
249impl Iterator for RecognitionStream {
250 type Item = Result<RecognitionResult>;
251
252 fn next(&mut self) -> Option<Self::Item> {
253 RecognitionStream::next(self)
254 }
255}