Google Cloud Speech API streaming limit at 65 seconds, not 5 minutes - c#

I am using Google's Cloud Speech API in C# to stream input from a microphone and output text. It works well up to the point where it crashes at 65 seconds, with the error saying that there's a 65 minute request limit. This website https://cloud.google.com/speech-to-text/quotas says that my limit should be about 5 minutes. The function I used is below, and the seconds parameter is how long the stream is requested to last. Am I using old methods or could it be because I'm on the free trial version for the API key?
I've seen other Stack Overflow questions similar to this, and they link to the same website saying the streaming limit is supposed to be about one minute, yet the site has updated within the last year and a half but my limit is still one minute.
public static async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 16000,
LanguageCode = "en",
},
InterimResults = true,
}
});
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(
default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
if (alternative.Confidence != 0)
{
Console.WriteLine(alternative.Transcript);
}
}
}
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(16000, 1); //(hertz, channels)
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
waveIn.StartRecording();
Console.WriteLine("Speak now.");
await Task.Delay(TimeSpan.FromSeconds(seconds));
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
EDIT: A product manager from Google replied to my post on a Google discussion page. He said that the 5 minutes advertised is a mistake, yet they are planning on extending the streaming limit soon. Is there a clean way to make multiple streaming requests that are within the 65 second limit yet also doesn't miss the user's speaking or cut words in half between requests?

Related

Microsoft.CognitiveServices.Speech.DetailedSpeechRecognitionResultCollection error

We're experimenting with speech-to-text using Microsoft Cognitive Services. One of our requirements is to have word level timestamps. This works fine with short wav files, say, 2-3 minutes of audio, but with larger files we're getting an error:
"There was an error deserializing the object of type Microsoft.CognitiveServices.Speech.DetailedSpeechRecognitionResultCollection. The value '2152200000' cannot be parsed as the type 'Int32'."
Any and all hints as to how I can get around this would be greatly appreciated. Thanks in advance!
Code snippet:
config.OutputFormat = OutputFormat.Detailed;
config.RequestWordLevelTimestamps();
using (var audioInput = AudioConfig.FromWavFileInput(wavfile))
{
using var recognizer = new SpeechRecognizer(config, audioInput);
recognizer.Recognized += (s, e) =>
{
if (e.Result.Reason == ResultReason.RecognizedSpeech)
{
var framesStart = TimeSpan.FromTicks(e.Result.OffsetInTicks).TotalMilliseconds / 40;
var te = new TranscriptElement((long)framesStart, e.Result.Text, languageCode);
// Eventually fails on the following line:
var words = e.Result.Best().OrderByDescending(x => x.Confidence).First().Words;
foreach (var w in words.OrderBy(w => w.Offset))
{
var start = TimeSpan.FromTicks(w.Offset).TotalMilliseconds / 40;
var duration = TimeSpan.FromTicks(w.Duration).TotalMilliseconds / 40;
te.SingleWords.Add(new TranscriptSingleWord((long)start, (long)(start + duration), w.Word));
}
transcriptElements.Add(te);
}
else if (e.Result.Reason == ResultReason.NoMatch)
{
_logger.LogError($"NOMATCH: Speech could not be recognized.");
}
};
await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
Task.WaitAny(new[] { stopRecognition.Task });
await recognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}
It's a bug in the data type the extension is using for the offset. An int can only track ~214s of audio.
You can access the raw JSON that the Best() method is using from the result's property collection through the SpeechServiceResponse_JsonResult property until a fix is available.

How to call a AVPlayer in XAMARIN iOS during notification instead of 30 second max duration notification sound?

