public static PlayItem DecodeToPlayItem(byte[] audioFileBytes)
        {
            var item = new PlayItem();

            // Audio file bytes into memory stream.
            using (var stream = new MemoryStream(audioFileBytes))
            {
                // Load existing XML and WAV data into PlayItem.
                using (var ms = new MemoryStream())
                {
                    // Loading stream into decoder.
                    using (var ad = new SharpDX.MediaFoundation.AudioDecoder(stream))
                    {
                        var samples    = ad.GetSamples();
                        var enumerator = samples.GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            var sample = enumerator.Current.ToArray();
                            ms.Write(sample, 0, sample.Length);
                        }
                        // Read WAV head.
                        item.WavHead = ad.WaveFormat;
                        // Read WAV data.
                        item.WavData  = ms.ToArray();
                        item.Duration = (int)ad.Duration.TotalMilliseconds;
                    }
                }
            }
            item.Status = JobStatusType.Synthesized;
            return(item);
        }
Exemple #2
0
        public static void AddMessageToPlay(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return;
            }

            if (text.StartsWith("<message"))
            {
                // Insert name="Undefined" attribute if not submited.
                text = !text.Contains("name=") ? text.Replace("<message", "<message name=\"Undefined\"") : text;

                var voiceItem = (VoiceListItem)Activator.CreateInstance(Program.MonitorItem.GetType());
                voiceItem.Load(text);
                addVoiceListItem(voiceItem);
            }
            else
            {
                var item = new PlayItem()
                {
                    Text   = "SAPI XML",
                    Xml    = text,
                    Status = JobStatusType.Parsed,
                };
                lock (playlistLock)
                { playlist.Add(item); }
            }
        }
Exemple #3
0
        public static void AddIntroSoundToPlayList(string text, string group, Stream stream)
        {
            var item = new PlayItem()
            {
                Text       = text,
                WavData    = new byte[0],
                StreamData = stream,
                Group      = group,
                Status     = JobStatusType.Pitched,
            };

            lock (playlistLock) { playlist.Add(item); }
        }
Exemple #4
0
 public static void Write(PlayItem item, FileInfo wavFi)
 {
     // Create directory if not exists.
     if (!wavFi.Directory.Exists)
     {
         wavFi.Directory.Create();
     }
     if (item == null)
     {
         throw new ArgumentNullException(nameof(item));
     }
     if (wavFi == null)
     {
         throw new ArgumentNullException(nameof(wavFi));
     }
     using (var stream = new FileStream(wavFi.FullName, FileMode.Create))
         Write(item, stream);
 }
