@@ -53,6 +53,7 @@ public bool MultiChannelEncryptFlag
5353 public AwcChunkInfo [ ] ChunkInfos { get ; set ; } // just for browsing convenience really
5454 public bool WholeFileEncrypted { get ; set ; }
5555 public AwcStreamInfo [ ] StreamInfos { get ; set ; }
56+ public AwcStream [ ] AllStreams { get ; set ; }
5657 public AwcStream [ ] Streams { get ; set ; }
5758 public AwcStream MultiChannelSource { get ; set ; }
5859 public Dictionary < uint , AwcStream > StreamDict { get ; set ; }
@@ -94,6 +95,7 @@ public void Load(byte[] data, string entry)
9495 var fn = Path . GetFileNameWithoutExtension ( nl ) ;
9596 JenkIndex . Ensure ( fn + "_left" ) ;
9697 JenkIndex . Ensure ( fn + "_right" ) ;
98+ JenkIndex . Ensure ( fn + "_center" ) ;
9799 }
98100
99101 if ( ( data == null ) || ( data . Length < 8 ) )
@@ -203,8 +205,10 @@ private void Read(DataReader r)
203205 }
204206
205207 Streams = streams . Where ( s => s . DataChunk != null ) . ToArray ( ) ;
208+ AllStreams = streams . ToArray ( ) ;
206209 MultiChannelSource ? . AssignMultiChannelSources ( Streams ) ;
207- BuildStreamDict ( ) ;
210+
211+ BuildStreamDict ( AllStreams ) ;
208212 TestChunkOrdering ( ) ;
209213 }
210214
@@ -219,7 +223,7 @@ private void Write(DataWriter w)
219223 dataOffset += ( int ) info . ChunkCount * 8 ;
220224 }
221225
222- var chunks = GetSortedChunks ( ) ;
226+ var chunks = GetSortedChunks ( AllStreams ) ;
223227 DataOffset = dataOffset ;
224228
225229 w . Write ( Magic ) ;
@@ -319,14 +323,15 @@ public void ReadXml(XmlNode node, string wavfolder)
319323 }
320324
321325 Streams = slist . ToArray ( ) ;
326+ AllStreams = slist . ToArray ( ) ;
322327 StreamCount = Streams ? . Length ?? 0 ;
323328 MultiChannelSource ? . CompactMultiChannelSources ( Streams ) ;
324329 }
325330
326331 BuildPeakChunks ( ) ;
327- BuildChunkIndices ( ) ;
328- BuildStreamInfos ( ) ;
329- BuildStreamDict ( ) ;
332+ BuildChunkIndices ( AllStreams ) ;
333+ BuildStreamInfos ( AllStreams ) ;
334+ BuildStreamDict ( AllStreams ) ;
330335 }
331336
332337 public static void WriteXmlNode ( AwcFile f , StringBuilder sb , int indent , string wavfolder , string name = "AudioWaveContainer" )
@@ -472,58 +477,7 @@ public void ReplaceAudioStreamSingle(MetaHash streamHash, uint sampleCount, uint
472477 }
473478 else if ( targetCodec == AwcCodecType . OPUS )
474479 {
475- var pcmSamples = new short [ pcmAudioData . Length / 2 ] ;
476- Buffer . BlockCopy ( pcmAudioData , 0 , pcmSamples , 0 , pcmAudioData . Length ) ;
477-
478- var frameSamples = 960 ; //20ms @ 48kHz
479- var frameSizeBytes = 80 ;
480- var frames = new List < byte [ ] > ( ) ;
481-
482- using var encoder = OpusEncoder . Create ( ( int ) sampleRate , 1 ) ; //2 for stereo
483- encoder . SetBitrate ( 64000 ) ;
484-
485- for ( int pos = 0 ; pos < pcmSamples . Length ; pos += frameSamples )
486- {
487- var len = Math . Min ( frameSamples , pcmSamples . Length - pos ) ;
488- var frame = new short [ len * 2 ] ;
489- Array . Copy ( pcmSamples , pos , frame , 0 , len ) ;
490-
491- var packet = encoder . Encode ( frame , len ) ;
492- if ( packet . Length < frameSizeBytes )
493- {
494- Array . Resize ( ref packet , frameSizeBytes ) ; //Pad
495- }
496- frames . Add ( packet ) ;
497- }
498-
499- using var ms = new MemoryStream ( ) ;
500- using var bw = new BinaryWriter ( ms ) ;
501-
502- //Block header (per channel, 0x10)
503- bw . Write ( ( uint ) 0 ) ; //start_entry
504- bw . Write ( ( uint ) frames . Count ) ; //entries
505- bw . Write ( ( uint ) 0 ) ; //channel_skip
506- bw . Write ( ( uint ) ( frames . Count * frameSamples ) ) ; //channel_samples
507-
508- //Extra header (0x70, D11A)
509- bw . Write ( 0x41313144 ) ; //"D11A"
510- bw . Write ( ( ushort ) frameSizeBytes ) ; //Bytes per packet
511- bw . Write ( ( ushort ) frameSamples ) ; //PCM samples per frame
512- bw . Write ( ( ushort ) 0x0101 ) ; //OPUS flags
513- bw . Write ( ( ushort ) sampleRate ) ; //Sample rate
514- bw . Write ( 0x45313144 ) ; //"D11E"
515-
516- for ( int i = 0x10 ; i < 0x70 ; i ++ )
517- {
518- bw . Write ( ( byte ) 0x77 ) ;
519- }
520-
521- //Payload
522- foreach ( var f in frames )
523- {
524- bw . Write ( f ) ;
525- }
526- pcmAudioData = ms . ToArray ( ) ;
480+ pcmAudioData = OpusHelper . Encode ( pcmAudioData , sampleCount , sampleRate ) ;
527481 }
528482
529483 AwcStream targetStream = null ;
@@ -546,27 +500,26 @@ public void ReplaceAudioStreamSingle(MetaHash streamHash, uint sampleCount, uint
546500 case AwcCodecType . PCM :
547501 case AwcCodecType . ADPCM :
548502 case AwcCodecType . MSADPCM :
503+ case AwcCodecType . OPUS :
549504 ReplacePCM ( ref targetStream , sampleCount , sampleRate , pcmAudioData , targetCodec , MultiChannelFlag ) ;
550505 break ;
551506 case AwcCodecType . VORBIS :
552507 throw new NotImplementedException ( "Vorbis support is not implemented" ) ;
553- case AwcCodecType . OPUS :
554- throw new NotImplementedException ( "OPUS support is not implemented" ) ;
555508 default :
556509 throw new NotImplementedException ( "This codec is not implemented" ) ;
557510 }
558511 }
559512
560- public static void ReplacePCM ( ref AwcStream stream , uint sampleCount , uint sampleRate , byte [ ] pcmAudioData , AwcCodecType codecType , bool isMultiChannel = false )
513+ public void ReplacePCM ( ref AwcStream stream , uint sampleCount , uint sampleRate , byte [ ] pcmAudioData , AwcCodecType codecType , bool isMultiChannel = false )
561514 {
562515 if ( stream . VorbisChunk != null )
563516 {
564517 stream . VorbisChunk = null ;
565518 }
566519
567- if ( ! isMultiChannel )
520+ if ( ! isMultiChannel && codecType != AwcCodecType . OPUS )
568521 {
569- var fmtChunk = ( AwcFormatChunk ) stream . Chunks . Where ( c => c . GetType ( ) == typeof ( AwcFormatChunk ) ) . FirstOrDefault ( ) ;
522+ var fmtChunk = ( AwcFormatChunk ) stream . Chunks . Where ( c => c . GetType ( ) == typeof ( AwcFormatChunk ) ) ;
570523 if ( fmtChunk != null )
571524 {
572525 fmtChunk . Samples = sampleCount ;
@@ -576,15 +529,51 @@ public static void ReplacePCM(ref AwcStream stream, uint sampleCount, uint sampl
576529 }
577530 else
578531 {
579- stream . StreamFormat . Samples = sampleCount ;
580- stream . StreamFormat . SamplesPerSecond = ( ushort ) sampleRate ;
581- stream . StreamFormat . Codec = codecType ;
532+ UpdateAllFormatChunksForMultichannel ( sampleCount , sampleRate , codecType ) ;
582533 }
583534
584535 stream . DataChunk ??= new AwcDataChunk ( new AwcChunkInfo ( ) { Type = AwcChunkType . data } ) ;
585536 stream . DataChunk . Data = pcmAudioData ;
586537 }
587538
539+ public void UpdateAllFormatChunksForMultichannel ( uint sampleCount , uint sampleRate , AwcCodecType codecType )
540+ {
541+ if ( MultiChannelSource != null )
542+ {
543+ MultiChannelSource . StreamFormat . Samples = sampleCount ;
544+ MultiChannelSource . StreamFormat . SamplesPerSecond = ( ushort ) sampleRate ;
545+ MultiChannelSource . StreamFormat . Codec = codecType ;
546+ }
547+
548+ var src = AllStreams ?? Streams ;
549+ if ( src == null ) return ;
550+
551+ foreach ( var s in src )
552+ {
553+ if ( s ? . Chunks == null ) continue ;
554+ foreach ( var c in s . Chunks )
555+ {
556+ if ( c is AwcFormatChunk fmt )
557+ {
558+ fmt . Samples = sampleCount ;
559+ fmt . SamplesPerSecond = ( ushort ) sampleRate ;
560+ fmt . Codec = codecType ;
561+ }
562+
563+ if ( c is AwcStreamFormatChunk sfc )
564+ {
565+ for ( int i = 0 ; i < sfc . ChannelCount ; i ++ )
566+ {
567+ var channel = sfc . Channels [ i ] ;
568+ channel . Samples = sampleCount ;
569+ channel . SamplesPerSecond = ( ushort ) sampleRate ;
570+ channel . Codec = codecType ;
571+ }
572+ }
573+ }
574+ }
575+ }
576+
588577 public static void ReplaceVorbis ( ref AwcStream stream , uint sampleCount , uint sampleRate , byte [ ] audioData , byte [ ] streamIdData , byte [ ] commentData , byte [ ] codebookData , bool isMultiChannel = false )
589578 {
590579 //Remove old format chunk
@@ -659,17 +648,15 @@ public static void ReplaceVorbis(ref AwcStream stream, uint sampleCount, uint sa
659648 stream . DataChunk . Data = audioData ;
660649 }
661650
662- public AwcChunk [ ] GetSortedChunks ( )
651+ public AwcChunk [ ] GetSortedChunks ( AwcStream [ ] src )
663652 {
664653 var chunks = new List < AwcChunk > ( ) ;
665- if ( Streams != null )
654+ if ( src != null )
666655 {
667- foreach ( var stream in Streams )
656+ foreach ( var stream in src )
668657 {
669- if ( stream . Chunks != null )
670- {
658+ if ( stream ? . Chunks != null )
671659 chunks . AddRange ( stream . Chunks ) ;
672- }
673660 }
674661 }
675662
@@ -841,42 +828,39 @@ ushort getPeak(int n)
841828 }
842829 }
843830
844- public void BuildChunkIndices ( )
831+ public void BuildChunkIndices ( AwcStream [ ] src )
845832 {
846- if ( Streams == null ) return ;
833+ if ( src == null ) return ;
847834
848835 var inds = new List < ushort > ( ) ;
849836 ushort ind = 0 ;
850837
851- foreach ( var stream in Streams )
838+ foreach ( var stream in src )
852839 {
853840 inds . Add ( ind ) ;
854841 ind += ( ushort ) ( stream . Chunks ? . Length ?? 0 ) ;
855842 }
856843
857- if ( ChunkIndicesFlag )
858- ChunkIndices = inds . ToArray ( ) ;
859- else
860- ChunkIndices = null ;
844+ ChunkIndices = ChunkIndicesFlag ? inds . ToArray ( ) : null ;
861845 }
862846
863- public void BuildStreamInfos ( )
847+ public void BuildStreamInfos ( AwcStream [ ] src )
864848 {
865849 var streaminfos = new List < AwcStreamInfo > ( ) ;
866850 var chunkinfos = new List < AwcChunkInfo > ( ) ;
867851
868- if ( Streams != null )
852+ if ( src != null )
869853 {
870- var streamCount = Streams . Length ;
854+ var streamCount = src . Length ;
871855 var infoStart = 16 + ( ChunkIndicesFlag ? ( streamCount * 2 ) : 0 ) ;
872856 var dataOffset = infoStart + streamCount * 4 ;
873857
874- foreach ( var stream in Streams )
858+ foreach ( var stream in src )
875859 {
876860 dataOffset += ( stream ? . Chunks ? . Length ?? 0 ) * 8 ;
877861 }
878862
879- var chunks = GetSortedChunks ( ) ;
863+ var chunks = GetSortedChunks ( src ) ;
880864 foreach ( var chunk in chunks )
881865 {
882866 var chunkinfo = chunk . ChunkInfo ;
@@ -894,7 +878,7 @@ public void BuildStreamInfos()
894878 dataOffset += size ;
895879 }
896880
897- foreach ( var stream in Streams )
881+ foreach ( var stream in src )
898882 {
899883 var streaminfo = stream . StreamInfo ;
900884 streaminfos . Add ( streaminfo ) ;
@@ -915,12 +899,12 @@ public void BuildStreamInfos()
915899 StreamInfos = streaminfos . ToArray ( ) ;
916900 }
917901
918- public void BuildStreamDict ( )
902+ public void BuildStreamDict ( AwcStream [ ] src )
919903 {
920904 StreamDict = new Dictionary < uint , AwcStream > ( ) ;
921- if ( Streams == null ) return ;
905+ if ( src == null ) return ;
922906
923- foreach ( var stream in Streams )
907+ foreach ( var stream in src )
924908 {
925909 StreamDict [ stream . Hash ] = stream ;
926910 }
0 commit comments