public void Write(Span <byte> data, Meta meta) { UpdatedSubscriptionCache(); var codec = meta?.Codec ?? Codec.OpusMusic; // XXX a bit hacky switch (SendMode) { case TargetSendMode.None: break; case TargetSendMode.Voice: client.SendAudio(data, codec); break; case TargetSendMode.Whisper: client.SendAudioWhisper(data, codec, channelSubscriptionsCache, clientSubscriptionsCache); break; case TargetSendMode.WhisperGroup: client.SendAudioGroupWhisper(data, codec, GroupWhisperType, GroupWhisperTarget, GroupWhisperTargetId); break; default: throw new ArgumentOutOfRangeException(nameof(SendMode), "Unknown send target"); } }
private void AudioSend() { lock (ffmpegLock) { if (ffmpegProcess == null) { return; } if (audioBuffer == null || audioBuffer.Length < encoder.OptimalPacketSize) { audioBuffer = new byte[encoder.OptimalPacketSize]; } UpdatedSubscriptionCache(); while (audioTimer.RemainingBufferDuration < audioBufferLength) { int read = ffmpegProcess.StandardOutput.BaseStream.Read(audioBuffer, 0, encoder.OptimalPacketSize); if (read == 0) { // check for premature connection drop if (ffmpegProcess.HasExited && !hasTriedToReconnectAudio) { var expectedStopLength = GetCurrentSongLength(); if (expectedStopLength != TimeSpan.Zero) { var actualStopPosition = audioTimer.SongPosition; if (actualStopPosition + retryOnDropBeforeEnd < expectedStopLength) { Log.Write(Log.Level.Debug, "Connection to song lost, retrying at {0}", actualStopPosition); hasTriedToReconnectAudio = true; Position = actualStopPosition; return; } } } if (ffmpegProcess.HasExited && audioTimer.RemainingBufferDuration < TimeSpan.Zero && !encoder.HasPacket) { AudioStop(); OnSongEnd?.Invoke(this, new EventArgs()); } return; } hasTriedToReconnectAudio = false; audioTimer.PushBytes(read); bool doSend = true; switch (SendMode) { case TargetSendMode.None: doSend = false; break; case TargetSendMode.Voice: break; case TargetSendMode.Whisper: case TargetSendMode.WhisperGroup: if (isStall) { if (++stallCount % StallCountInterval == 0) { stallNoErrorCount++; if (stallNoErrorCount > StallNoErrorCountMax) { stallCount = 0; isStall = false; } } else { doSend = false; } } if (SendMode == TargetSendMode.Whisper) { doSend &= channelSubscriptionsCache.Length > 0 || clientSubscriptionsCache.Length > 0; } break; default: throw new InvalidOperationException(); } // Save cpu when we know there is noone to send to if (!doSend) { break; } AudioModifier.AdjustVolume(audioBuffer, read, volume); encoder.PushPcmAudio(audioBuffer, read); while (encoder.HasPacket) { var packet = encoder.GetPacket(); switch (SendMode) { case TargetSendMode.Voice: tsFullClient.SendAudio(packet.Array, packet.Length, encoder.Codec); break; case TargetSendMode.Whisper: tsFullClient.SendAudioWhisper(packet.Array, packet.Length, encoder.Codec, channelSubscriptionsCache, clientSubscriptionsCache); break; case TargetSendMode.WhisperGroup: tsFullClient.SendAudioGroupWhisper(packet.Array, packet.Length, encoder.Codec, GroupWhisperType, GroupWhisperTarget); break; } encoder.ReturnPacket(packet.Array); } } } }