Exemple #5
0
        static void ApplyPitch(PlayItem item)
        {
            // Get info about the WAV.
            int sampleRate    = item.WavHead.SampleRate;
            int bitsPerSample = item.WavHead.BitsPerSample;
            int channelCount  = item.WavHead.Channels;
            // Get info about effects and pitch.
            var ms     = new MemoryStream();
            var writer = new System.IO.BinaryWriter(ms);
            var bytes  = item.WavData;
            // Add 100 milliseconds at the start.
            var silenceStart = 100;
            // Add 200 milliseconds at the end.
            var silenceEnd   = 200;
            var silenceBytes = AudioHelper.GetSilenceByteCount(sampleRate, bitsPerSample, channelCount, silenceStart + silenceEnd);

            // Comment WriteHeader(...) line, because SharpDX don't need that (it creates noise).
            //AudioHelper.WriteHeader(writer, bytes.Length + silenceBytes, channelCount, sampleRate, bitsPerSample);
            if (ApplyEffects)
            {
                token = new CancellationTokenSource();
                // This part could take long time.
                bytes = EffectsGeneral.ApplyPitchShift(bytes, channelCount, sampleRate, bitsPerSample, PitchShift, token);
                // If pitch shift was canceled then...
                if (token.IsCancellationRequested)
                {
                    return;
                }
            }
            // Add silence at the start to make room for effects.
            Audio.AudioHelper.WriteSilenceBytes(writer, sampleRate, bitsPerSample, channelCount, silenceStart);
            writer.Write(bytes);
            // Add silence at the back to make room for effects.
            Audio.AudioHelper.WriteSilenceBytes(writer, sampleRate, bitsPerSample, channelCount, silenceEnd);
            // Add result to play list.
            item.WavData = ms.ToArray();
            //System.IO.File.WriteAllBytes("Temp.wav", item.Data);
            var duration = ((decimal)bytes.Length * 8m) / (decimal)channelCount / (decimal)sampleRate / (decimal)bitsPerSample * 1000m;

            duration     += (silenceStart + silenceEnd);
            item.Duration = (int)duration;
        }
        /// <summary>
        /// Convert XML to WAV bytes. WAV won't have the header, so you have to add it separately.
        /// </summary>
        static void ConvertXmlToWav(PlayItem item)
        {
            var query = System.Web.HttpUtility.ParseQueryString(item.VoiceSourceKeys ?? "");
            // Default is local.
            var source = VoiceSource.Local;

            Enum.TryParse(query[InstalledVoiceEx._KeySource], true, out source);
            var voiceId = query[InstalledVoiceEx._KeyVoiceId];

            byte[] wavBytes;
            if (source == VoiceSource.Amazon)
            {
                var region = query[InstalledVoiceEx._KeyRegion];
                var engine = query[InstalledVoiceEx._KeyEngine];
                var client = new Voices.AmazonPolly(
                    SettingsManager.Options.AmazonAccessKey,
                    SettingsManager.Options.AmazonSecretKey,
                    region
                    );
                wavBytes = client.SynthesizeSpeech(voiceId, item.Xml, Amazon.Polly.OutputFormat.Mp3, engine);
                if (wavBytes != null && wavBytes.Length > 0)
                {
                    var pi = DecodeToPlayItem(wavBytes);
                    item.WavHead  = pi.WavHead;
                    item.WavData  = pi.WavData;
                    item.Duration = pi.Duration;
                    pi.Dispose();
                }
            }
            else
            {
                wavBytes = ConvertSsmlXmlToWav(voiceId, item.Xml, item.WavHead);
                if (wavBytes != null && wavBytes.Length > 0)
                {
                    item.WavData  = wavBytes;
                    item.Duration = AudioHelper.GetDuration(wavBytes.Length, item.WavHead.SampleRate, item.WavHead.BitsPerSample, item.WavHead.Channels);
                }
            }
        }
Exemple #7
0
        public static void Write(PlayItem item, Stream stream)
        {
            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }
            var headBytes = GetWavHead(
                item.WavData.Length,
                item.WavHead.SampleRate,
                item.WavHead.BitsPerSample,
                item.WavHead.Channels
                );

            // Write WAV head.
            stream.Write(headBytes, 0, headBytes.Length);
            // Write WAV data.
            stream.Write(item.WavData, 0, item.WavData.Length);
        }
