I'm trying to understand the MPSGraph api. Why does the following code failing to change the value of var after the second run call?
#import <Foundation/Foundation.h>
#import <MetalPerformanceShadersGraph/MetalPerformanceShadersGraph.h>
void run(void);
void run(void) {
MPSGraph *graph = [MPSGraph new];
float test1 = 2.0;
float test2 = 3.0;
MPSGraphTensor *constant = [graph constantWithScalar:test1
dataType:MPSDataTypeFloat32];
MPSGraphTensor *var = [graph variableWithData:[NSData dataWithBytes:&test2 length:sizeof(test2)]
shape:#[#1]
dataType:MPSDataTypeFloat32
name:#"var"];
MPSGraphTensorDataDictionary *result = [graph runWithFeeds:#{}
targetTensors:#[constant, var]
targetOperations:NULL];
float test3;
NSInteger temp = sizeof(test3);
[result[var].mpsndarray readBytes:&test3
strideBytes:&temp];
NSLog(#"%f", test3);
[result[constant].mpsndarray readBytes:&test3
strideBytes:&temp];
NSLog(#"%f", test3);
MPSGraphOperation *op = [graph assignVariable:var
withValueOfTensor:constant
name:NULL];
result = [graph runWithFeeds:#{}
targetTensors:#[var]
targetOperations:#[op]];
[result[var].mpsndarray readBytes:&test3
strideBytes:&temp];
NSLog(#"%f", test3);
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
run();
}
return 0;
}
Output:
2022-09-27 13:26:35.708641-0400 MPSGraphExample[9643:2669233] Metal API Validation Enabled
2022-09-27 13:26:35.732058-0400 MPSGraphExample[9643:2669233] 3.000000
2022-09-27 13:26:35.732097-0400 MPSGraphExample[9643:2669233] 2.000000
2022-09-27 13:26:35.733821-0400 MPSGraphExample[9643:2669233] 3.000000
Program ended with exit code: 0
It turns out that the operation is applied after the tensor is evaluated. Changing it to
[graph runWithFeeds:#{}
targetTensors:#[]
targetOperations:#[op]];
result = [graph runWithFeeds:#{}
targetTensors:#[var]
targetOperations:#[]];
now shows the updated values.
Related
I'm trying to build a stitched metal kernel. My shader code is
[[stitchable]] float add(float a, float b) {
return a + b;
}
[[stitchable]] float load(constant float *a, uint32_t index) {
return a[index];
}
[[stitchable]] void store(device float *a, float value, uint32_t index) {
a[index] = value;
}
[[visible]] void two_inputs(constant float *a, constant float *b, device *c, uint32_t tid);
The driver code is
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> queue = [device newCommandQueue];
id<MTLLibrary> library = [device newDefaultLibrary];
NSArray *functions = #[
[library newFunctionWithName:#"add"],
[library newFunctionWithName:#"load"],
[library newFunctionWithName:#"store"]
];
MTLFunctionStitchingInputNode *srcA = [[MTLFunctionStitchingInputNode alloc] initWithArgumentIndex:0];
MTLFunctionStitchingInputNode *srcB = [[MTLFunctionStitchingInputNode alloc] initWithArgumentIndex:1];
MTLFunctionStitchingInputNode *srcC = [[MTLFunctionStitchingInputNode alloc] initWithArgumentIndex:2];
MTLFunctionStitchingInputNode *srcI = [[MTLFunctionStitchingInputNode alloc] initWithArgumentIndex:3];
MTLFunctionStitchingFunctionNode *loadA = [[MTLFunctionStitchingFunctionNode alloc] initWithName:#"load" arguments:#[srcA, srcI] controlDependencies:#[]];
MTLFunctionStitchingFunctionNode *loadB = [[MTLFunctionStitchingFunctionNode alloc] initWithName:#"load" arguments:#[srcA, srcI] controlDependencies:#[]];
MTLFunctionStitchingFunctionNode *add = [[MTLFunctionStitchingFunctionNode alloc] initWithName:#"load" arguments:#[loadA, loadB] controlDependencies:#[]];
MTLFunctionStitchingFunctionNode *storeC = [[MTLFunctionStitchingFunctionNode alloc] initWithName:#"load" arguments:#[srcC, add, srcI] controlDependencies:#[]];
MTLFunctionStitchingGraph *graph = [[MTLFunctionStitchingGraph alloc] initWithFunctionName:#"two_inputs" nodes:#[loadA, loadB, add] outputNode:storeC attributes:#[]];
MTLStitchedLibraryDescriptor *graphDescriptor = [MTLStitchedLibraryDescriptor new];
graphDescriptor.functions = functions;
graphDescriptor.functionGraphs = #[graph];
NSError *error = NULL;
id<MTLLibrary> graphLibrary = [device newLibraryWithStitchedDescriptor: graphDescriptor.functions error:&error];
NSLog(#"%#", error);
This is causing the metal compiler to fail with the error.
Compiler failed with XPC_ERROR_CONNECTION_INTERRUPTED
MTLCompiler: Compilation failed with XPC_ERROR_CONNECTION_INTERRUPTED on 1 try
...
Error Domain=MTLLibraryErrorDomain Code=3 "Compiler encountered an internal error" UserInfo={NSLocalizedDescription=Compiler encountered an internal error}
I'm trying to run this on an M1 mac.
It turns out the reason the Metal compiler was crashing was this line
MTLFunctionStitchingGraph *graph = [[MTLFunctionStitchingGraph alloc] initWithFunctionName:#"two_inputs" nodes:#[loadA, loadB, add] outputNode:storeC attributes:#[]];
should be
MTLFunctionStitchingGraph *graph = [[MTLFunctionStitchingGraph alloc] initWithFunctionName:#"two_inputs" nodes:#[loadA, loadB, add, storeC] outputNode:NULL attributes:#[]];
instead.
I'm very, very new to Objective-C (this is for a React Native app), and I am trying to pare down a list of colors and remove duplicates.
I've got this so far:
... code related to pulling pixels from an image
float flexibility = 2;
float range = 60;
NSMutableArray * colors = [NSMutableArray new];
float x = 0;
float y = 0;
for (int n = 0; n<(width*height); n++){
int index = (bytesPerRow * y) + x * bytesPerPixel;
int red = rawData[index];
int green = rawData[index + 1];
int blue = rawData[index + 2];
int alpha = rawData[index + 3];
NSArray * a = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%i",red],[NSString stringWithFormat:#"%i",green],[NSString stringWithFormat:#"%i",blue],[NSString stringWithFormat:#"%i",alpha], nil];
[colors addObject:a];
y++;
if (y==height){
y=0;
x++;
}
}
free(rawData);
Now the script I am basing this off of has some code to pare down the list:
NSMutableDictionary * colourCounter = [NSMutableDictionary new];
//count the occurences in the array
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:colors];
for (NSString *item in countedSet) {
NSUInteger count = [countedSet countForObject:item];
[colourCounter setValue:[NSNumber numberWithInteger:count] forKey:item];
}
//sort keys highest occurrence to lowest
NSArray *orderedKeys = [colourCounter keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2){
return [obj2 compare:obj1];
}];
//checks if the colour is similar to another one already included
NSMutableArray * ranges = [NSMutableArray new];
for (NSString * key in orderedKeys){
NSArray * rgb = [key componentsSeparatedByString:#","];
int r = [rgb[0] intValue];
int g = [rgb[1] intValue];
int b = [rgb[2] intValue];
bool exclude = false;
for (NSString * ranged_key in ranges){
NSArray * ranged_rgb = [ranged_key componentsSeparatedByString:#","];
int ranged_r = [ranged_rgb[0] intValue];
int ranged_g = [ranged_rgb[1] intValue];
int ranged_b = [ranged_rgb[2] intValue];
if (r>= ranged_r-range && r<= ranged_r+range){
if (g>= ranged_g-range && g<= ranged_g+range){
if (b>= ranged_b-range && b<= ranged_b+range){
exclude = true;
}
}
}
}
if (!exclude){ [ranges addObject:key]; }
}
//return ranges array here if you just want the ordered colours high to low
NSMutableArray * colourArray = [NSMutableArray new];
for (NSString * key in ranges){
NSArray * rgb = [key componentsSeparatedByString:#","];
float r = [rgb[0] floatValue];
float g = [rgb[1] floatValue];
float b = [rgb[2] floatValue];
UIColor * colour = [UIColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];
[colourArray addObject:colour];
}
//if you just want an array of images of most common to least, return here
//return [NSDictionary dictionaryWithObject:colourArray forKey:#"colours"];
I ultimately want to return this colourArray (the original library uses colour, I use color, so you'll sometimes see one or the other spelling - I'll probably standardize it to color when finished).
However, whenever I use this code, RN generates an error:
As far as I can tell, this is due to calling a function that doesn't exist? Is there something I am missing here, or calling incorrectly?
I have Metal compute kernel that takes two textures for arguments. However, I'm running into a problem where the kernel doesn't run. I have reduced to the problem down to this simple kernel.
#include <metal_stdlib>
using namespace metal;
kernel void test_texture(texture2d<float, access::sample> tex1 [[texture(0)]],
texture2d<float, access::sample> tex2 [[texture(1)]],
device float *buf [[buffer(0)]],
uint idx [[thread_position_in_grid]])
{
buf[idx] = 100;
}
And the following host code.
#import <Metal/Metal.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
const size_t max_buffer = 128000000;
const size_t max_texture = 16384;
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
id<MTLLibrary> library = [device newDefaultLibrary];
id<MTLCommandQueue> queue = [device newCommandQueue];
id<MTLBuffer> buffer = [device newBufferWithLength:sizeof(float)*max_buffer
options:MTLResourceCPUCacheModeDefaultCache |
MTLResourceStorageModeManaged];
MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];
textureDescriptor.textureType = MTLTextureType2D;
textureDescriptor.pixelFormat = MTLPixelFormatR32Float;
textureDescriptor.width = max_texture;
textureDescriptor.height = max_texture;
textureDescriptor.depth = 1;
textureDescriptor.mipmapLevelCount = 1;
textureDescriptor.sampleCount = 1;
textureDescriptor.arrayLength = 1;
textureDescriptor.resourceOptions = MTLResourceStorageModePrivate | MTLResourceCPUCacheModeDefaultCache;
textureDescriptor.cpuCacheMode = MTLCPUCacheModeDefaultCache;
textureDescriptor.storageMode = MTLStorageModePrivate;
textureDescriptor.usage = MTLTextureUsageShaderRead;
id<MTLTexture> texture1 = [device newTextureWithDescriptor:textureDescriptor];
id<MTLTexture> texture2 = [device newTextureWithDescriptor:textureDescriptor];
MTLComputePipelineDescriptor *discriptor = [[MTLComputePipelineDescriptor alloc] init];
discriptor.computeFunction = [library newFunctionWithName:#"test_texture"];
discriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
id<MTLComputePipelineState> pipeline = [device newComputePipelineStateWithDescriptor:discriptor
options:MTLPipelineOptionNone
reflection:NULL
error:NULL];
id<MTLCommandBuffer> command_buffer = queue.commandBuffer;
id<MTLComputeCommandEncoder> compute_encoder = [command_buffer computeCommandEncoder];
[compute_encoder setComputePipelineState:pipeline];
[compute_encoder setTexture:texture1 atIndex:0];
[compute_encoder setTexture:texture2 atIndex:1];
[compute_encoder setBuffer:buffer offset:0 atIndex:0];
[compute_encoder dispatchThreads:MTLSizeMake(max_buffer, 1, 1) threadsPerThreadgroup:MTLSizeMake(1024, 1, 1)];
[compute_encoder endEncoding];
id<MTLBlitCommandEncoder> blit_encoder = [command_buffer blitCommandEncoder];
[blit_encoder synchronizeResource:buffer];
[blit_encoder endEncoding];
[command_buffer commit];
[command_buffer waitUntilCompleted];
float *result = (float *)buffer.contents;
NSLog(#"%f",result[0]);
}
return 0;
}
If I comment out the second texture argument, I get the expected value when I read the result buffer. However when I leave the second texture argument intact, it appears as if kernel doesn't run and the value in result comes out as zero. Is there a limitation on the number of textures that can be sampled in a compute kernel on MacOS? Or is the problem caused by my use of the maximum texture dimensions in both textures (Am I running out of texture memory)?strong text
In your case the error most likely occurred due to the textures taking up your whole video memory budget. 16384 x 16384 * sizeof(float) = 1024mb of memory per texture. Because you're using MTLStorageModePrivate the resource is stored in video memory only.
I use SceneKit to display .obj file. But to get an .obj file I use SDK from a sensor, so this sensor scans the arm of a man and returns the .obj file as a result. But when I load the .obj file, there are a lot of not proper parts (part of chair, part of the surface and so on), I need to remove these parts of the object, so as a result I has to see only the arm of the man.
So for example I want to select a rectangle or a sphere and to remove all vertices and faces in this sphere.
Are there any SDK or frameworks in iOS to do that?
P.S. I tried nineveh and some other frameworks, but they can only view objects, they can't edit them.
Edit
I found the code to manipulate vertices (it merges vertices from different child nodes) in SceneKit. Can I use the same approach to find vertices I need to remove (that are inside my rectangle) or it will be very slow with 65 K vertices?
//
// VertexManager.m
// Test
//
#import "VertexManager.h"
#import <SceneKit/SceneKit.h>
#import <GLKit/GLKit.h>
#implementation VertexManager
+ (SCNNode *) flattenNodeHierarchy:(SCNNode *) input
{
SCNNode *result = [SCNNode node];
NSUInteger nodeCount = [[input childNodes] count];
if(nodeCount > 0){
SCNNode *node = [[input childNodes] objectAtIndex:0];
NSArray *vertexArray = [node.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
SCNGeometrySource *vertex = [vertexArray objectAtIndex:0];
SCNGeometryElement *element = [node.geometry geometryElementAtIndex:0]; //todo: support multiple elements
NSUInteger primitiveCount = element.primitiveCount;
NSUInteger newPrimitiveCount = primitiveCount * nodeCount;
size_t elementBufferLength = newPrimitiveCount * 3 * sizeof(int); //nTriangle x 3 vertex * size of int
int* elementBuffer = (int*)malloc(elementBufferLength);
/* simple case: here we consider that all the objects to flatten are the same
In the regular case we should iterate on every geometry and accumulate the number of vertex/triangles etc...*/
NSUInteger vertexCount = [vertex vectorCount];
NSUInteger newVertexCount = vertexCount * nodeCount;
SCNVector3 *newVertex = malloc(sizeof(SCNVector3) * newVertexCount);
SCNVector3 *newNormal = malloc(sizeof(SCNVector3) * newVertexCount); //assume same number of normal/vertex
//fill
NSUInteger vertexFillIndex = 0;
NSUInteger primitiveFillIndex = 0;
for(NSUInteger index=0; index< nodeCount; index++){
#autoreleasepool {
node = [[input childNodes] objectAtIndex:index];
NSArray *vertexArray = [node.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
NSArray *normalArray = [node.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticNormal];
SCNGeometrySource *vertex = [vertexArray objectAtIndex:0];
SCNGeometrySource *normals = [normalArray objectAtIndex:0];
if([vertex bytesPerComponent] != sizeof(float)){
NSLog(#"todo: support other byte per component");
continue;
}
float *vertexBuffer = (float *)[[vertex data] bytes];
float *normalBuffer = (float *)[[normals data] bytes];
SCNMatrix4 t = [node transform];
GLKMatrix4 matrix = MyGLKMatrix4FromCATransform3D(t);
//append source
for(NSUInteger vIndex = 0; vIndex < vertexCount; vIndex++, vertexFillIndex++){
GLKVector3 v = GLKVector3Make(vertexBuffer[vIndex * 3], vertexBuffer[vIndex * 3+1], vertexBuffer[vIndex * 3 + 2]);
GLKVector3 n = GLKVector3Make(normalBuffer[vIndex * 3], normalBuffer[vIndex * 3+1], normalBuffer[vIndex * 3 + 2]);
//transform
v = GLKMatrix4MultiplyVector3WithTranslation(matrix, v);
n = GLKMatrix4MultiplyVector3(matrix, n);
newVertex[vertexFillIndex] = SCNVector3Make(v.x, v.y, v.z);
newNormal[vertexFillIndex] = SCNVector3Make(n.x, n.y, n.z);
}
//append elements
//here we assume that all elements are SCNGeometryPrimitiveTypeTriangles
SCNGeometryElement *element = [node.geometry geometryElementAtIndex:0];
const void *inputPrimitive = [element.data bytes];
size_t bpi = element.bytesPerIndex;
NSUInteger offset = index * vertexCount;
for(NSUInteger pIndex = 0; pIndex < primitiveCount; pIndex++, primitiveFillIndex+=3){
elementBuffer[primitiveFillIndex] = offset + _getIndex(inputPrimitive, bpi, pIndex*3);
elementBuffer[primitiveFillIndex+1] = offset + _getIndex(inputPrimitive, bpi, pIndex*3+1);
elementBuffer[primitiveFillIndex+2] = offset + _getIndex(inputPrimitive, bpi, pIndex*3+2);
}
}
}
NSArray *sources = #[[SCNGeometrySource geometrySourceWithVertices:newVertex count:newVertexCount],
[SCNGeometrySource geometrySourceWithNormals:newNormal count:newVertexCount]];
NSData *newElementData = [NSMutableData dataWithBytesNoCopy:elementBuffer length:elementBufferLength freeWhenDone:YES];
NSArray *elements = #[[SCNGeometryElement geometryElementWithData:newElementData
primitiveType:SCNGeometryPrimitiveTypeTriangles
primitiveCount:newPrimitiveCount bytesPerIndex:sizeof(int)]];
result.geometry = [SCNGeometry geometryWithSources:sources elements:elements];
//cleanup
free(newVertex);
free(newNormal);
}
return result;
}
//helpers:
GLKMatrix4 MyGLKMatrix4FromCATransform3D(SCNMatrix4 transform) {
GLKMatrix4 m = {{transform.m11, transform.m12, transform.m13, transform.m14,
transform.m21, transform.m22, transform.m23, transform.m24,
transform.m31, transform.m32, transform.m33, transform.m34,
transform.m41, transform.m42, transform.m43, transform.m44}};
return m;
}
GLKVector3 MySCNVector3ToGLKVector3(SCNVector3 vector) {
GLKVector3 v = {{vector.x, vector.y, vector.z}};
return v;
}
#end
No. You'll want to use a 3d tool like Blender, Maya, 3ds Max, or Cheetah 3D.
I try to record midi file with an Ipad.
My Ipad is pluged with the usb output of my electric piano.
I have read the apple core midi documentation and I have understand that :
For record a file, I should create a MusicSequence. So that I try to do but It doesn't work :(
Here is my code:
Firstly, I setup my midi connection:
-(void) setupMIDI {
MIDIClientRef client = nil;
MIDIClientCreate(CFSTR("Core MIDI to System Sounds Demo"), MyMIDINotifyProc, (__bridge void *)(self), &client);
inputPort = nil;
MIDIInputPortCreate(client, CFSTR("Input port"), MyMIDIReadProc, (__bridge void *)(self), &inputPort);
sequence = nil;
NewMusicSequence(&(sequence));
unsigned long sourceCount = MIDIGetNumberOfSources();
[self appendToTextView:[NSString stringWithFormat:#"%ld sources\n", sourceCount]];
for (int i = 0; i < sourceCount; ++i) {
MIDIEndpointRef src = MIDIGetSource(i);
CFStringRef endpointName = NULL;
OSStatus nameErr = MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endpointName);
if (noErr == nameErr) {
[self appendToTextView: [NSString stringWithFormat:#" source %d: %#\n", i, endpointName]];
}
MIDIPortConnectSource(inputPort, src, NULL);
MusicSequenceSetMIDIEndpoint(sequence, src);
}
}
After that, I receive my Midi event with MyMIDIReadProc which is a callback function of my input port :
static void MyMIDIReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
{
AppViewController *vc = (__bridge AppViewController*) refCon;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
for (int i=0; i < pktlist->numPackets; i++) {
Byte midiStatus = packet->data[0];
Byte midiCommand = midiStatus >> 4;
// is it a note-on or note-off
if ((midiCommand == 0x09) ||
(midiCommand == 0x08)) {
Byte note = packet->data[1] & 0x7F;
Byte velocity = packet->data[2] & 0x7F;
NSLog(#"midiCommand=%d. Note=%d, Velocity=%d\n", midiCommand, note, velocity);
MIDINoteMessage noteMessage;
noteMessage.releaseVelocity = 0;
noteMessage.velocity = velocity;
noteMessage.note = note;
MusicTrackNewMIDINoteEvent(vc->musicTrack, packet->timeStamp, ¬eMessage);
packet = MIDIPacketNext(packet);
}
}
I try to transform MIDIPklist on MIDINoteMessage to add it on my track.
When I have finished that, I create the file with this function :
-(void) createMidiFile
{
// init sequence
NewMusicSequence(&sequence);
CFURLRef pathUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:self.path];
//set track to sequence
MusicSequenceNewTrack(sequence, &musicTrack);
// write sequence in file
MusicSequenceFileCreate(sequence,
pathUrl,
kMusicSequenceFile_MIDIType,
kMusicSequenceFileFlags_EraseFile,
0);
}
The file has been created but the data aren't correct. It have every time the same size.
Thanks if you can help me to debug that ! I don't understand what I have to do to fill the track and sequence object for create a good mid file...
Sorry for my english guys.. :)
I'm trying to solve same problem. From what I can see - the MIDINoteMessage needs to have a duration which corresponds to the delta of note on and subsequent note off call. You have to keep track of this.
The callback should be performed on main thread and you need to be using CACurrentMediaTime to stash the times midi timestamps prior to dumping out the midi file. Some of the code below.
The other alternative approach was sourced from apple forums
"Create a MusicSequence, add a MusicTrack to it, add some midi events to the track via MusicTrackNewMidiNoteEvent, set that MusicSequence on a newly created MusicPlayer, and start the player. Now that you have that player playing you can query it for the current time in beats via the MusicPlayerGetTime function. Set that time for the MusicTimeStamp for midi messages you send to MusicTrackNewMidiNoteEvent.
**Important Note - You MUST populate the MusicTrack for the MusicPlayer that you are querying for the time stamp, or it won't play! You'll get an error (probably (-50) depending on if you set everything else up correctly). I did this with a loop, adding a message with a time stamp starting at zero, going up to four. I would guess that you don't even have to go that high, but the Track has to have something for the player to play. Don't worry about it running off the end since all we want It for is the MusicTimeStamp. The MusicPlayer will continue playing until you tell it to stop."
This code is the closest I've come to answer - although this is targeting IOS.
https://github.com/benweitzman/ReTune/blob/ee47009999298c2b03527302c3fb6d7be17b10e2/Return4/ViewController.m
#interface NoteObject : NSObject
#property (nonatomic) int time;
#property (nonatomic) int note;
#property (nonatomic) int velocity;
#property (nonatomic) bool noteOn;
#end
- (void) midiSource:(PGMidiSource*)midi midiReceived:(const MIDIPacketList *)packetList
{
[self performSelectorOnMainThread:#selector(addString:)
withObject:#"MIDI received:"
waitUntilDone:NO];
const MIDIPacket *packet = &packetList->packet[0];
for (int i = 0; i < packetList->numPackets; ++i)
{
//[self performSelectorOnMainThread:#selector(addString:)
// withObject:[self StringFromPacket:packet]
// waitUntilDone:NO];
if (packet->length == 3) {
if ((packet->data[0]&0xF0) == 0x90) {
if (packet->data[2] != 0) {
[self noteOn:packet->data[1] withVelocity:packet->data[2]];
} else {
[self noteOff:packet->data[1]];
}
} else if ((packet->data[0]&0xF0) == 0x80) {
[self noteOff:packet->data[1]];
}
}
packet = MIDIPacketNext(packet);
}
}
- (void) noteOff:(int)noteValue {
//NSLog(#"off");
if (noteValue>=0 && noteValue<127) {
ALSource * source = [sources objectAtIndex:noteValue];
ALSource *loopSource = [loopSources objectAtIndex:noteValue];
if (source.playing || loopSource.playing) {
[[fadingOut objectAtIndex:noteValue] release];
[fadingOut replaceObjectAtIndex:noteValue withObject:[[NSNumber alloc] initWithBool:YES]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
float timeDone = 0;
float duration = 0.2;
float timeStep = 0.01;
float valStep = source.gain*timeStep/duration;
float loopStep = loopSource.gain*timeStep/duration;
while (timeDone < duration) {
if (![[fadingOut objectAtIndex:noteValue] boolValue]) break;
source.gain -= valStep;
loopSource.gain -= loopStep;
[NSThread sleepForTimeInterval:timeStep];
timeDone += timeStep;
}
if ([[fadingOut objectAtIndex:noteValue] boolValue]) {
[source stop];
[loopSource stop];
}
//source.gain = 1;
});
if (recording) {
double currentTime = CACurrentMediaTime();
int deltaTime = (int)(currentTime*1000-recordTimer*1000);
NoteObject * recordedNote = [[NoteObject alloc] init];
recordedNote.note = noteValue;
recordedNote.time = deltaTime;
recordedNote.noteOn = false;
[recordedNotes addObject:recordedNote];
recordTimer = currentTime;
}
}
}
}
- (void) finishFadeIn:(ALSource*)source {
}
- (void) noteOn:(int)noteValue withVelocity:(int)velocity {
if (noteValue>=0 && noteValue<127) {
if (recording) {
double currentTime = CACurrentMediaTime();
int deltaTime = (int)(currentTime*1000-recordTimer*1000);
NoteObject * recordedNote = [[NoteObject alloc] init];
recordedNote.note = noteValue;
recordedNote.time = deltaTime;
recordedNote.noteOn = true;
[recordedNotes addObject:recordedNote];
recordTimer = currentTime;
}
while(loadingScale || changingPitch);
float pitchToPlay = [[ratios objectAtIndex:noteValue] floatValue];
[[fadingOut objectAtIndex:noteValue] release];
[fadingOut replaceObjectAtIndex:noteValue withObject:[[NSNumber alloc] initWithBool:NO]];
ALSource * source = [sources objectAtIndex:noteValue];
[source stop];
source.gain = velocity/127.0f;
source.pitch = pitchToPlay;
[source play:[buffers objectAtIndex:noteValue]];
if ([loopBuffers objectAtIndex:noteValue] != (id)[NSNull null]) {
ALSource *loopSource = [loopSources objectAtIndex:noteValue];
[loopSource stop];
loopSource.gain = 0;
loopSource.pitch = source.pitch;
[loopSource play:[loopBuffers objectAtIndex:noteValue] loop:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
float timeDone = 0;
float duration = [(ALBuffer*)[buffers objectAtIndex:noteValue] duration]-.4;
float timeStep = 0.01;
float valStep = source.gain*timeStep/duration;
float loopStep = valStep;
while (timeDone < duration) {
if ([[fadingOut objectAtIndex:noteValue] boolValue]) break;
source.gain -= valStep;
loopSource.gain += loopStep;
[NSThread sleepForTimeInterval:timeStep];
timeDone += timeStep;
}
/*if ([[fadingOut objectAtIndex:noteValue] boolValue]) {
[source stop];
[loopSource stop];
}*/
//source.gain = 1;
});
}
/*
[source play];*/
//[[sources objectAtIndex:noteValue] play:toPlay gain:velocity/127.0f pitch:pitchToPlay pan:0.0f loop:FALSE];
}
}