AVAudioSinkNode in Xamarin.iOS can't connect to InputNode - xamarin

Using Xamarin, I'd like to use an AVAudioSinkNode to store and eventually transfer incoming audio data from a mic at the lowest latency possible (without going straight into AudioUnits and the deprecated AUGraphs). See my commented code below where the SinkNode is connected to the default InputNode. It's giving me grief. I'm using Xamarin.Forms with a simple iOS dependency class. I can successfully hook up an InputNode through an fx node (Reverb for example) and on out to the OutputNode. In this case, I've minimized my code down to focus on the problem at hand:
public unsafe class AudioEngine : IAudioEngine
{
AVAudioEngine engine;
AVAudioInputNode input;
AVAudioSinkNode sink;
public AudioEngine()
{
ActivateAudioSession();
}
protected void ActivateAudioSession()
{
var session = AVAudioSession.SharedInstance();
session.SetCategory(AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers);
session.SetActive(true);
session.SetPreferredIOBufferDuration(0.0007, out error); // 32 byte buffer, if possible!
engine = new AVAudioEngine();
input = engine.InputNode; // to save on typing
input.Volume = 0.5f;
var format = input.GetBusInputFormat(0); // used for fx connections, but not used in this snippet. If I use this in the Input -> Sink connection, it crashes.
sink = new AVAudioSinkNode(sinkReceiverHandler);
engine.AttachNode(sink);
try
{
//-----------------------------------------------------
// Param #3 (format) is nil in all the Apple Documentation and multiple examples
// In place of nil, **NSNull.Null** isn't accepted.
// In place of nil, **null** throws a System.NullReferenceException. (see stack dump)
// In place of nil, using the **InputNode's format** crashes with
// something about missing the Trampolines.g.cs file... no clue...
engine.Connect(input, sink, **null**); // null doesn't work in place of nil.
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace); // Exception messages included below
}
engine.Prepare();
engine.StartAndReturnError(out error);
}
private unsafe int sinkReceiverHandler(AudioToolbox.AudioTimeStamp timeStamp, uint frames, ref AudioToolbox.AudioBuffers inputData)
{
// Do stuff with the data...
return 0;
}
}
I found a post related to the use of nil as a parameter in Xamarin.iOS that says the author of the library needs to include the [NullAllowed] argument:
How to assign something to nil using Xamarin.iOS
My question is: Am I missing something obvious, or is this an oversight in the Xamarin library definition? I always assume it's my lack of expertise, but if this is a bug, how do I go about reporting it to Xamarin?
A follow up question: If this is a glitch, is there a viable workaround? Can I go in and tweak the Xamarin library definition manually? (which would break on any updates, I'm sure.) Or can I make a little library using Swift which I then import into my Xamarin project?
Just trying to think of options. Thanks for reading! Below is the Stack dump when I use null as a substitute for nil (again... NSNull.Null isn't considered a valid type in this case. It just doesn't compile):
{System.NullReferenceException: Object reference not set to an instance of an object
at AVFoundation.AVAudioFormat.op_Equality (AVFoundation.AVAudioFormat a, AVFoundation.AVAudioFormat b) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.3.2/src/Xamarin.iOS/AVFoundation/AVAudioFormat.cs:27
at AVFoundation.AVAudioEngine.Connect (AVFoundation.AVAudioNode sourceNode, AVFoundation.AVAudioNode targetNode, AVFoundation.AVAudioFormat format) [0x00024] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.3.2/src/Xamarin.iOS/AVAudioEngine.g.cs:120
at udptest.iOS.AudioEngine.ActivateAudioSession () [0x0009b] in /Users/eludema/dev/xamarin/udptest/udptest.iOS/AudioEngine.cs:43 }
THANKS!

This has been confirmed as a bug: The format parameter is [NullAllowable], but the current code to actually process that null wasn't linked up in the wrapper code. Here's the issue tracker on the Xamarin.Mac/iOS github repo:
https://github.com/xamarin/xamarin-macios/issues/9267
Thanks for the github issue submission link, #SushiHangover!

Related

C++ CLI / TS3 Client crashes on plugin

Sooo...
I have written a plugin, and the whole plugin works fine.
ONLY PROBLEM:
My TS3 Client crashes.
To give a context:
(Warning: That code is just poorly pasted. On GitHub, it crashes at line 270 and 285)
// Helper Function
String^ getChannelName(uint64 serverConnectionHandlerID, uint64 channelID) {
char* tmp;
if (ts3Functions.getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &tmp) == ERROR_ok) {
return marshal_as<String^>(tmp);
}
else
{
return "ERROR_GETTING_CHANNELNAME";
}
}
void assemble_a() {
List<String^>^ clients;
List<String^>^ channel;
// Some middlepart here, but I made sure it works as it should
// And the actual part where it is crashing
if (resChL == ERROR_ok) {
for (int i = 0; channelListPtr[i]; ++i) {
String^ a = getChannelName(schid, channelListPtr[i]);
const char* b = (const char*)(Marshal::StringToHGlobalAnsi(a)).ToPointer();
ts3Functions.logMessage(b, LogLevel_DEBUG, "DEBUG_VC", schid);
if (String::IsNullOrEmpty(a) == false) {
channel->Add(a); // It crashes RIGHT at this point
}
}
}
}
So I am asking on the TS3 forum for a long time, got a lot of answers, and noone could tell me why it actually crashes, and I didn't manage to figure it out on my own either.
It does actually print the channel name [*spacer0]t but as soon as it should append it to the String List, it crashes.
It throws the message The thread has tried to write or read from a virtual address that it does not have the accesspermissions for.
I seriously have no idea what to do, trying to fix it now for over 2 weeks.
For full context: GitHub Sourcecode
Sorry if this question MIGHT be a little out of topic here (Is it? I don't know...) but I don't really know what to do with that problem anymore...
EDIT:
Errormessage from try/catch is:
System.NullReferebceException: The Objectreference was not set to the Objectinstance, occured in tsapi.assembleGrammar()
List<String^>^ channel;
...
channel->Add(a);
channel is null. You need to initialize it with something, probably gcnew List<String^>(). I'm not sure why you're getting an access denied message instead of NullReferenceException.
Other issues
Make sure you're handling all the unmanaged strings properly. For example, does getChannelVariableAsString require a call to explicitly free the buffer? Be absolutely sure to call FreeHGlobal to free the memory that StringToHGlobalAnsi allocated for you.

Custom HttpContent won't build - Could not AOT the assembly (mtouch)

In my efforts to create a progress indicator for uploading videos using HttpClient (SendAsync) in Xamarin Forms, I now have to ask for assistance.
The upload itself works fine, and all other API calls, but when I try to create a custom HttpContent to track the progress of the upload the project won't even build any more.
Error MT3001: Could not AOT the assembly
'[...].iOS/obj/iPhone/Debug/build-iphone7.2-10.1.1/mtouch-cache/Build/theproject.dll'
(MT3001) (theproject.iOS)
Using StreamContent or ByteArrayContent instead the project builds, but I can't get it working to track the progress.
A snippet of code (this is minimal example):
public class ProgressableContent : HttpContent
{
private const int defaultBufferSize = 4096;
private Stream content;
private int progress;
public ProgressableContent(Stream content)
{
this.content = content;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return Task.Run(async () =>
{
var buffer = new byte[defaultBufferSize];
var size = content.Length;
var uploaded = 0;
using (content) while (true)
{
var length = content.Read(buffer, 0, buffer.Length);
if (length <= 0) break;
uploaded += length;
progress = (int)((float)uploaded / size * 100);
await stream.WriteAsync(buffer, 0, length);
}
});
}
protected override bool TryComputeLength(out long length)
{
length = content.Length;
return true;
}
}
I use this by transforming my byte's to a stream, hopefully correctly:
//... building httpMessage.
httpMessage.Content = new ProgressableContent(await byteArrayContent.ReadAsStreamAsync());
//...
var response = await _httpClient.SendAsync(httpMessage, Cancellation.Token);
//...
The question(s):
Am I somehow causing the error? Is there a "better" way to do this?
Tagged this with Xamarin.iOS also since monotouch is complaining.
Double-click on the error from XS and it should bring you to a web page that provide more description about the issue. E.g.
MT3001 Could not AOT the assembly '*'
This generally indicates a bug in the AOT compiler. Please file a bug
http://bugzilla.xamarin.com with a project that can be used to
reproduce the error.
Sometimes it's possible to work around this by disabling incremental
builds in the project's iOS Build option (but it's still a bug, so
please report it anyways).
The main thing about 3001 is that the AOT compiler did not produce an output binary. There can be several reasons for this. Generally the process crashed and the build logs will give a bit more details why.
Even more important is to attach a self-contained test case to the bug report. Something else, beside the code you pasted, can be playing an important part that led to the crash (and it could be impossible to duplicate or guess what that piece could be). That also gives us a better chance to suggest a workaround to the issue.