Exemple #8
0
 public static void AddMessageToPlay(string text)
 {
     if (!string.IsNullOrEmpty(text))
     {
         if (text.StartsWith("<message"))
         {
             var voiceItem = (VoiceListItem)Activator.CreateInstance(Program.MonitorItem.GetType());
             voiceItem.Load(text);
             addVoiceListItem(voiceItem);
         }
         else
         {
             var item = new PlayItem()
             {
                 Text   = "SAPI XML",
                 Xml    = text,
                 Status = JobStatusType.Parsed,
             };
             lock (playlistLock) { playlist.Add(item); }
         }
     }
 }
        /// <summary>
        /// Thread which will process all play items and convert XML to WAV bytes.
        /// </summary>
        /// <param name="status"></param>
        static void ProcessPlayItems(object status)
        {
            while (true)
            {
                PlayItem item = null;
                lock (threadIsRunningLock)
                {
                    lock (playlistLock)
                    {
                        // Get first incomplete item in the list.
                        JobStatusType[] validStates = { JobStatusType.Parsed, JobStatusType.Synthesized };
                        item = playlist.FirstOrDefault(x => validStates.Contains(x.Status));
                        // If nothing to do then...
                        if (item == null || playlist.Any(x => x.Status == JobStatusType.Error))
                        {
                            // Exit thread.
                            threadIsRunning = false;
                            return;
                        }
                    }
                }
                try
                {
                    // If XML is available.
                    if (item.Status == JobStatusType.Parsed)
                    {
                        item.Status = JobStatusType.Synthesizing;
                        var      encoding   = System.Text.Encoding.UTF8;
                        var      synthesize = true;
                        FileInfo xmlFi      = null;
                        FileInfo wavFi      = null;
                        if (SettingsManager.Options.CacheDataRead)
                        {
                            var dir = MainHelper.GetCreateCacheFolder();

                            // Look for generalized file first.
                            var uniqueName = item.GetUniqueFilePath(true);
                            // Get XML file path.
                            var xmlFile     = string.Format("{0}.xml", uniqueName);
                            var xmlFullPath = Path.Combine(dir.FullName, xmlFile);
                            xmlFi = new FileInfo(xmlFullPath);
                            // If generalized file do not exists then...
                            if (!xmlFi.Exists)
                            {
                                // Look for normal file.
                                uniqueName = item.GetUniqueFilePath(false);
                                // Get XML file path.
                                xmlFile     = string.Format("{0}.xml", uniqueName);
                                xmlFullPath = Path.Combine(dir.FullName, xmlFile);
                                xmlFi       = new FileInfo(xmlFullPath);
                            }
                            // Prefer MP3 audio file first (custom recorded file).
                            var wavFile     = string.Format("{0}.mp3", uniqueName);
                            var wavFullPath = Path.Combine(dir.FullName, wavFile);
                            wavFi = new FileInfo(wavFullPath);
                            // If wav do not exist or file is invalid.
                            if (!wavFi.Exists || wavFi.Length == 0)
                            {
                                // Get WAV file path.
                                wavFile     = string.Format("{0}.wav", uniqueName);
                                wavFullPath = Path.Combine(dir.FullName, wavFile);
                                wavFi       = new FileInfo(wavFullPath);
                            }
                            // If both files exists and Wav file is valid then...
                            if (xmlFi.Exists && wavFi.Exists && wavFi.Length > 0)
                            {
                                using (Stream stream = new FileStream(wavFi.FullName, FileMode.Open, FileAccess.Read))
                                {
                                    // Load existing XML and WAV data into PlayItem.
                                    var ms         = new MemoryStream();
                                    var ad         = new SharpDX.MediaFoundation.AudioDecoder(stream);
                                    var samples    = ad.GetSamples();
                                    var enumerator = samples.GetEnumerator();
                                    while (enumerator.MoveNext())
                                    {
                                        var sample = enumerator.Current.ToArray();
                                        ms.Write(sample, 0, sample.Length);
                                    }
                                    // Read WAV head.
                                    item.WavHead = ad.WaveFormat;
                                    // Read WAV data.
                                    item.WavData  = ms.ToArray();
                                    item.Duration = (int)ad.Duration.TotalMilliseconds;
                                }
                                // Load XML.
                                item.Xml = System.IO.File.ReadAllText(xmlFi.FullName);
                                // Make sure WAV data is not synthesized.
                                synthesize = false;
                            }
                        }
                        if (synthesize)
                        {
                            item.WavHead = new SharpDX.Multimedia.WaveFormat(
                                SettingsManager.Options.AudioSampleRate,
                                SettingsManager.Options.AudioBitsPerSample,
                                (int)SettingsManager.Options.AudioChannels);
                            // WavHead could change.
                            ConvertXmlToWav(item);
                        }
                        if (item.WavData != null)
                        {
                            var applyRate   = SettingsManager.Options.ModifyLocallyRate && _Rate != 0;
                            var applyPitch  = SettingsManager.Options.ModifyLocallyPitch && _Pitch != 0;
                            var applyVolume = SettingsManager.Options.ModifyLocallyVolume && item.Volume < 100;
                            if (applyRate || applyPitch)
                            {
                                var parameters = new SoundStretch.RunParameters();
                                parameters.TempoDelta = GetTempoDeltaFromRate(_Rate);
                                parameters.PitchDelta = (float)_Pitch;
                                parameters.Speech     = true;
                                var inStream = new MemoryStream();
                                AudioHelper.Write(item, inStream);
                                inStream.Position = 0;
                                var outStream = new MemoryStream();
                                SoundStretch.SoundTouchHelper.Process(inStream, outStream, parameters);
                                var outBytes = outStream.ToArray();
                                var pi       = DecodeToPlayItem(outBytes);
                                item.WavHead  = pi.WavHead;
                                item.WavData  = pi.WavData;
                                item.Duration = pi.Duration;
                                pi.Dispose();
                                inStream.Dispose();
                                outStream.Dispose();
                            }
                            if (applyVolume)
                            {
                                var inStream = new MemoryStream();
                                AudioHelper.Write(item, inStream);
                                inStream.Position = 0;
                                var vol       = (float)item.Volume / 100f;
                                var outStream = new MemoryStream();
                                AudioHelper.ChangeVolume(vol, inStream, outStream);
                                var outBytes = outStream.ToArray();
                                var pi       = DecodeToPlayItem(outBytes);
                                item.WavHead  = pi.WavHead;
                                item.WavData  = pi.WavData;
                                item.Duration = pi.Duration;
                                pi.Dispose();
                                inStream.Dispose();
                                outStream.Dispose();
                            }
                            if (SettingsManager.Options.CacheDataWrite || SettingsManager.Options.CacheAudioConvert)
                            {
                                // Create directory if not exists.
                                if (!xmlFi.Directory.Exists)
                                {
                                    xmlFi.Directory.Create();
                                }
                                if (!xmlFi.Exists)
                                {
                                    // Write XML.
                                    System.IO.File.WriteAllText(xmlFi.FullName, item.Xml, encoding);
                                }
                            }
                            // If data was synthesized i.e. was not loaded from the file then...
                            if (synthesize && SettingsManager.Options.CacheDataWrite)
                            {
                                AudioHelper.Write(item, wavFi);
                            }
                            // If must convert data to other formats.
                            if (SettingsManager.Options.CacheAudioConvert)
                            {
                                AudioHelper.Convert(item, wavFi);
                            }
                        }
                        item.Status = (item.WavHead == null || item.WavData == null)
                                                        ? item.Status = JobStatusType.Error
                                                        : item.Status = JobStatusType.Synthesized;
                    }
                    if (item.Status == JobStatusType.Synthesized)
                    {
                        item.Status = JobStatusType.Pitching;
                        ApplyPitch(item);
                        item.Status = JobStatusType.Pitched;
                    }
                }
                catch (Exception ex)
                {
                    OnEvent(Exception, ex);
                    item.Status = JobStatusType.Error;
                    // Exit thread.
                    threadIsRunning = false;
                    return;
                }
            }
        }
        public static List <PlayItem> AddTextToPlaylist(string game, string text, bool addToPlaylist, string voiceGroup,
                                                        // Optional properties for NPC character.
                                                        string name   = null,
                                                        string gender = null,
                                                        string effect = null,
                                                        int volume    = 100,
                                                        // Optional propertied for player character
                                                        string playerName        = null,
                                                        string playerNameChanged = null,
                                                        string playerClass       = null,
                                                        // Keys which will be used to which voice source (Local, Amazon or Google).
                                                        string voiceSourceKeys = null
                                                        )
        {
            MessageGender _gender;

            Enum.TryParse(gender, out _gender);
            // It will take too long to convert large amount of text to WAV data and apply all filters.
            // This function will split text into smaller sentences.
            var cs    = "[comment]";
            var ce    = "[/comment]";
            var items = new List <PlayItem>();
            // Split sentences if option is enabled.
            var splitItems = SettingsManager.Options.SplitMessageIntoSentences
                                ? MainHelper.SplitText(text, new string[] { ". ", "! ", "? ", cs, ce })
                                : MainHelper.SplitText(text, new string[] { cs, ce });
            var  sentences = splitItems.Where(x => (x.Value + x.Key).Trim().Length > 0).ToArray();
            bool comment   = false;

            // Loop trough each sentence.
            for (int i = 0; i < sentences.Length; i++)
            {
                var block = sentences[i];
                // Combine sentence and separator.
                var sentence = block.Value + block.Key.Replace(cs, "").Replace(ce, "") + "";
                if (!string.IsNullOrEmpty(sentence.Trim('\r', '\n', ' ')))
                {
                    var item = new PlayItem();
                    item.Game = game;
                    // Set Player properties
                    item.PlayerName        = playerName;
                    item.PlayerNameChanged = playerNameChanged;
                    item.PlayerClass       = playerClass;
                    // Set NPC properties.
                    item.Name   = name;
                    item.Gender = _gender;
                    item.Effect = effect;
                    item.Volume = volume;
                    // Set data properties.
                    item.Status          = JobStatusType.Parsed;
                    item.IsComment       = comment;
                    item.Group           = voiceGroup;
                    item.VoiceSourceKeys = voiceSourceKeys;
                    item.Text            = sentence;
                    if (SettingsManager.Options.CacheDataGeneralize)
                    {
                        item.Text = item.GetGeneralizedText();
                    }
                    item.Xml = ConvertTextToXml(item.Text, comment, volume);
                    items.Add(item);
                    if (addToPlaylist)
                    {
                        lock (playlistLock) { playlist.Add(item); }
                    }
                }
                ;
                // If comment started.
                if (block.Key == cs)
                {
                    comment = true;
                }
                // If comment ended.
                if (block.Key == ce)
                {
                    comment = false;
                }
            }
            return(items);
        }
