Using new AVAudioUnitSampler (and AVAudioEngine/AVAudioSession instead AUGraph) didn't helped.
Post
Replies
Boosts
Views
Activity
Using AudioKit 4.8 and Wrapper (AKMidiSampler/AKAppleSampler) for Apple's AVAudioUnitSampler fixed the problem, so the problem may come from the way I manage midi messages (however it is similar as how AudioKit does).
The code I'm using :
err = MIDIInputPortCreateWithBlock(client, portName, &port, onMIDIMessageReceived)
And handler defined as :
func onMIDIMessageReceived(message: UnsafePointer<MIDIPacketList>, srcConnRefCon: UnsafeMutableRawPointer?) {
				let packetList: MIDIPacketList = message.pointee
				let n = packetList.numPackets
				var packet = packetList.packet
				for _ in 0 ..< n {
					
						let mes: UInt8 = packet.data.0 & 0xF0
						let ch: UInt8 = packet.data.0 & 0x0F
						if mes == 0x90 && packet.data.2 != 0 {
							
								let noteNo = packet.data.1
								let velocity = packet.data.2
								DispatchQueue.main.async {
										self.delegate?.noteOn(ch: ch, note: noteNo, vel: velocity)
								}
						} else if (mes == 0x80 || mes == 0x90) {
								
								let noteNo = packet.data.1
								let velocity = packet.data.2
								DispatchQueue.main.async {
										self.delegate?.noteOff(ch: ch, note: noteNo, vel: velocity)
								}
						}
						let packetPtr = MIDIPacketNext(&packet)
						packet = packetPtr.pointee
				}
		}
I also tested without DispatchQueue.main.async (using it instead in the delegate when calling the sampler methods), same problem.
After looking into audioKit source code and finding articles about managing MIDIPacket indeed they contain a tuple data. We have to check its total size and then iterating over the pointer to get messages (status byte followed by one or two value bytes). It now works
It finally worked since the previously created file was put on a new iCloud folder (however I don't See the local Documents folder and couldn't access to the applications's file (clicking "On my iPad" did nothing).
Now, after having added the two properties you suggested I can browse "On my ipad", and see application's name content (generated files).
There is a new non-blocking error :
DocumentManager] Failed to associate thumbnails for picked URL file:///Users/.../Library/Developer/CoreSimulator/Devices/.../data/Containers/Data/Application/.../Documents/perf.plist with the Inbox copy file:///Users/.../Library/Developer/CoreSimulator/Devices/.../data/Containers/Data/Application/.../tmp/com.Player-Inbox/perf.plist: Error Domain=QLThumbnail Code=2 "(null)" UserInfo={NSUnderlyingError=0x600003dad7d0 {Error Domain=GSLibraryErrorDomain Code=3 "Generation not found" UserInfo={NSDescription=Generation not found}}
Should I use UIDocumentInteractionController instead ?
In fact the first popover displays fine, however when I choose Save to file, a new popover comes from bottom and immediately closes down (I havent't time to see its content).
I've not implemented any of the UIDocumentInteractionControllerDelegate methods, is there one required ?
Its from iOS 12.4 simulator.
I added activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
NSLog(@"act type %@",activityType);
};
It was called after the second view disappeared.
activityType was : com.apple.CloudDocsUI.AddToiCloudDrive
completed : NO
returnedItems : nil
activityError : Error Domain=NSPOSIXErrorDomain Code=22 "Invalid argument"
When I specify a message besides the url as items, the second popover stays fine.
initWithActivityItems:[NSArray arrayWithObjects:tempUrl, @"Save Perf", nil]
However what is saved is the corresponding text item, not the file corresponding to the url.
Also there is still mention to action type com.apple.CloudDocsUI.AddToiCloudDrive in the log (wich I didn't choose).
I'm also sorry, don't know what I could have provided more. I have a single view controller, the app is simple.
I created a new project (with the two required options in Info.plist - UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace to YES), and using a single view controller.
The second popover also goes down, as if it could't handle the item (plist file url), with error :
Failed to determine whether URL /Users/.../Library/Developer/CoreSimulator/Devices/.../data/Containers/Data/Application/.../Documents/perf.plist (n) is managed by a file provider
On iOS12.4 iPad target (simulator), and XCode 10.3.
UPDATE : it works on a real device (iPad mini 2) (the second popover stays, I can select On my iPad, and the application directory as target. However can't rename the file).
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *saveBtn;
@property (strong, nonatomic) NSDictionary *partsConfigs;
@end
@implementation ViewController
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
(IBAction)saveFile:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"perf.plist"];
NSDictionary *firstPartConfig = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"Analog 4", @"PADS", nil] forKeys:[NSArray arrayWithObjects:@"name", @"category", nil]];
self.partsConfigs = [NSDictionary dictionaryWithObject:firstPartConfig forKey:@"1"];
[self.partsConfigs writeToFile:path atomically:YES];
NSURL *url = [NSURL fileURLWithPath:path];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:[NSArray arrayWithObjects:url, nil] applicationActivities:nil];
activityViewController.popoverPresentationController.sourceView = self.view;
[self presentViewController:activityViewController animated:YES completion:nil];
}
@end
I also added MIDI services discovery :
browser = [[NSNetServiceBrowser alloc] init];
browser.delegate = self;
browser searchForServicesOfType:MIDINetworkBonjourServiceType /* i.e. @"_apple-midi._udp"*/
														inDomain:@""];