Can't create handler inside thread which has not called Looper.prepare() [xamarin]

So i don't actually have a question because i've already solved it, but in case someone else runs into this issue it's always nice to have a neat solution.
And while there is a plentitude of "Can't create handler inside thread which has not called Looper.prepare()" - questions there is none tagged with xamarin. (so theirs is all java and i had 0 matches for "Can't create handler inside thread which has not called Looper.prepare() [xamarin]")
The issue is generated because You tried to do work on UI from other thread. If you want to change UI, Must call UI changes from UI thread.
Xamarin Android:
activity.RunOnUiThread(() =>
{
// Write your code here
});
Xamarin IOS:
nsObject.BeginInvokeOnMainThread(() =>
{
// Write your code here
});
Xamarin Forms:
Device.BeginInvokeOnMainThread(() =>
{
// Write your code here
});
public static class PageExtensions
{
public static Task<bool> DisplayAlertOnUi(this Page source, string title, string message, string accept, string cancel)
{
TaskCompletionSource<bool> doneSource = new TaskCompletionSource<bool>();
Device.BeginInvokeOnMainThread(async () =>
{
try
{
var result = await source.DisplayAlert(title, message, accept, cancel);
doneSource.SetResult(result);
}
catch (Exception ex)
{
doneSource.SetException(ex);
}
});
return doneSource.Task;
}
}
Finally i had a case for using TaskCompletionSource to solve an issue.
Very similar actually happend to me. Previous evening I was developing and testing my app. Next morning, from other computer I got the exception you described. I was remembered from a official Xamarin.Forms documentation that sometimes a bin and obj folder removal solves lot of issues.
I did exactly the same, removed my bin and obj folder from my shared Xamarin.Forms library and also from Xamarin.Android library.
The strange exception disappeared.

