active_call/media/
loader.rs

1use crate::media::cache;
2use anyhow::{Result, anyhow};
3use audio_codec::Resampler;
4use hound::WavReader;
5use reqwest::Client;
6use std::fs::File;
7use std::io::{BufReader, Read, Seek, SeekFrom, Write};
8use std::time::Instant;
9use tracing::{info, warn};
10use url::Url;
11
12pub async fn download_from_url(url: &str, use_cache: bool) -> Result<File> {
13    // Check if file is already cached
14    let cache_key = cache::generate_cache_key(url, 0, None, None);
15    if use_cache && cache::is_cached(&cache_key).await? {
16        match cache::get_cache_path(&cache_key) {
17            Ok(path) => return File::open(&path).map_err(|e| anyhow!(e)),
18            Err(e) => {
19                warn!("loader: Error getting cache path: {}", e);
20                return Err(e);
21            }
22        }
23    }
24
25    // Download file if not cached
26    let start_time = Instant::now();
27    let client = Client::new();
28    let response = client.get(url).send().await?;
29    let bytes = response.bytes().await?;
30    let data = bytes.to_vec();
31    let duration = start_time.elapsed();
32
33    info!(
34        "loader: Downloaded {} bytes in {:?} for {}",
35        data.len(),
36        duration,
37        url,
38    );
39
40    // Store in cache if enabled
41    if use_cache {
42        cache::store_in_cache(&cache_key, &data).await?;
43        match cache::get_cache_path(&cache_key) {
44            Ok(path) => return File::open(path).map_err(|e| anyhow!(e)),
45            Err(e) => {
46                warn!("loader: Error getting cache path: {}", e);
47                return Err(e);
48            }
49        }
50    }
51
52    // Return temporary file with downloaded data
53    let mut temp_file = tempfile::tempfile()?;
54    temp_file.write_all(&data)?;
55    temp_file.seek(SeekFrom::Start(0))?;
56    Ok(temp_file)
57}
58
59pub fn decode_wav(file: File, target_sample_rate: u32) -> Result<Vec<i16>> {
60    let reader = BufReader::new(file);
61    let mut wav_reader = WavReader::new(reader)?;
62    let spec = wav_reader.spec();
63    let sample_rate = spec.sample_rate;
64    let is_stereo = spec.channels == 2;
65
66    info!(
67        "WAV file detected with sample rate: {} Hz, channels: {}, bits: {}",
68        sample_rate, spec.channels, spec.bits_per_sample
69    );
70
71    let mut all_samples = Vec::new();
72
73    // Read all samples based on format and bit depth
74    match spec.sample_format {
75        hound::SampleFormat::Int => match spec.bits_per_sample {
76            16 => {
77                for sample in wav_reader.samples::<i16>() {
78                    if let Ok(s) = sample {
79                        all_samples.push(s);
80                    } else {
81                        break;
82                    }
83                }
84            }
85            8 => {
86                for sample in wav_reader.samples::<i8>() {
87                    if let Ok(s) = sample {
88                        all_samples.push((s as i16) * 256); // Convert 8-bit to 16-bit
89                    } else {
90                        break;
91                    }
92                }
93            }
94            24 | 32 => {
95                for sample in wav_reader.samples::<i32>() {
96                    if let Ok(s) = sample {
97                        all_samples.push((s >> 16) as i16); // Convert 24/32-bit to 16-bit
98                    } else {
99                        break;
100                    }
101                }
102            }
103            _ => {
104                return Err(anyhow!(
105                    "Unsupported bits per sample: {}",
106                    spec.bits_per_sample
107                ));
108            }
109        },
110        hound::SampleFormat::Float => {
111            for sample in wav_reader.samples::<f32>() {
112                if let Ok(s) = sample {
113                    all_samples.push((s * 32767.0) as i16); // Convert float to 16-bit
114                } else {
115                    break;
116                }
117            }
118        }
119    }
120
121    // Convert stereo to mono if needed
122    if is_stereo {
123        let mono_samples = all_samples
124            .chunks(2)
125            .map(|chunk| {
126                if chunk.len() == 2 {
127                    ((chunk[0] as i32 + chunk[1] as i32) / 2) as i16
128                } else {
129                    chunk[0]
130                }
131            })
132            .collect();
133        all_samples = mono_samples;
134    }
135
136    if sample_rate != target_sample_rate && sample_rate > 0 {
137        let mut resampler = Resampler::new(sample_rate as usize, target_sample_rate as usize);
138        all_samples = resampler.resample(&all_samples);
139    }
140
141    Ok(all_samples)
142}
143
144pub fn decode_mp3(file: File, target_sample_rate: u32) -> Result<Vec<i16>> {
145    let mut reader = BufReader::new(file);
146    let mut file_data = Vec::new();
147    reader.read_to_end(&mut file_data)?;
148
149    let mut decoder = rmp3::Decoder::new(&file_data);
150    let mut all_samples = Vec::new();
151    let mut sample_rate = 0;
152
153    while let Some(frame) = decoder.next() {
154        match frame {
155            rmp3::Frame::Audio(audio) => {
156                if sample_rate == 0 {
157                    sample_rate = audio.sample_rate();
158                    info!("MP3 file detected with sample rate: {} Hz", sample_rate);
159                }
160                all_samples.extend_from_slice(audio.samples());
161            }
162            rmp3::Frame::Other(_) => {}
163        }
164    }
165
166    if sample_rate != target_sample_rate && sample_rate > 0 {
167        let mut resampler = Resampler::new(sample_rate as usize, target_sample_rate as usize);
168        all_samples = resampler.resample(&all_samples);
169    }
170
171    Ok(all_samples)
172}
173
174pub async fn load_audio_as_pcm(
175    path: &str,
176    target_sample_rate: u32,
177    use_cache: bool,
178) -> Result<Vec<i16>> {
179    let extension = if path.starts_with("http://") || path.starts_with("https://") {
180        path.parse::<Url>()?
181            .path()
182            .split(".")
183            .last()
184            .unwrap_or("")
185            .to_string()
186    } else {
187        path.split('.').last().unwrap_or("").to_string()
188    };
189
190    let file = if path.starts_with("http://") || path.starts_with("https://") {
191        download_from_url(path, use_cache).await?
192    } else {
193        File::open(path).map_err(|e| anyhow!("loader: {} {}", path, e))?
194    };
195
196    match extension.to_lowercase().as_str() {
197        "wav" => decode_wav(file, target_sample_rate),
198        "mp3" => decode_mp3(file, target_sample_rate),
199        _ => Err(anyhow!("loader: Unsupported file extension: {}", extension)),
200    }
201}