CoreMidi with MacRuby - ruby

I am trying to use the CoreMidi.framework with MacRuby and I am getting hung up on the correct way to implement the CoreMidi mechanisms in Ruby. I have created the MIDIClient and the OutputPort:
clientName = "Client"
clientRef = Pointer.new(:uint)
MIDIClientCreate( clientName, nil, nil, clientRef )
portName = "Output"
outport = Pointer.new(:uint)
MIDIOutputPortCreate( clientRef[0], portName, outport )
numberOfDestinations = MIDIGetNumberOfDestinations()
destination = MIDIGetDestination( 0 )
After this, I am at a loss for exactly what to do. I need to create a MIDIPacketList to send to MIDISend, but I'm not sure how this would be done in Ruby. The program hangs when I try to create a packet list with:
packetList = MIDIPacketList.new
Any suggestions?

Related

How to encode protobuf map in python?

I am using protobuf and grpc as interface between a client and server.
The server is written in C and the client uses python to communicate to the server.
I have a message created in protobuf like below.
message value_obj {
uint32 code = 1;
uint32 value = 2;
}
message list_of_maps {
map<uint32, value_obj> mapObj1 = 1;
map<uint32, value_obj> mapObj2 = 2;
}
I tried creating objects in Python like below:
obj = list_of_maps()
mapObjToStore = value_obj()
mapObjToStore.code = 10
obj.mapObj1[1].CopyFrom(mapObjToStore)
When I try to receive the message in server, I get wrong values (huge numbers!).
Any help on this will be greatly appreciated.
You can try using python dictionary for that:
map1 = {}
obj1 = value_obj()
map1[1] = obj1
map2 = {}
listOfMaps = list_of_maps(mapObj1=map1, mapObj2=map2)

Passing an argument to a SynthDef doesn't register when synth is initialized

I have a simple SynthDef where I want to use the CCIn class, like so:
(
SynthDef(\lfo_sin, {|bus, amp, myArg|
var m = CCIn.new;
var v = [300, 700, \exp].asSpec;
var sig = SinOsc.ar(m.kr(0, myArg, v), mul:amp);
Out.ar(bus, sig);
}).add;
)
When I instantiate it like so y = Synth(\lfo_sin, [\bus, 0, \amp, 1, \myArg, 71]);, to match with the MIDI CC on my MIDI controller, I am not able to use the CCIn.kr method like I should.
If I however directly type in the MIDI CC when I define the SynthDef like so:
(
SynthDef(\lfo_sin2, {|bus, amp|
var m = CCIn.new;
var v = [300, 700, \exp].asSpec;
var sig = SinOsc.ar(m.kr(0, 71, v), mul:amp);
Out.ar(bus, sig);
}).add;
)
everything runs like it should and I am able to control the frequency using my MIDI controller.
Why does this behavior happen and how can I modify my code so that I can pass in the MIDI CC when initializing the synth or setting the argument afterwards?
If you take a look at the source for CCIn, you can see it's doing something a bit more complex than an ordinary UGen:
kr { |chan = 0, num = 0, spec = \amp, lag = 0.05|
var outArray = [chan, num, spec, lag].flop.collect{ |args|
var ch, n, sp, lg;
# ch, n, sp, lg = args;
(sp.asSpec.map( In.kr(this.prGetBus(ch, n).index) ).lag3(lg))
};
if (outArray.size>1) {^outArray} {^(outArray[0])} //fix to work with muliout
Specifically, this...
this.prGetBus(ch, n)
is using the provided channel and number (ch and n) to look up the Bus from which it can read the MIDI data (see prGetBus). It's doing this lookup as part of BUILDING the SynthDef, not RUNNING the Synth, so once it's been built the bus it's reading from is pretty much fixed. The CCIn quark obscures some fairly complex things under the hood in order to behave as a simple UGen, so it's unlikely you'll easily be able to get the behavior you're looking for.
Here are a few alternatives.
1. Write your MIDI data to a bus yourself
// One for each cc number
~ccBusses = 127.collect({
Bus.control(s, 1);
});
// A midi responder that sets the value of the right bus
MIDIdef.cc(\cc, {
|value, cc|
~ccBusses[cc].set(value);
}, ccNum: (0..127) ) // meaning: all cc values
// Once those are set up, to map a cc to a new synth use:
Synth(\mySynth, args:[\freq, ~ccBusses[10].asMap]);
2. Using the Connection quark
// Create a value between 100..2400, controlled by MIDI
~freq = MIDIControlValue(spec:ControlSpec(100, 2400));
~freq.cc_(10); // cc number 10
// Run your synth
~note = Synth(\mySynth, args:[\freq, ~freq]);
// Connect the value of ~freq to the \freq argument of your synth. Now, MIDI changes will be broadcast to your synth.
~freq.signal(\value).connectTo(~note.argSlot(\freq));

Streaming audio to multiple AirPlay devices

Anyone know how to stream audio to multiple AirPlay destinations? Apparently, this was possible through Core Audio at some point in the past, but on 10.9 and 10.10, this does not seem possible. iTunes does it, so what's the secret? Here is some code I tried to see if I could get this to work:
OSStatus err = 0;
UInt32 size = sizeof(UInt32);
SSAudioSource * targetSource = airplayDevice.airplaySources[0];
AudioDeviceID airPlayDeviceID = targetSource.deviceID;
SSAudioSource * source1 = airplayDevice.airplaySources[0];
SSAudioSource * source2 = airplayDevice.airplaySources[1];
SSAudioSource * source3 = airplayDevice.airplaySources[2];
AudioDeviceID alldevices[] = {source3.sourceID, source2.sourceID, source1.sourceID};
AudioObjectPropertyAddress addr;
addr.mSelector = kAudioDevicePropertyDataSource;
addr.mScope = kAudioDevicePropertyScopeOutput;
addr.mElement = kAudioObjectPropertyElementMaster;
// Set the 'AirPlay' device to point to all of its sources...
err = AudioObjectSetPropertyData(airPlayDeviceID, &addr, 0, nil, size, alldevices);
AudioObjectPropertyAddress audioDevicesAddress = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
// ...now set the system output to point at the 'AirPlay' device
err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &audioDevicesAddress, 0, nil, size, &airPlayDeviceID);
No matter how I arrange the devices in the array, sound only comes out of the first device (index 0) of the array. So what's the secret?
Thanks
I raised a bug report with Apple for this back in July and got a reply in October:
Engineering has determined that there are no plans to address this
issue.
I've gone back to Apple asking why the functionality has been removed but not hopeful for a (timely) response.
For what it's worth I think your approach is correct, it's similar to the way I had it working in the past for an app. I suspect iTunes uses Audio Units or something similar to do multiple speakers.

