88using Prowl . Runtime . Audio ;
99using Prowl . Runtime . GUI ;
1010using Prowl . Runtime . Utils ;
11+ using NVorbis ;
1112
1213namespace Prowl . Editor . Assets . Importers ;
1314
14- [ Importer ( "FileIcon.png" , typeof ( AudioClip ) , ".wav" ) ]
15+ [ Importer ( "FileIcon.png" , typeof ( AudioClip ) , ".wav" , ".wave" , ".ogg" ) ]
1516public class AudioClipImporter : ScriptedImporter
1617{
1718 public override void Import ( SerializedAsset ctx , FileInfo assetPath )
@@ -20,10 +21,55 @@ public override void Import(SerializedAsset ctx, FileInfo assetPath)
2021 {
2122 ".wav" => LoadWav ( assetPath ) ,
2223 ".wave" => LoadWav ( assetPath ) ,
24+ ".ogg" => LoadOgg ( assetPath ) ,
25+ ".oga" => LoadOgg ( assetPath ) ,
2326 _ => throw new InvalidOperationException ( "Unsupported audio format: " + assetPath . Extension . ToLower ( ) ) ,
2427 } ) ;
2528 }
2629
30+ private static AudioClip LoadOgg ( FileInfo file )
31+ {
32+ // Decode Ogg Vorbis to interleaved 16-bit PCM (little-endian) using NVorbis
33+ using var fs = file . OpenRead ( ) ;
34+ using var vorbis = new VorbisReader ( fs , false ) ;
35+
36+ int channels = vorbis . Channels ;
37+ int sampleRate = vorbis . SampleRate ;
38+ const short bitsPerSample = 16 ; // output s16 PCM
39+
40+ // Converting NVorbis outputs floats of [-1..1] interleaved across channels
41+ // to s16 to avoid large allocations.
42+ var floatBuf = new float [ 4096 ] ; // count is "samples"
43+ using var ms = new MemoryStream ( ) ;
44+
45+ int samplesRead ;
46+ Span < byte > le = stackalloc byte [ 2 ] ; // scratch for one s16
47+
48+ while ( ( samplesRead = vorbis . ReadSamples ( floatBuf , 0 , floatBuf . Length ) ) > 0 )
49+ {
50+ for ( int i = 0 ; i < samplesRead ; i ++ )
51+ {
52+ // clamp and convert float -> 16-bit signed
53+ float f = floatBuf [ i ] ;
54+ if ( f > 1f ) f = 1f ;
55+ else if ( f < - 1f ) f = - 1f ;
56+
57+ short s = ( short ) MathF . Round ( f * 32767f ) ;
58+
59+ // write little-endian
60+ System . Buffers . Binary . BinaryPrimitives . WriteInt16LittleEndian ( le , s ) ;
61+ ms . Write ( le ) ;
62+ }
63+ }
64+
65+ byte [ ] audioData = ms . ToArray ( ) ;
66+
67+ // Creating the clip just like the WAV path does:
68+ AudioClip audioClip = AudioClip . Create ( file . Name , audioData , ( short ) channels , bitsPerSample , sampleRate ) ;
69+ return audioClip ;
70+ }
71+
72+
2773 #region Wave Format
2874
2975 private static AudioClip LoadWav ( FileInfo file )
0 commit comments