Exemple #11
0
        public static void Convert(PlayItem item, FileInfo wavFi)
        {
            if (wavFi == null)
            {
                throw new ArgumentNullException(nameof(wavFi));
            }
            var    convertFormat = SettingsManager.Options.CacheAudioFormat;
            var    fileName      = Path.GetFileNameWithoutExtension(wavFi.FullName);
            string fullName;

            if (convertFormat == CacheFileFormat.ULaw || convertFormat == CacheFileFormat.ALaw)
            {
                fullName = Path.Combine(wavFi.Directory.FullName, fileName + "." + convertFormat.ToString().ToLower() + ".wav");
            }
            else if (convertFormat == CacheFileFormat.MP3)
            {
                fullName = Path.Combine(wavFi.Directory.FullName, fileName + ".copy.mp3");
            }
            else if (convertFormat == CacheFileFormat.WAV)
            {
                fullName = Path.Combine(wavFi.Directory.FullName, fileName + ".copy.wav");
            }
            else
            {
                // Do nothing if format do not match.
                return;
            }
            // Exit if file already exists.
            if (File.Exists(fullName))
            {
                return;
            }
            // Write whole WAV (head and data) into one stream.
            var source = new MemoryStream();

            Write(item, source);
            source.Position = 0;
            // Create directory if not exists.
            if (!wavFi.Directory.Exists)
            {
                wavFi.Directory.Create();
            }
            // https://www.codeproject.com/Articles/501521/How-to-convert-between-most-audio-formats-in-NET
            source.Position = 0;
            var reader = new WaveFileReader(source);

            if (convertFormat == CacheFileFormat.ULaw || convertFormat == CacheFileFormat.ALaw)
            {
                // The ACM mu-law encoder expects its input to be 16 bit.
                // If you're working with mu or a-law, the sample rate is likely to be low as well.
                // The following two lines of code will create a zero-length stream of PCM 16 bit and
                // pass it into a WaveFormatConversionStream to convert it to a-law.
                // It should not throw a "conversion not possible" error unless for some reason you don't have the G.711 encoder installed on your machine.
                var wavFormat = convertFormat == CacheFileFormat.ULaw
                                                ? WaveFormatEncoding.MuLaw
                                                : WaveFormatEncoding.ALaw;
                var destinationFormat = WaveFormat.CreateCustomFormat(
                    wavFormat,
                    SettingsManager.Options.CacheAudioSampleRate,
                    (int)SettingsManager.Options.CacheAudioChannels,
                    SettingsManager.Options.CacheAudioAverageBitsPerSecond / 8,
                    SettingsManager.Options.CacheAudioBlockAlign,
                    SettingsManager.Options.CacheAudioBitsPerSample
                    );
                var conversionStream1 = new WaveFormatConversionStream(new WaveFormat(destinationFormat.SampleRate, 16, destinationFormat.Channels), reader);
                using (var conversionStream2 = new WaveFormatConversionStream(destinationFormat, conversionStream1))
                    WaveFileWriter.CreateWaveFile(fullName, conversionStream2);
                conversionStream1.Dispose();
            }
            else if (convertFormat == CacheFileFormat.MP3)
            {
                // If media foundation is not started then you will get this exception during MP3 encoding:
                // System.Runtime.InteropServices.COMException:
                // 'The request is invalid because Shutdown() has been called. (Exception from HRESULT: 0xC00D3E85)'
                MediaFoundationApi.Startup();
                MediaFoundationEncoder.EncodeToMp3(reader, fullName, SettingsManager.Options.CacheAudioAverageBitsPerSecond);
                MediaFoundationApi.Shutdown();
            }
            else if (convertFormat == CacheFileFormat.WAV)
            {
                var destWav = new WaveFormat(
                    SettingsManager.Options.CacheAudioSampleRate,
                    SettingsManager.Options.CacheAudioBitsPerSample,
                    (int)SettingsManager.Options.CacheAudioChannels
                    );
                using (var conversionStream2 = new WaveFormatConversionStream(destWav, reader))
                    WaveFileWriter.CreateWaveFile(fullName, conversionStream2);
            }
            reader.Dispose();
        }