/// <summary>
 /// Creates outgoing audio stream of type automatically assigned and adds procedures (callback or serviceable) for consuming given audio source data.
 /// Adds audio specific features (e.g. resampling, level meter) to processing pipeline and to returning stream handler.
 /// </summary>
 /// <param name="voiceInfo">Outgoing audio stream parameters. Set applicable fields to read them by encoder and by receiving client when voice created.</param>
 /// <param name="source">Streaming audio source.</param>
 /// <param name="forceShort">For audio sources producing buffers of 'float' type, creates stream of 'short' type and adds converter.</param>
 /// <param name="channelId">Transport channel specific to transport. Set to VoiceClient.ChannelAuto to let transport automatically assign channel.</param>
 /// <param name="encoder">Audio encoder. Set to null to use default Opus encoder.</param>
 /// <returns>Outgoing stream handler.</returns>
 /// <remarks>
 /// voiceInfo.sourceSamplingRate and voiceInfo.SamplingRate may do not match. Automatic resampling will occur in this case.
 /// </remarks>
 public Voice.LocalVoice CreateLocalVoiceAudioFromSource(Voice.VoiceInfo voiceInfo, Voice.IAudioDesc source, bool forceShort = false, int channelId = ChannelAuto, IEncoder encoder = null)
 {
     if (source is Voice.IAudioPusher <float> )
     {
         if (forceShort)
         {
             var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, channelId, encoder);
             // we can safely reuse the same buffer in callbacks from native code
             //
             var bufferFactory = new FactoryReusableArray <float>(0);
             ((Voice.IAudioPusher <float>)source).SetCallback(buf => {
                 var shortBuf = localVoice.BufferFactory.New(buf.Length);
                 AudioUtil.Convert(buf, shortBuf, buf.Length);
                 localVoice.PushDataAsync(shortBuf);
             }, bufferFactory);
             return(localVoice);
         }
         else
         {
             var localVoice = CreateLocalVoiceAudio <float>(voiceInfo, channelId, encoder);
             ((Voice.IAudioPusher <float>)source).SetCallback(buf => localVoice.PushDataAsync(buf), localVoice.BufferFactory);
             return(localVoice);
         }
     }
     else if (source is Voice.IAudioPusher <short> )
     {
         var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, channelId, encoder);
         ((Voice.IAudioPusher <short>)source).SetCallback(buf => localVoice.PushDataAsync(buf), localVoice.BufferFactory);
         return(localVoice);
     }
     else if (source is Voice.IAudioReader <float> )
     {
         if (forceShort)
         {
             transport.LogInfo("[PV] Creating local voice with source samples type conversion from float to short.");
             var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, channelId, encoder);
             localVoice.LocalUserServiceable = new Voice.BufferReaderPushAdapterAsyncPoolFloatToShort(localVoice, source as Voice.IAudioReader <float>);
             return(localVoice);
         }
         else
         {
             var localVoice = CreateLocalVoiceAudio <float>(voiceInfo, channelId, encoder);
             localVoice.LocalUserServiceable = new Voice.BufferReaderPushAdapterAsyncPool <float>(localVoice, source as Voice.IAudioReader <float>);
             return(localVoice);
         }
     }
     else if (source is Voice.IAudioReader <short> )
     {
         var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, channelId, encoder);
         localVoice.LocalUserServiceable = new Voice.BufferReaderPushAdapterAsyncPool <short>(localVoice, source as Voice.IAudioReader <short>);
         return(localVoice);
     }
     else
     {
         transport.LogError("[PV] CreateLocalVoiceAudioFromSource does not support Voice.IAudioDesc of type {0}", source.GetType());
         return(Voice.LocalVoiceAudioDummy.Dummy);
     }
 }