I then implemented some NSNetServiceDelegate and NSNetServiceBrowserDelegate methods
(void)netServiceDidResolveAddress:(NSNetService *)service {
		
		if (![[service name] isEqualToString:[[UIDevice currentDevice] name]]) {
				
				[service setDelegate:nil];
				NSLog(@"Resolved service name: %@ host name: %@", [service name], [service hostName]);
				MIDINetworkHost* host = [MIDINetworkHost hostWithName:[service name] netService:service];
				MIDINetworkConnection* newConnection = [MIDINetworkConnection connectionWithHost:host];
				[session addConnection:newConnection];
				NSLog(@"Connected to %@", [service name]);
		}
}
(void)netServiceBrowser:(NSNetServiceBrowser *)aBrowser didFindService:(NSNetService *)aService {
				NSLog(@"Found service %@ on %@", [aService name], [aService hostName]);
if (![[service name] isEqualToString:[[UIDevice currentDevice] name]]) {
				
				[service setDelegate:nil];
				NSLog(@"Resolved service name: %@ host name: %@", [service name], [service hostName]);
				MIDINetworkHost* host = [MIDINetworkHost hostWithName:[service name] netService:service];
				MIDINetworkConnection* newConnection = [MIDINetworkConnection connectionWithHost:host];
				[networkSession addConnection:newConnection];
				NSLog(@"Connected to %@", [service name]);
		}
The two delegate methods didn't get called.
Also when I send midi messages (fom Logic using Network session 1 as Port) the ios app midi handler is not called.
By creating the network connection manually (without discovering), passing the Bonjour name and IP Adresss displayed in Audio&Midi utility
MIDINetworkHost *host = [MIDINetworkHost hostWithName:@"Mac mini ***" address:@"***.***.0.10" port:5004];
MIDINetworkConnection *connection = [MIDINetworkConnection connectionWithHost:host];
[networkSession addConnection:connection];
The MyMIDIReadProc handler was called once in debug mode, the first note started playing, however it was cut shortly thereafter, and subsequent MIDI messages were ignored. Also there was a low buzz sound.
In fact there wasn't an issue, the notes that were cut simply weren't mapped to samples on the AU sampler. It is playing fine.
I may have to implement AUaudioUnit callbacks.
To pass mainMixerNode buffers to AUaudioUnit render callback I could store (in memory, so would grow over time... not ideal) these through installTapOnBus, and then access these from render callback.
AVSampler : AUAudioUnit
-(void) createAudioEngine
...
[mixerNode installTapOnBus:0
bufferSize:4096
format:[[AVAudioFormat alloc]initWithStreamDescription:&audioFormat]
block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
[self.timeToBuffer addObject:buffer forKey:when];
}];
...
(AUInternalRenderBlock)internalRenderBlock {
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *timestamp,
AVAudioFrameCount frameCount,
NSInteger outputBusNumber,
AudioBufferList *outputBufferListPtr,
const AURenderEvent *realtimeEventListHead,
AURenderPullInputBlock pullInputBlock ) {
int outputNumBuffers = outputBufferListPtr->mNumberBuffers;
float *ptrLeft = (float*)outputBufferListPtr->mBuffers[0].mData;
float *ptrRight = NULL;
if (outputNumBuffers == 2) {
ptrRight = (float*)outputBufferListPtr->mBuffers[1].mData;
}
AVAudioPCMBuffer *mixerNodeBuffer = [self.timeToBuffer objectForKey:timestamp];
mixerNodeBuffer.audioBufferList.pointee.mBuffers
int n = frameCount;
if(mixerNodeBuffer.frameLength == frameCount)
for (int i=0;i<n;i++) {
ptrLeft[i] = mixerNodeBuffer.floatChannelData[0].pointee[n];
ptrRight[i] = mixerNodeBuffer.floatChannelData[1].pointee[n];
}
}
}