Custom memory allocator for OpenCV

Is it possible to set a custom allocator for OpenCV 2.3.1? I have a memory pool created and I want OpenCV to use that pool for what it needs.
Is that possible?
If it is, how can it be done?
Updated:
I made some developments since last answer, but I still have some problems.
This is the code I have now:
CvAllocFunc allocCV()
{
return (CvAllocFunc) MEMPOOL->POOLalloc(sz);
}
CvFreeFunc deallocCV()
{
return (CvFreeFunc) MEMPOOL->POOLfree(ptr);
}
...
cvSetMemoryManager(allocCV(),deallocCV(),data);
Now, my question is, how can I have access to the size and the pointer to the data I want to allocate and later to deallocate?
Just found out:
The version of OpenCV I'm using (2.3.1) throws an error when using cvSetMemoryManager. The reason is in its source code:
void cvSetMemoryManager( CvAllocFunc, CvFreeFunc, void * )
{
CV_Error( -1, "Custom memory allocator is not supported" );
}
I was very disappointed with this. I guess I can't use a custom memory pool with OpenCV anymore!
Right from the docs
http://opencv.willowgarage.com/documentation/c/core_utility_and_system_functions_and_macros.html#setmemorymanager
Use the
void cvSetMemoryManager(CvAllocFunc allocFunc=NULL, CvFreeFunc freeFunc=NULL, void* userdata=NULL)
function.
Your code
cvSetMemoryManager(allocCV(),deallocCV(),data);
is WRONG.
See what happens: you call allocCV and deallocCV functions (they return some pointer which is quietly converted to cvAllocFunc) !
And this is only the beginning. See the docs for alloc/dealloc semantics. You should write allocCV/deallocCV with correct signatures.
void* CV_CDECL allocCV(size_t size, void* userdata)
{
return ((YourMemPoolTypeWhichIDoNotKnow*)userdata)->POOLalloc(size);
}
int CV_CDECL deallocCV(void* pptr, void* userdata)
{
return ((YourMemPoolTypeWhichIDoNotKnow*)userdata)->POOLfree(pptr);
}
and then pass your MEMPOOL in the 'userdata' parameter:
cvSetMemoryManager(&allocCV, &deallocCV, (void*)MEMPOOL);
This is a standard way for a library to provide user callbacks.