예제 #2
0
        /// <summary>
        /// Creates outgoing audio stream of type automatically assigned and adds procedures (callback or serviceable) for consuming given audio source data.
        /// Adds audio specific features (e.g. resampling, level meter) to processing pipeline and to returning stream handler.
        /// </summary>
        /// <param name="voiceInfo">Outgoing audio stream parameters. Set applicable fields to read them by encoder and by receiving client when voice created.</param>
        /// <param name="source">Streaming audio source.</param>
        /// <param name="sampleType">Voice's audio sample type. If does not match source audio sample type, conversion will occur.</param>
        /// <param name="channelId">Transport channel specific to transport.</param>
        /// <param name="encoder">Audio encoder. Set to null to use default Opus encoder.</param>
        /// <returns>Outgoing stream handler.</returns>
        /// <remarks>
        /// audioSourceDesc.SamplingRate and voiceInfo.SamplingRate may do not match. Automatic resampling will occur in this case.
        /// </remarks>
        public LocalVoice CreateLocalVoiceAudioFromSource(VoiceInfo voiceInfo, IAudioDesc source, AudioSampleType sampleType, IEncoder encoder = null, int channelId = 0)
        {
            // resolve AudioSampleType.Source to concrete type for encoder creation
            if (sampleType == AudioSampleType.Source)
            {
                if (source is IAudioPusher <float> || source is IAudioReader <float> )
                {
                    sampleType = AudioSampleType.Float;
                }
                else if (source is IAudioPusher <short> || source is IAudioReader <short> )
                {
                    sampleType = AudioSampleType.Short;
                }
            }

            if (encoder == null)
            {
                switch (sampleType)
                {
                case AudioSampleType.Float:
                    encoder = Platform.CreateDefaultAudioEncoder <float>(transport, voiceInfo);
                    break;

                case AudioSampleType.Short:
                    encoder = Platform.CreateDefaultAudioEncoder <short>(transport, voiceInfo);
                    break;
                }
            }

            if (source is IAudioPusher <float> )
            {
                if (sampleType == AudioSampleType.Short)
                {
                    transport.LogInfo("[PV] Creating local voice with source samples type conversion from IAudioPusher float to short.");
                    var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, source, encoder, channelId);
                    // we can safely reuse the same buffer in callbacks from native code
                    //
                    var bufferFactory = new FactoryReusableArray <float>(0);
                    ((IAudioPusher <float>)source).SetCallback(buf => {
                        var shortBuf = localVoice.BufferFactory.New(buf.Length);
                        AudioUtil.Convert(buf, shortBuf, buf.Length);
                        localVoice.PushDataAsync(shortBuf);
                    }, bufferFactory);
                    return(localVoice);
                }
                else
                {
                    var localVoice = CreateLocalVoiceAudio <float>(voiceInfo, source, encoder, channelId);
                    ((IAudioPusher <float>)source).SetCallback(buf => localVoice.PushDataAsync(buf), localVoice.BufferFactory);
                    return(localVoice);
                }
            }
            else if (source is IAudioPusher <short> )
            {
                if (sampleType == AudioSampleType.Float)
                {
                    transport.LogInfo("[PV] Creating local voice with source samples type conversion from IAudioPusher short to float.");
                    var localVoice = CreateLocalVoiceAudio <float>(voiceInfo, source, encoder, channelId);
                    // we can safely reuse the same buffer in callbacks from native code
                    //
                    var bufferFactory = new FactoryReusableArray <short>(0);
                    ((IAudioPusher <short>)source).SetCallback(buf =>
                    {
                        var floatBuf = localVoice.BufferFactory.New(buf.Length);
                        AudioUtil.Convert(buf, floatBuf, buf.Length);
                        localVoice.PushDataAsync(floatBuf);
                    }, bufferFactory);
                    return(localVoice);
                }
                else
                {
                    var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, source, encoder, channelId);
                    ((IAudioPusher <short>)source).SetCallback(buf => localVoice.PushDataAsync(buf), localVoice.BufferFactory);
                    return(localVoice);
                }
            }
            else if (source is IAudioReader <float> )
            {
                if (sampleType == AudioSampleType.Short)
                {
                    transport.LogInfo("[PV] Creating local voice with source samples type conversion from IAudioReader float to short.");
                    var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, source, encoder, channelId);
                    localVoice.LocalUserServiceable = new BufferReaderPushAdapterAsyncPoolFloatToShort(localVoice, source as IAudioReader <float>);
                    return(localVoice);
                }
                else
                {
                    var localVoice = CreateLocalVoiceAudio <float>(voiceInfo, source, encoder, channelId);
                    localVoice.LocalUserServiceable = new BufferReaderPushAdapterAsyncPool <float>(localVoice, source as IAudioReader <float>);
                    return(localVoice);
                }
            }
            else if (source is IAudioReader <short> )
            {
                if (sampleType == AudioSampleType.Float)
                {
                    transport.LogInfo("[PV] Creating local voice with source samples type conversion from IAudioReader short to float.");
                    var localVoice = CreateLocalVoiceAudio <float>(voiceInfo, source, encoder, channelId);
                    localVoice.LocalUserServiceable = new BufferReaderPushAdapterAsyncPoolShortToFloat(localVoice, source as IAudioReader <short>);
                    return(localVoice);
                }
                else
                {
                    var localVoice = CreateLocalVoiceAudio <short>(voiceInfo, source, encoder, channelId);
                    localVoice.LocalUserServiceable = new BufferReaderPushAdapterAsyncPool <short>(localVoice, source as IAudioReader <short>);
                    return(localVoice);
                }
            }
            else
            {
                transport.LogError("[PV] CreateLocalVoiceAudioFromSource does not support Voice.IAudioDesc of type {0}", source.GetType());
                return(LocalVoiceAudioDummy.Dummy);
            }
        }