I need to play a audio file which is 3 minutes length. But default notification sound does not play more than 30 seconds. So my idea is Calling a Avplayer
which will play my desired audio. But i do not know how to call this. Can any one please help me. I will be very grateful.
I am attaching my notification method here.
public void AVPlayer()
{
NSUrl songURL;
if (!MusicOn) return;
//Song url from your local Resource
songURL = new NSUrl("azan.wav");
NSError err;
player = new AVAudioPlayer(songURL, "Song", out err);
player.Volume = MusicVolume;
player.FinishedPlaying += delegate {
// backgroundMusic.Dispose();
player = null;
};
//Background Music play
player.Play();
}
public void CreateRequest(JamatTime jamat)
{
// Create action
var actionID = "pause";
var title = "PAUSE";
var action = UNNotificationAction.FromIdentifier(actionID, title, UNNotificationActionOptions.None);
// Create category
var categoryID = "message";
var actions = new UNNotificationAction[] { action };
var intentIDs = new string[] { };
var categoryOptions = new UNNotificationCategoryOptions[] { };
var category = UNNotificationCategory.FromIdentifier(categoryID, actions, intentIDs, UNNotificationCategoryOptions.None);
// Register category
var categories = new UNNotificationCategory[] { category };
UNUserNotificationCenter.Current.SetNotificationCategories(new NSSet<UNNotificationCategory>(categories));
// Rebuild notification
var content = new UNMutableNotificationContent();
content.Title = " Jamat Time alert";
content.Badge = 1;
content.CategoryIdentifier = "message";`enter code here`
content.Sound = UNNotificationSound.GetSound("sample.wav");
var times = new string[] { jamat.Asr, jamat.Dhuhr, jamat.Faijr, jamat.Ishaa, jamat.Jumah, jamat.Maghib };
int id = 0;
foreach (var time in times)
{
var ndate = DateTime.ParseExact(time, "h:mm tt", null);
var date = new NSDateComponents()
{
Calendar = NSCalendar.CurrentCalendar,
Hour = ndate.Hour,
Minute = ndate.Minute,
Second = 0
};
content.UserInfo = new NSDictionary<NSString, NSString>(
new NSString[] {
(NSString)"time1",
(NSString)"time2"
},
new NSString[] {
(NSString)DateTime.Now.ToString("h:mm tt"),
(NSString)time
});
var trigger = UNCalendarNotificationTrigger.CreateTrigger(date, true);
// ID of Notification to be updated
var request = UNNotificationRequest.FromIdentifier(id++.ToString(), content, trigger);
// Add to system to modify existing Notification
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err1) =>
{
if (err1 != null)
{
Console.WriteLine("Error: {0}", err1);
}
Console.WriteLine($"Success: {request}");
});
}
}
You can't play an audio file instead of the UNNotificationSound.
There's no way to trigger the player's play method when the local notification comes. You could only configure the sound property using the code you post above. And the file should be embedded in the bundle resource.
It seems you are aware of UNNotificationSound: https://developer.apple.com/documentation/usernotifications/unnotificationsound?language=objc. But I still want to remind you of the file's format and length limitations.
Finally I have solved my problem.
When a notification fires then WillPresentNotification() method hits and I simply call the AVplayer there and perfectly working. If u want to play sound via UNNotificationSound then not possible because that is limited by 30 second duration..but problem this works only in foreground.

C#, difficulties interpreting how to use API wrapper

I'm working with an API wrapper for the first time and I'm not doing too hot.
I am not certain where it's going wrong so I'll keep it short but let me know if it's not enough code to determine what the problem is.
From the documentation:
public async Task<OrderBookResponse> GetOrderBook(string symbol, bool useCache = false, int limit = 100)
{
Guard.AgainstNull(symbol);
if (limit > 100)
{
throw new ArgumentException("When requesting the order book, you can't request more than 100 at a time.", nameof(limit));
}
return await _apiProcessor.ProcessGetRequest<OrderBookResponse>(Endpoints.MarketData.OrderBook(symbol, limit, useCache));
}
This is from an example program:
var orderBook = await client.GetOrderBook("ETHBTC", true);
And finally my own code and problem:
public async void fetchPrice()
{
using (WebClient w = new WebClient())
{
try
{
var trades = await client.GetOrderBook("BTCUSDT", false, 50);
Console.WriteLine(trades);
{more code}
}
}
}
Console.WriteLine says this instead of the fetched data
"BinanceExchange.API.Models.Response.OrderBookResponse".
Thanks!

C# Task.WaitAll isn't waiting

