/// <summary> /// Initializes this upmixer by creating the obligatory reference channels and the extra channels that are /// used by the <see cref="source"/>. /// </summary> void Init() { ReferenceChannel[] layout = ChannelPrototype.GetStandardMatrix(8); output = new ChannelData[layout.Length]; for (int channel = 0; channel < layout.Length; ++channel) { output[channel].target = layout[channel]; } output[0].lastSamples = new float[0]; // To skip null check in GenerateSamples layout = ChannelPrototype.GetStandardMatrix(source.Channels); List <ReferenceChannel> missing = new List <ReferenceChannel>(); for (int channel = 0; channel < layout.Length; ++channel) { if (!output.Has(entry => entry.target == layout[channel])) { missing.Add(layout[channel]); } } int offset = output.Length, news = missing.Count; Array.Resize(ref output, offset + news); for (int channel = 0; channel < news; ++channel) { output[channel + offset] = new ChannelData(missing[channel]); } }
/// <summary> /// Render new object samples for the next timeslot /// </summary> void RenderNextTimeslot() { float[] input = new float[QuadratureMirrorFilterBank.subbands * stream.ChannelCount]; stream.DecodeBlock(input, 0, input.LongLength); Source?.ReadBlock(input, 0, input.LongLength); WaveformUtils.InterlacedToMultichannel(input, inputData); ReferenceChannel[] matrix = ChannelPrototype.GetStandardMatrix(stream.ChannelCount); EnhancedAC3Decoder decoder = (EnhancedAC3Decoder)stream; // Object-based rendering if (HasObjects = decoder.Extensions.HasObjects) { decoder.Extensions.OAMD.UpdateSources(decoder.LastFetchStart / stream.ChannelCount, objects); float[][] sources = new float[JointObjectCodingTables.inputMatrix.Length][]; for (int i = 0; i < sources.Length; ++i) { for (int j = 0; j < matrix.Length; ++j) { if (JointObjectCodingTables.inputMatrix[i] == matrix[j]) { sources[i] = inputData[j]; break; } } } for (int i = 0; i < matrix.Length; ++i) { if (matrix[i] == ReferenceChannel.ScreenLFE) { lfeTimeslot = inputData[i]; } } timeslotResult = applier.Apply(sources, decoder.Extensions.JOC); } // Channel-based rendering or fallback to it when OAMD or JOC can't be decoded correctly else { for (int i = 0; i < matrix.Length; ++i) { timeslotResult[i] = inputData[i]; objects[i].Position = channelPositions[(int)matrix[i]] * Listener.EnvironmentSize; if (ChannelPrototype.Mapping[(int)matrix[i]].LFE) // LFE is handled elsewhere { objects[i].Position = default; Array.Clear(timeslotResult[i], 0, timeslotResult[i].Length); } } for (int i = matrix.Length; i < timeslotResult.Length; ++i) { objects[i].Position = default; Array.Clear(timeslotResult[i], 0, timeslotResult[i].Length); } } }
/// <summary> /// Set up the renderer for a number of standard channels. /// </summary> protected void SetupChannels(int count) { ReferenceChannel[] matrix = ChannelPrototype.GetStandardMatrix(count); for (int channel = 0; channel < count; ++channel) { Source source = new StreamMasterSource(reader, channel) { Position = channelPositions[(int)matrix[channel]] * Listener.EnvironmentSize }; objects.Add(source); } FinishSetup(count); }
void ProcessImpulse(object sender, RoutedEventArgs e) { if (browser.ShowDialog().Value) { AudioReader reader = AudioReader.Open(browser.FileName); float[] impulse = reader.Read(); float gain = 1; if (keepGain.IsChecked.Value) { gain = WaveformUtils.GetPeak(impulse); } if (commonEQ.IsChecked.Value) { ProcessCommon(reader, ref impulse); } else { ProcessPerChannel(reader, ref impulse); } if (keepGain.IsChecked.Value) { WaveformUtils.Gain(impulse, gain / WaveformUtils.GetPeak(impulse)); } BitDepth bits = reader.Bits; if (forceFloat.IsChecked.Value) { bits = BitDepth.Float32; } int targetLen = QMath.Base2Ceil((int)reader.Length); if (separateExport.IsChecked.Value) { ReferenceChannel[] channels = ChannelPrototype.GetStandardMatrix(reader.ChannelCount); for (int ch = 0; ch < reader.ChannelCount; ++ch) { string exportName = Path.GetFileName(browser.FileName); int idx = exportName.LastIndexOf('.'); string channelName = ChannelPrototype.Mapping[(int)channels[ch]].Name; exporter.FileName = $"{exportName[..idx]} - {channelName}{exportName[idx..]}";
/// <summary> /// Generate the next <paramref name="samples"/> samples to be retrieved with <see cref="RetrieveSamples(ReferenceChannel)"/>. /// </summary> public void GenerateSamples(int samples) { // Preparations ReferenceChannel[] layout = ChannelPrototype.GetStandardMatrix(source.Channels); if (output[0].lastSamples.Length != samples) { for (int channel = 0; channel < output.Length; ++channel) { output[channel].lastSamples = null; } cache = new float[source.Channels][]; for (int channel = 0; channel < source.Channels; ++channel) { cache[channel] = new float[samples]; int channelId = GetChannelId(layout[channel]); if (channelId != -1) { output[channelId].lastSamples = cache[channel]; // Cache linked with output, no need to copy } } for (int channel = 0; channel < output.Length; ++channel) { if (output[channel].lastSamples == null) { output[channel].lastSamples = new float[samples]; } } } for (int channel = 0; channel < output.Length; ++channel) { output[channel].writtenTo = false; } // Fetch samples source.GetData(cache, timeSamples); if (timeSamples >= source.Samples) { if (loop) { timeSamples %= source.Samples; } else { timeSamples = 0; OnPlaybackFinished(); } } timeSamples += samples; for (int channel = 0; channel < source.Channels; ++channel) { output[GetChannelId(layout[channel])].writtenTo = true; } // Create missing channels via matrix if asked for if (matrixUpmix) { if (output[0].writtenTo && output[1].writtenTo) // Left and right channels available { if (!output[2].writtenTo) // Create discrete middle channel { float[] left = output[0].lastSamples, right = output[1].lastSamples, center = output[2].lastSamples; for (int offset = 0; offset < samples; ++offset) { center[offset] = (left[offset] + right[offset]) * .5f; } output[2].writtenTo = true; } if (!output[6].writtenTo) // Matrix mix for sides { float[] leftFront = output[0].lastSamples, rightFront = output[1].lastSamples, leftSide = output[6].lastSamples, rightSide = output[7].lastSamples; for (int offset = 0; offset < samples; ++offset) { leftSide[offset] = (leftFront[offset] - rightFront[offset]) * .5f; rightSide[offset] = -leftSide[offset]; } output[6].writtenTo = output[7].writtenTo = true; } if (!output[4].writtenTo) // Extend sides to rears... { bool rearsAvailable = false; // ...but only if there are rears in the system for (int channel = 0; channel < Listener.Channels.Length; ++channel) { float currentY = Listener.Channels[channel].Y; if (currentY < -135 || currentY > 135) { rearsAvailable = true; break; } } if (rearsAvailable) { float[] leftSide = output[6].lastSamples, rightSide = output[7].lastSamples, leftRear = output[4].lastSamples, rightRear = output[5].lastSamples; for (int offset = 0; offset < samples; ++offset) { leftRear[offset] = leftSide[offset] *= .5f; rightRear[offset] = rightSide[offset] *= .5f; } output[4].writtenTo = output[5].writtenTo = true; } } } } }
/// <summary> /// Read the next <paramref name="samples"/> and update the objects. /// </summary> /// <param name="samples">Samples per channel</param> public override void Update(int samples) { if (lfeResult.Length != samples) { timeslotResult = new float[finalResult.Length][]; for (int obj = 0; obj < finalResult.Length; ++obj) { finalResult[obj] = new float[samples]; } lfeTimeslot = new float[samples]; lfeResult = new float[samples]; } if (inputData.Length != stream.ChannelCount) { inputData = new float[stream.ChannelCount][]; for (int channel = 0; channel < inputData.Length; ++channel) { inputData[channel] = new float[QuadratureMirrorFilterBank.subbands]; } } int pointer = 0; while (pointer < samples) { if (timeslotPosition == 0) { RenderNextTimeslot(); } int next = Math.Min(samples - pointer, QuadratureMirrorFilterBank.subbands - timeslotPosition); for (int obj = 0; obj < finalResult.Length; ++obj) { for (int i = 0; i < next; ++i) { finalResult[obj][pointer + i] = timeslotResult[obj][timeslotPosition + i]; } } for (int i = 0; i < next; ++i) { lfeResult[pointer + i] = lfeTimeslot[timeslotPosition + i]; } pointer += next; timeslotPosition += next; if (timeslotPosition == QuadratureMirrorFilterBank.subbands) { timeslotPosition = 0; } } int lfe = ((EnhancedAC3Decoder)stream).Extensions.OAMD.GetLFEPosition(); if (lfe != -1) { for (int obj = 0; obj < lfe; ++obj) { objectSamples[obj] = finalResult[obj]; } ReferenceChannel[] matrix = ChannelPrototype.GetStandardMatrix(stream.ChannelCount); for (int i = 0; i < matrix.Length; ++i) { if (matrix[i] == ReferenceChannel.ScreenLFE) { objectSamples[lfe] = lfeResult; break; } } for (int obj = lfe + 1; obj < objectSamples.Length; ++obj) { objectSamples[obj] = finalResult[obj - 1]; } } else { for (int obj = 0; obj < objectSamples.Length; ++obj) { objectSamples[obj] = finalResult[obj]; } } }
/// <summary> /// Get the bed channels. /// </summary> public virtual ReferenceChannel[] GetChannels() => ChannelPrototype.GetStandardMatrix(stream.ChannelCount);
/// <summary> /// Loads a standard layout of the channel count of the imported file to a list of channels. /// </summary> void SetStandardLayout(ListBox holder) => holder.ItemsSource = (ReferenceChannel[])ChannelPrototype.GetStandardMatrix(reader.ChannelCount).Clone();