Symbol Device Kill Process

I'm trying to see how many instances of an application are running on a MC65 device, a Windows Mobile 6.5 device. Then if there is more than one instance of the application running kill all instances and run the application. I've tried that code here. But it doesn't work on the MC65 device. I believe this is because it is a symbol device and I've read somewhere that they act differently than non-symbol devices.
Does anyone know how to find out what processes are running on a symbol device programatically?
Update: Upon further testing the device is having problems creating a snapshot of the running processes. Still haven't found a solution.
Taking a snapshot should work fine BUT you have to use a flag to avoid memory limitations throwing an exception:
[Flags]
private enum SnapshotFlags : uint
{
HeapList = 0x00000001,
Process = 0x00000002,
Thread = 0x00000004,
Module = 0x00000008,
Module32 = 0x00000010,
Inherit = 0x80000000,
All = 0x0000001F,
NoHeaps = 0x40000000
}
Then in a normal call to CreateToolhelp32Snapshot you can get a list of processes:
public static Dictionary<UInt32, process> getProcessNameList()
{
int iCnt = 0;
//List<processnames> name_list = new List<processnames>();
Dictionary<UInt32, process> _pList = new Dictionary<uint, process>();
uint procID = 0;
IntPtr pHandle = CreateToolhelp32Snapshot(SnapshotFlags.Process | SnapshotFlags.NoHeaps, procID);
if ((Int32)pHandle == INVALID_HANDLE_VALUE)
throw new Exception("CreateToolhelp32Snapshot error: " + Marshal.GetLastWin32Error().ToString());
if ((int)pHandle != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 pEntry = new PROCESSENTRY32();
pEntry.dwSize = (uint)Marshal.SizeOf(pEntry);
if (Process32First(pHandle, ref pEntry) == 1)
{
do
{
//name_list.Add(new processnames(pEntry.th32ProcessID, pEntry.szExeFile));
_pList[pEntry.th32ProcessID] = new process(pEntry.th32ProcessID, pEntry.szExeFile, new List<thread>());
iCnt++;
} while (Process32Next(pHandle, ref pEntry) == 1);
}
else
System.Diagnostics.Debug.WriteLine("Process32First error: " + Marshal.GetLastWin32Error().ToString());
CloseToolhelp32Snapshot(pHandle);
}
return _pList;
}
The above code is part of my remote ProcessorUsage test application.
Nevertheless normal windows mobile application will terminate them self if a previous instance is already running. That is also the default when you create and run a SmartDevice project in CSharp or CPP targetting "Windows Mobile ...".
If you target a Standard Windows CE based SDK, there is no automatic code generated to prevent multiple instances in the start code of the app.
Let us know, if you still need assistance.