My aim is to download images from an Amazon Web Services bucket.
I have the following code function which downloads multiple images at once:
public static void DownloadFilesFromAWS(string bucketName, List<string> imageNames)
{
int batchSize = 50;
int maxDownloadMilliseconds = 10000;
List<Task> tasks = new List<Task>();
for (int i = 0; i < imageNames.Count; i++)
{
string imageName = imageNames[i];
Task task = Task.Run(() => GetFile(bucketName, imageName));
tasks.Add(task);
if (tasks.Count > 0 && tasks.Count % batchSize == 0)
{
Task.WaitAll(tasks.ToArray(), maxDownloadMilliseconds);//wait to download
tasks.Clear();
}
}
//if there are any left, wait for them
Task.WaitAll(tasks.ToArray(), maxDownloadMilliseconds);
}
private static void GetFile(string bucketName, string filename)
{
try
{
using (AmazonS3Client awsClient = new AmazonS3Client(Amazon.RegionEndpoint.EUWest1))
{
string key = Path.GetFileName(filename);
GetObjectRequest getObjectRequest = new GetObjectRequest() {
BucketName = bucketName,
Key = key
};
using (GetObjectResponse response = awsClient.GetObject(getObjectRequest))
{
string directory = Path.GetDirectoryName(filename);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
if (!File.Exists(filename))
{
response.WriteResponseStreamToFile(filename);
}
}
}
}
catch (AmazonS3Exception amazonS3Exception)
{
if (amazonS3Exception.ErrorCode == "NoSuchKey")
{
return;
}
if (amazonS3Exception.ErrorCode != null && (amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId") || amazonS3Exception.ErrorCode.Equals("InvalidSecurity")))
{
// Log AWS invalid credentials
throw new ApplicationException("AWS Invalid Credentials");
}
else
{
// Log generic AWS exception
throw new ApplicationException("AWS Exception: " + amazonS3Exception.Message);
}
}
catch
{
//
}
}
The downloading of the images all works fine but the Task.WaitAll seems to be ignored and the rest of the code continues to be executed - meaning I try to get files that are currently non existent (as they've not yet been downloaded).
I found this answer to another question which seems to be the same as mine. I tried to use the answer to change my code but it still wouldn't wait for all files to be downloaded.
Can anyone tell me where I am going wrong?
The code behaves as expected. Task.WaitAll returns after ten seconds even when not all files have been downloaded, because you have specified a timeout of 10 seconds (10000 milliseconds) in variable maxDownloadMilliseconds.
If you really want to wait for all downloads to finish, call Task.WaitAll without specifying a timeout.
Use
Task.WaitAll(tasks.ToArray());//wait to download
at both places.
To see some good explanations on how to implement parallel downloads while not stressing the system (only have a maximum number of parallel downloads), see the answer at How can I limit Parallel.ForEach?

SSDP (UDP) on Windows Store applications (.NET)

I am trying to implement a basic SSDP (UDP) broadcast/listener for a Windows Store application using C#.
I have found that Windows.Networking.Sockets contains the DatagramSocket class which is what I need to use for UDP networking.
However, my current attempts seem to execute just fine but have no results via Wireshark and do not get a response back from the devices on the network.
Here is the code I am currently using (and running through the RT Simulator):
public async static Task<IEnumerable<HueBridge>> DiscoverAsync(TimeSpan timeout)
{
if (timeout <= TimeSpan.Zero)
throw new ArgumentException("Timeout value must be greater than zero.", "timeout");
var discoveredBridges = new List<HueBridge>();
using (var socket = new DatagramSocket())
{
while (true)
{
var bridgeWasFound = false;
socket.MessageReceived += (sender, e) =>
{
var bpx = true; // breakpoint here for success
};
var multicastIP = new HostName("239.255.255.250");
await socket.BindServiceNameAsync("1900");
socket.JoinMulticastGroup(multicastIP);
using (var writer = new DataWriter(socket.OutputStream))
{
var request = new StringBuilder();
request.AppendLine("M-SEARCH * HTTP/1.1");
request.AppendLine("HOST: 239.255.255.250:1900");
request.AppendLine("MAN: ssdp:discover");
request.AppendLine("MX: 5");
request.AppendLine("ST: ssdp:all");
writer.WriteString(request.ToString());
await writer.FlushAsync();
}
if (timeout > TimeSpan.Zero)
await Task.Delay(timeout);
if (!bridgeWasFound)
break; // breakpoint here for failure check
}
}
return discoveredBridges;
}
Any ideas on what I may be doing incorrectly? I don't get an exception and I have the proper Capabilities set in the manifest. My breakpoint at the break always gets hit and I am using a timeout of 10 seconds.
Seems I have found the problem(s).
First, I should use socket.BindEndpointAsync(null, string.Empty) instead of socket.BindServiceNameAsync("1900"), which will properly listen for broadcast packets.
Secondly, writer.FlushAsync() does not write to the socket; however, writer.StoreAsync() does.
Here is the final result, which does work (almost) perfectly:
public async static Task<IEnumerable<HueBridge>> DiscoverAsync(TimeSpan timeout)
{
if (timeout <= TimeSpan.Zero)
throw new ArgumentException("Timeout value must be greater than zero.", "timeout");
var discoveredBridges = new List<HueBridge>();
var multicastIP = new HostName("239.255.255.250");
var bridgeWasFound = false;
using (var socket = new DatagramSocket())
{
socket.MessageReceived += (sender, e) =>
{
var reader = e.GetDataReader();
var bytesRemaining = reader.UnconsumedBufferLength;
var receivedString = reader.ReadString(bytesRemaining);
// TODO: Check for existing bridges, only add new ones to prevent infinite loop.
// TODO: Create new bridge and add to the list.
bridgeWasFound = true;
};
await socket.BindEndpointAsync(null, string.Empty);
socket.JoinMulticastGroup(multicastIP);
while (true)
{
bridgeWasFound = false;
using (var stream = await socket.GetOutputStreamAsync(multicastIP, "1900"))
using (var writer = new DataWriter(stream))
{
var request = new StringBuilder();
request.AppendLine("M-SEARCH * HTTP/1.1");
request.AppendLine("HOST: 239.255.255.250:1900");
request.AppendLine("MAN: ssdp:discover");
request.AppendLine("MX: 3");
request.AppendLine("ST: ssdp:all");
writer.WriteString(request.ToString());
await writer.StoreAsync();
if (timeout > TimeSpan.Zero)
await Task.Delay(timeout);
if (!bridgeWasFound)
break;
}
}
}
return discoveredBridges;
}
According Specifications :
MAN REQUIRED by HTTP Extension Framework. Unlike the NTS and ST field
values, the field value of the MAN header field is enclosed in double
quotes; it defines the scope (namespace) of the extension. MUST be
"ssdp:discover".
then your code
request.AppendLine("MAN: ssdp:discover");
must be
request.AppendLine("MAN: \"ssdp:discover\"");
Hope this help.

Categories

Resources