Visual Studio 2010 plugin / code to clear "Error List" warnings before each build

VS2010 is driving me nuts: whenever I rebuild, the "Error List" warnings from the previous compilation are persisted and any new warnings are simply added to the end of the list. Over time, this list becomes ridiculously long and unwieldy.
I'm using the Chirpy 2.0 tools to run JSHint and JSLint on my JS files, and these tools generate a lot of false positives.
I've been looking for an easy way to clear the contents of this window, but the only manual mechanism that works 100% of the time is to close and re-open the solution. Not very elegant.
I'd like to write a small VS Plug-In or some code that gets called right before a compilation to clear out this list so I can focus only on new warnings for the currently loaded file(s).
I see a .Clear() method for the Output window but not for the Error List. Is this doable?
Once upon a time I was an Add-In/VSIX Package/MEF developer ...
The answer is shortly no, but I have to do it on the long way:
Add-Ins, packages (Managed or not) have access to the VS service level separatedly. Every error belongs to the reporter (If they are manage them as Chirpy do), so you can not handle the errors created by Chirpy 2.0
I take a several look to it's source code and it is persist it's erros gained by the tools in a Singleton collection called TaskList.
The deletion of the collection elements is happening in several point of code in the latest release through the RemoveAll method:
First: after the soulution is closed.
by this:
private static string[] buildCommands = new[] { "Build.BuildSelection", "Build.BuildSolution", "ClassViewContextMenus.ClassViewProject.Build" };
private void CommandEvents_BeforeExecute(string guid, int id, object customIn, object customOut, ref bool cancelDefault) {
EnvDTE.Command objCommand = default(EnvDTE.Command);
string commandName = null;
try {
objCommand = this.App.Commands.Item(guid, id);
} catch (System.ArgumentException) {
}
if (objCommand != null) {
commandName = objCommand.Name;
var settings = new Settings();
if (settings.T4RunAsBuild) {
if (buildCommands.Contains(commandName)) {
if (this.tasks != null) {
this.tasks.RemoveAll();
}
Engines.T4Engine.RunT4Template(this.App, settings.T4RunAsBuildTemplate);
}
}
}
}
As you may see, clear of results depends on many thigs.
First on a setting (which I don't know where to set on GUI or configs, but seems to get its value form a check box).
Second the array of names which are not contains every build commands name.
So I see a solution, but only on the way to modify and rebuild/redepeloy your own version from Chirpy (and make a Pull request):
The code souldn't depend on the commands, and their names. (rebuilds are missing for example)
You could change the method above something like this:
this.eventsOnBuild.OnBuildBegin += ( scope, action ) =>
{
if (action != vsBuildAction.vsBuildActionDeploy)
{
if (this.tasks != null)
{
this.tasks.RemoveAll();
}
if (settings.T4RunAsBuild && action != vsBuildAction.vsBuildActionClean)
{
Engines.T4Engine.RunT4Template(this.App, settings.T4RunAsBuildTemplate);
}
}
};
Or with something equivalent handler method instead of lambda expression.
You shold place it into the subscription OnStartupComplete method of Chirp class.
The unsubscription have to placed into OnDisconnection method in the same class. (As for all other subscribed handlers...)
Update:
When an Add-In disconneced, it isn't means the Studio will be closed immediately. The Add-In could be unloaded. So you should call the RemoveAll from OnDisconneconnection too. (Or Remove and Dispose the TaskList...)
Update2:
You can also make a custom command, and bind it to a hotkey.

Resources