1616
1717#define DR_MP3_IMPLEMENTATION
1818#include " dr_libs/dr_mp3.h"
19+ #include " stb/stb_vorbis.c"
1920
2021#include < thread>
2122#include < mutex>
@@ -128,14 +129,19 @@ namespace
128129 enum class audio_file_type
129130 {
130131 unknown,
131- mp3
132+ mp3,
133+ ogg
132134 };
133135
134136 audio_file_type detect_audio_type (const u8 * header, u32 header_size)
135137 {
136138 if (header_size < 4 )
137139 return audio_file_type::unknown;
138140
141+ // Check for Ogg container (OggS magic)
142+ if (header[0 ] == ' O' && header[1 ] == ' g' && header[2 ] == ' g' && header[3 ] == ' S' )
143+ return audio_file_type::ogg;
144+
139145 // Check for ID3 tag (ID3v2 at start of file)
140146 if (header[0 ] == ' I' && header[1 ] == ' D' && header[2 ] == ' 3' )
141147 return audio_file_type::mp3;
@@ -192,38 +198,60 @@ namespace
192198 // Detect file type from header
193199 audio_file_type file_type = detect_audio_type ((const u8 *)file_buf, file_size);
194200
195- if (file_type != audio_file_type::mp3 )
201+ if (file_type == audio_file_type::unknown )
196202 {
197203 pen::memory_free (file_buf);
198204 _waveform_data[resource_slot].state = e_waveform_state::error;
199205 continue ;
200206 }
201207
202- // Initialize dr_mp3 decoder from memory
203- drmp3 mp3;
204- if (!drmp3_init_memory (&mp3, file_buf, file_size, nullptr ))
208+ // Initialize decoder and get metadata
209+ drmp3 mp3 = {};
210+ stb_vorbis* vorbis = nullptr ;
211+ u64 total_frames = 0 ;
212+ u32 num_channels = 0 ;
213+ u32 sample_rate = 0 ;
214+
215+ if (file_type == audio_file_type::mp3)
205216 {
206- pen::memory_free (file_buf);
207- _waveform_data[resource_slot].state = e_waveform_state::error;
208- continue ;
217+ if (!drmp3_init_memory (&mp3, file_buf, file_size, nullptr ))
218+ {
219+ pen::memory_free (file_buf);
220+ _waveform_data[resource_slot].state = e_waveform_state::error;
221+ continue ;
222+ }
223+ total_frames = drmp3_get_pcm_frame_count (&mp3);
224+ num_channels = mp3.channels ;
225+ sample_rate = mp3.sampleRate ;
226+ }
227+ else if (file_type == audio_file_type::ogg)
228+ {
229+ int vorbis_error = 0 ;
230+ vorbis = stb_vorbis_open_memory ((const unsigned char *)file_buf, file_size, &vorbis_error, nullptr );
231+ if (!vorbis)
232+ {
233+ pen::memory_free (file_buf);
234+ _waveform_data[resource_slot].state = e_waveform_state::error;
235+ continue ;
236+ }
237+ stb_vorbis_info info = stb_vorbis_get_info (vorbis);
238+ total_frames = stb_vorbis_stream_length_in_samples (vorbis);
239+ num_channels = info.channels ;
240+ sample_rate = info.sample_rate ;
209241 }
210242
211- // Get total frame count for calculating bucket sizes
212- drmp3_uint64 total_frames = drmp3_get_pcm_frame_count (&mp3);
213- if (total_frames == 0 )
243+ if (total_frames == 0 || num_channels == 0 )
214244 {
215- drmp3_uninit (&mp3);
245+ if (file_type == audio_file_type::mp3) drmp3_uninit (&mp3);
246+ if (vorbis) stb_vorbis_close (vorbis);
216247 pen::memory_free (file_buf);
217248 _waveform_data[resource_slot].state = e_waveform_state::error;
218249 continue ;
219250 }
220251
221- u32 num_channels = mp3.channels ;
222- u32 sample_rate = mp3.sampleRate ;
223252 u32 length_ms = (u32 )((total_frames * 1000 ) / sample_rate);
224253
225254 // Allocate local bucket storage (min/max pairs)
226- // Keep local until complete to avoid races with cleanup
227255 f32 * buckets = (f32 *)pen::memory_alloc (resolution * 2 * sizeof (f32 ));
228256
229257 // Initialize buckets
@@ -234,15 +262,15 @@ namespace
234262 }
235263
236264 // Calculate frames per bucket
237- drmp3_uint64 frames_per_bucket = total_frames / resolution;
265+ u64 frames_per_bucket = total_frames / resolution;
238266 if (frames_per_bucket == 0 ) frames_per_bucket = 1 ;
239267
240268 // Decode in chunks
241269 constexpr u32 chunk_frames = 4096 ;
242270 f32 * chunk_buffer = (f32 *)pen::memory_alloc (chunk_frames * num_channels * sizeof (f32 ));
243271
244272 u32 current_bucket = 0 ;
245- drmp3_uint64 frames_in_current_bucket = 0 ;
273+ u64 frames_in_current_bucket = 0 ;
246274 f32 bucket_min = 1 .0f ;
247275 f32 bucket_max = -1 .0f ;
248276 f32 inv_num_channels = 1 .0f / (f32 )num_channels;
@@ -262,13 +290,21 @@ namespace
262290 }
263291
264292 // Read a chunk of PCM frames
265- drmp3_uint64 frames_read = drmp3_read_pcm_frames_f32 (&mp3, chunk_frames, chunk_buffer);
293+ u32 frames_read = 0 ;
294+ if (file_type == audio_file_type::mp3)
295+ {
296+ frames_read = (u32 )drmp3_read_pcm_frames_f32 (&mp3, chunk_frames, chunk_buffer);
297+ }
298+ else if (file_type == audio_file_type::ogg)
299+ {
300+ frames_read = stb_vorbis_get_samples_float_interleaved (vorbis, num_channels, chunk_buffer, chunk_frames * num_channels) ;
301+ }
266302
267303 if (frames_read == 0 )
268304 break ; // End of file
269305
270306 // Process the chunk into buckets
271- for (drmp3_uint64 frame = 0 ; frame < frames_read; frame += sample_stride)
307+ for (u32 frame = 0 ; frame < frames_read; frame += sample_stride)
272308 {
273309 // Average channels for this sample
274310 f32 sample_value = 0 .0f ;
@@ -308,7 +344,8 @@ namespace
308344
309345 // Clean up decode resources
310346 pen::memory_free (chunk_buffer);
311- drmp3_uninit (&mp3);
347+ if (file_type == audio_file_type::mp3) drmp3_uninit (&mp3);
348+ if (vorbis) stb_vorbis_close (vorbis);
312349 pen::memory_free (file_buf);
313350
314351 // Final cancel check before exposing buckets
0 commit comments