F# FTP Fails where C# Succeeds

I've an application written in C# (.Net 3.5) with this code.
using System;
using System.Net;
string strURI = String.Format("ftp://x{0}ftp/%2F'{1}'", parm1, parm2);
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(strURI);
ftp.Proxy = null;
ftp.KeepAlive = true;
ftp.UsePassive = false;
ftp.UseBinary = false;
ftp.Credentials = new NetworkCredential("uid", "pass");
ftp.Method = WebRequestMethods.Ftp.DownloadFile;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
...
I've translated this into F# (.Net 4.0) to use in my application.
open System.Net
let uri = sprintf "ftp://x%sftp/%%2F'%s'" parm1 parm2
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest
ftp.Credentials <- new NetworkCredential("uid", "pass")
ftp.Method <- WebRequestMethods.Ftp.DownloadFile
ftp.Proxy <- null
let response = ftp.GetResponse() :?> FtpWebResponse
...
At this point, FSI complains.
System.Net.WebException: The remote server returned an error: (550) File unavailable (e.g. file not found, no access).
Yet the C# application runs and successfully downloads the file. What am I missing in F# (besides the properties that aren't there in 4.0, i.e. KeepAlive, UsePassive, and UseBinary)?
The code may have errors, I don't have F# compiler right now.
open System
open System.Reflection
open System.Net
let switch_to_legacy_mode _ =
let wtype = typeof<FtpWebRequest>
let mfield = wtype.GetField("m_MethodInfo", BindingFlags.NonPublic ||| BindingFlags.Instance)
let mtype = mfield.FieldType
let knfield = mtype.GetField("KnownMethodInfo", BindingFlags.Static ||| BindingFlags.NonPublic)
let knarr = knfield.GetValue(null) :?> Array
let flags = mtype.GetField("Flags", BindingFlags.NonPublic ||| BindingFlags.Instance)
let flag_val = 0x100
for f in knarr do
let mutable ff = flags.GetValue(f) :?> int
ff <- ff ||| flag_val
flags.SetValue(f, ff)
let uri = sprintf "ftp://x%sftp/%%2F'%s'" parm1 parm2
do switch_to_legacy_mode () // Call it once before making first FTP request
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest
ftp.Credentials <- new NetworkCredential("uid", "pass")
ftp.Method <- WebRequestMethods.Ftp.DownloadFile
ftp.Proxy <- null
let response = ftp.GetResponse() :?> FtpWebResponse
Source: http://support.microsoft.com/kb/2134299
The cause of this issue is due to a behavior change in the
System.Net.FtpWebRequest class in .Net Framework 4. There has been a
change made to the System.Net.FtpWebRequest class from .Net Framework
3.5 to .Net Framework 4 to streamline the use of the CWD protocol commands. The new implementation of the System.Net.FtpWebRequest class
prevents the send of extra CWD commands before issuing the actual
command which the user requested and instead directly sends the
requested command. For fully RFC compliant FTP servers, this should
not be an issue, however for non-fully RFC compliant servers, you will
see these types of errors.
Try this instead:
open System
open System.Net
let parm1 = "test"
let parm2 = "othertest"
let uri = String.Format("ftp://x{0}ftp/%2F'{1}'", parm1, parm2)
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest;;
I had to add a dummy parm1 and parm2 but I'm assuming you've got parm1 and parm2 defined in your actual code. I'd guess that whatever the issue is, it lies in the uri string. That is, I think you'll find the problem by comparing uri to the known to be good uri string.

Resources