2010-10-06 27 views
5

Tôi đang sử dụng System.Speech.Synthesis.SpeechSynthesizer để chuyển đổi văn bản sang lời nói. Và do tài liệu thiếu máu của Microsoft (xem liên kết của tôi, không có nhận xét hoặc ví dụ mã) Tôi đang gặp khó khăn khi tạo đầu hoặc đuôi của sự khác biệt giữa hai phương pháp:TTS để phát trực tuyến bằng SpeechAudioFormatInfo sử dụng SpeechSynthesizer

SetOutputToAudioStream và SetOutputToWaveStream.

Dưới đây là những gì tôi đã suy luận:

SetOutputToAudioStream mất một dòng suối và một trường hợp SpeechAudioFormatInfo định nghĩa các định dạng của tập tin sóng (mẫu mỗi giây, bit mỗi giây, kênh âm thanh, vv) và viết các văn bản để dong chảy.

SetOutputToWaveStream chỉ lấy một luồng và ghi tệp sóng 16 bit, mono, 22kHz, PCM vào luồng. Không có cách nào để vượt qua trong SpeechAudioFormatInfo.

Sự cố của tôi là SetOutputToAudioStream không ghi tệp sóng hợp lệ vào luồng. Ví dụ tôi nhận được một InvalidOperationException ("Tiêu đề sóng bị hỏng") khi truyền luồng tới System.Media.SoundPlayer. Nếu tôi ghi luồng vào đĩa và cố gắng phát nó bằng WMP, tôi nhận được lỗi "Windows Media Player không thể phát tệp ..." nhưng luồng được viết bởi SetOutputToWaveStream phát đúng cách trong cả hai. Lý thuyết của tôi là SetOutputToAudioStream không viết tiêu đề (hợp lệ).

Quy ước đặt tên kỳ lạ cho SetOutputTo * Blah * không nhất quán. SetOutputToWaveFile mất một SpeechAudioFormatInfo trong khi SetOutputToWaveStream thì không.

Tôi cần có khả năng ghi một tệp sóng 8kHz, 16 bit, đơn luồng tới luồng, thứ gì đó mà cả SetOutputToAudioStream hoặc SetOutputToWaveStream đều không cho phép tôi thực hiện. Có ai có cái nhìn sâu sắc vào SpeechSynthesizer và hai phương pháp này?

Để tham khảo, đây là một số mã:

Stream ret = new MemoryStream(); 
using (SpeechSynthesizer synth = new SpeechSynthesizer()) 
{ 
    synth.SelectVoice(voiceName); 
    synth.SetOutputToWaveStream(ret); 
    //synth.SetOutputToAudioStream(ret, new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Sixteen, AudioChannel.Mono)); 
    synth.Speak(textToSpeak); 
} 

Giải pháp:

Nhiều nhờ @Hans passant, đây là ý chính của những gì tôi đang sử dụng hiện nay:

Stream ret = new MemoryStream(); 
using (SpeechSynthesizer synth = new SpeechSynthesizer()) 
{ 
    var mi = synth.GetType().GetMethod("SetOutputStream", BindingFlags.Instance | BindingFlags.NonPublic); 
    var fmt = new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Sixteen, AudioChannel.Mono); 
    mi.Invoke(synth, new object[] { ret, fmt, true, true }); 
    synth.SelectVoice(voiceName); 
    synth.Speak(textToSpeak); 
} 
return ret; 

Đối với thử nghiệm thô của tôi nó hoạt động tuyệt vời, mặc dù sử dụng phản ánh là một chút icky nó tốt hơn so với ghi các tập tin vào đĩa và mở một dòng.

Trả lời

6

Đoạn mã của bạn được borked, bạn đang sử dụng synth sau khi nó được xử lý. Nhưng đó không phải là vấn đề thực sự tôi chắc chắn. SetOutputToAudioStream tạo ra âm thanh PCM thô, 'số'. Không có định dạng tệp vùng chứa (tiêu đề) như những gì được sử dụng trong tệp .wav. Có, không thể phát lại bằng chương trình truyền thông thông thường.

Tình trạng quá tải bị thiếu cho SetOutputToWaveStream mất một SpeechAudioFormatInfo là lạ. Nó thực sự trông giống như một sự giám sát đối với tôi, mặc dù điều đó cực kỳ hiếm hoi trong .NET framework. Không có lý do thuyết phục nào tại sao nó không hoạt động, giao diện SAPI cơ bản không hỗ trợ nó. Nó có thể bị tấn công xung quanh với sự phản chiếu để gọi phương thức SetOutputStream riêng. Điều này làm việc tốt khi tôi thử nghiệm nó nhưng tôi không thể xác minh cho nó:

using System.Reflection; 
... 
      using (Stream ret = new MemoryStream()) 
      using (SpeechSynthesizer synth = new SpeechSynthesizer()) { 
       var mi = synth.GetType().GetMethod("SetOutputStream", BindingFlags.Instance | BindingFlags.NonPublic); 
       var fmt = new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Eight, AudioChannel.Mono); 
       mi.Invoke(synth, new object[] { ret, fmt, true, true }); 
       synth.Speak("Greetings from stack overflow"); 
       // Testing code: 
       using (var fs = new FileStream(@"c:\temp\test.wav", FileMode.Create, FileAccess.Write, FileShare.None)) { 
        ret.Position = 0; 
        byte[] buffer = new byte[4096]; 
        for (;;) { 
         int len = ret.Read(buffer, 0, buffer.Length); 
         if (len == 0) break; 
         fs.Write(buffer, 0, len); 
        } 
       } 
      } 

Nếu bạn không thoải mái với việc hack sau đó sử dụng Path.GetTempFileName() để tạm thời chuyển nó đến một tập tin chắc chắn sẽ làm việc.

+0

Hãy suy nghĩ về điều đó, đối số cuối cùng có thể là sai để nó không đóng luồng. Sẽ không quan trọng cho một MemoryStream mặc dù. –

+0

Bạn nói đúng, synth.Speak() đã được sử dụng trong mã của tôi. Tôi đã chỉnh sửa đoạn mã. Tôi sẽ cung cấp cho mã của bạn một shot, có vẻ như nó sẽ thực hiện những gì tôi yêu cầu. Tôi đồng ý rằng nó trông giống như một sự giám sát. Cảm ơn! – AceJordin

Các vấn đề liên quan