add midi clock

This commit is contained in:
Chikashi Miyama 2019-10-27 11:21:33 +01:00
parent f4602f9794
commit 7b273e2b8a
9 changed files with 356 additions and 40 deletions

View file

@ -71,6 +71,7 @@
<Compile Include="Assets\Scripts\Math\Threshold.cs" /> <Compile Include="Assets\Scripts\Math\Threshold.cs" />
<Compile Include="Assets\Scripts\PdConnection\DspController.cs" /> <Compile Include="Assets\Scripts\PdConnection\DspController.cs" />
<Compile Include="Assets\Scripts\PdConnection\ISpectrumGenerator.cs" /> <Compile Include="Assets\Scripts\PdConnection\ISpectrumGenerator.cs" />
<Compile Include="Assets\Scripts\PdConnection\MidiClockCounter.cs" />
<Compile Include="Assets\Scripts\PdConnection\MidiLogger.cs" /> <Compile Include="Assets\Scripts\PdConnection\MidiLogger.cs" />
<Compile Include="Assets\Scripts\PdConnection\MidiParser.cs" /> <Compile Include="Assets\Scripts\PdConnection\MidiParser.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdArray.cs" /> <Compile Include="Assets\Scripts\PdConnection\PdArray.cs" />

View file

@ -8,6 +8,7 @@ namespace cylvester
{ {
private PdBackend pdBackend_; private PdBackend pdBackend_;
private SerializedProperty midiMessageReceivedProperty_; private SerializedProperty midiMessageReceivedProperty_;
private SerializedProperty midiClockReceivedProperty_;
private readonly string[] samples_ = private readonly string[] samples_ =
{ {
@ -36,6 +37,9 @@ namespace cylvester
midiMessageReceivedProperty_ = serializedObject.FindProperty("midiMessageReceived"); midiMessageReceivedProperty_ = serializedObject.FindProperty("midiMessageReceived");
EditorGUILayout.PropertyField(midiMessageReceivedProperty_); EditorGUILayout.PropertyField(midiMessageReceivedProperty_);
midiClockReceivedProperty_ = serializedObject.FindProperty("midiClockReceived");
EditorGUILayout.PropertyField(midiClockReceivedProperty_);
if (Application.isPlaying) if (Application.isPlaying)
{ {
RenderSamplePlayback(); RenderSamplePlayback();

View file

@ -212,6 +212,171 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 3 m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &560516510
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 560516514}
- component: {fileID: 560516513}
- component: {fileID: 560516512}
- component: {fileID: 560516511}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &560516511
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 560516510}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &560516512
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 560516510}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
--- !u!223 &560516513
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 560516510}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!224 &560516514
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 560516510}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 1790437804}
m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!1 &700239840
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 700239843}
- component: {fileID: 700239842}
- component: {fileID: 700239841}
m_Layer: 0
m_Name: EventSystem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &700239841
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 700239840}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalAxis: Horizontal
m_VerticalAxis: Vertical
m_SubmitButton: Submit
m_CancelButton: Cancel
m_InputActionsPerSecond: 10
m_RepeatDelay: 0.5
m_ForceModuleActive: 0
--- !u!114 &700239842
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 700239840}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_FirstSelected: {fileID: 0}
m_sendNavigationEvents: 1
m_DragThreshold: 10
--- !u!4 &700239843
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 700239840}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1261823479 --- !u!1 &1261823479
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -223,6 +388,7 @@ GameObject:
- component: {fileID: 1261823481} - component: {fileID: 1261823481}
- component: {fileID: 1261823480} - component: {fileID: 1261823480}
- component: {fileID: 1261823482} - component: {fileID: 1261823482}
- component: {fileID: 1261823483}
m_Layer: 0 m_Layer: 0
m_Name: PdBackend m_Name: PdBackend
m_TagString: Untagged m_TagString: Untagged
@ -267,6 +433,20 @@ MonoBehaviour:
m_StringArgument: m_StringArgument:
m_BoolArgument: 0 m_BoolArgument: 0
m_CallState: 2 m_CallState: 2
midiClockReceived:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 1261823483}
m_MethodName: onClockReceived
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
samplePlayback: 0 samplePlayback: 0
--- !u!4 &1261823481 --- !u!4 &1261823481
Transform: Transform:
@ -297,6 +477,19 @@ MonoBehaviour:
logAll: 0 logAll: 0
logFiltered: 0 logFiltered: 0
filterStatusByte: 176 filterStatusByte: 176
--- !u!114 &1261823483
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1261823479}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 10a68b6f417f08e428a5e9c5e21c485a, type: 3}
m_Name:
m_EditorClassIdentifier:
counter: {fileID: 1790437805}
--- !u!1 &1461024797 --- !u!1 &1461024797
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -480,6 +673,83 @@ MonoBehaviour:
enableFptlForForwardOpaque: 0 enableFptlForForwardOpaque: 0
enableBigTilePrepass: 0 enableBigTilePrepass: 0
isFptlEnabled: 0 isFptlEnabled: 0
--- !u!1 &1790437803
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1790437804}
- component: {fileID: 1790437806}
- component: {fileID: 1790437805}
m_Layer: 5
m_Name: Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1790437804
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1790437803}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 560516514}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -31.3}
m_SizeDelta: {x: 300, y: 90}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1790437805
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1790437803}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 32
m_FontStyle: 1
m_BestFit: 0
m_MinSize: 3
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 1:1:0
--- !u!222 &1790437806
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1790437803}
m_CullTransparentMesh: 0
--- !u!1 &2049244779 --- !u!1 &2049244779
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View file

@ -0,0 +1,21 @@

using UnityEngine;
using UnityEngine.UI;
public class MidiClockCounter : MonoBehaviour
{
[SerializeField] private Text counter;
private int count_ = 0;
public void onClockReceived()
{
count_++;
var tick = count_ % 24;
var beat = count_ / 24;
var measure = beat / 4;
counter.text = (measure+1) + ":" + (beat%4+1) + ":" + tick;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10a68b6f417f08e428a5e9c5e21c485a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -10,6 +10,7 @@ namespace cylvester
public void OnMidiMessageReceived(MidiMessage mes) public void OnMidiMessageReceived(MidiMessage mes)
{ {
if(logAll) if(logAll)
Debug.Log("MIDI Received: " + mes); Debug.Log("MIDI Received: " + mes);

View file

@ -4,7 +4,7 @@ using UnityEngine.Events;
namespace cylvester namespace cylvester
{ {
[Serializable] [Serializable]
public class UnityControlEvent : UnityEvent<MidiMessage> public class UnityMidiEvent : UnityEvent<MidiMessage>
{} {}
public struct MidiMessage public struct MidiMessage
@ -24,6 +24,8 @@ namespace cylvester
public interface IMidiParser : IDisposable public interface IMidiParser : IDisposable
{ {
event Action<MidiMessage> MidiMessageReceived; event Action<MidiMessage> MidiMessageReceived;
event Action MidiClockReceived;
} }
public class MidiParser : IMidiParser public class MidiParser : IMidiParser
@ -39,6 +41,7 @@ namespace cylvester
private Accept accept_ = Accept.StatusByte; private Accept accept_ = Accept.StatusByte;
private readonly IPdReceiver pdReceiver_; private readonly IPdReceiver pdReceiver_;
private readonly Action<byte[]> onDataReceived_; private readonly Action<byte[]> onDataReceived_;
private static byte MIDI_CLOCK = 248;
public MidiParser(IPdReceiver pdReceiver) public MidiParser(IPdReceiver pdReceiver)
{ {
@ -48,6 +51,9 @@ namespace cylvester
{ {
foreach (var element in bytes) foreach (var element in bytes)
{ {
if (element == MIDI_CLOCK)
MidiClockReceived?.Invoke();
if (element >= 128) if (element >= 128)
{ {
message_ = new MidiMessage {Status = element}; message_ = new MidiMessage {Status = element};
@ -63,7 +69,7 @@ namespace cylvester
else if (accept_ == Accept.DataByte2 && element <= 128) else if (accept_ == Accept.DataByte2 && element <= 128)
{ {
message_.Data2 = element; message_.Data2 = element;
Invoke(); MidiMessageReceived?.Invoke(message_);
accept_ = Accept.StatusByte; accept_ = Accept.StatusByte;
} }
} }
@ -72,17 +78,12 @@ namespace cylvester
pdReceiver_.DataReceived += onDataReceived_; pdReceiver_.DataReceived += onDataReceived_;
} }
private void Invoke()
{
MidiMessageReceived?.Invoke(message_);
}
public void Dispose() public void Dispose()
{ {
pdReceiver_.DataReceived -= onDataReceived_; pdReceiver_.DataReceived -= onDataReceived_;
} }
public event Action<MidiMessage> MidiMessageReceived; public event Action<MidiMessage> MidiMessageReceived;
public event Action MidiClockReceived;
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
namespace cylvester namespace cylvester
{ {
@ -14,7 +15,9 @@ namespace cylvester
public class PdBackend : MonoBehaviour, IPdBackend public class PdBackend : MonoBehaviour, IPdBackend
{ {
[SerializeField] UnityControlEvent midiMessageReceived = null; [SerializeField] UnityMidiEvent midiMessageReceived = null;
[SerializeField] UnityEvent midiClockReceived = null;
public int samplePlayback; public int samplePlayback;
@ -33,6 +36,7 @@ namespace cylvester
private Action onSamplePlaybackChanged_; private Action onSamplePlaybackChanged_;
private Action<MidiMessage> onMidiMessageReceived_; private Action<MidiMessage> onMidiMessageReceived_;
private Action onMidiClockReceived_;
private void Awake() private void Awake()
{ {
@ -53,13 +57,12 @@ namespace cylvester
onSamplePlaybackChanged_ = () => { pdSender_.Send(new[]{(byte)PdMessage.SampleSound, (byte)samplePlayback}); }; onSamplePlaybackChanged_ = () => { pdSender_.Send(new[]{(byte)PdMessage.SampleSound, (byte)samplePlayback}); };
onMidiMessageReceived_ = (message) => { onMidiMessageReceived_ = (message) => { midiMessageReceived.Invoke(message); };
midiMessageReceived.Invoke(message); onMidiClockReceived_ = () => { midiClockReceived.Invoke(); };
};
samplePlaybackObserver_.ValueChanged += onSamplePlaybackChanged_; samplePlaybackObserver_.ValueChanged += onSamplePlaybackChanged_;
midiParser_.MidiMessageReceived += onMidiMessageReceived_; midiParser_.MidiMessageReceived += onMidiMessageReceived_;
midiParser_.MidiClockReceived += onMidiClockReceived_;
dspController_.State = true; dspController_.State = true;
} }
@ -69,6 +72,8 @@ namespace cylvester
pdSender_?.Dispose(); pdSender_?.Dispose();
samplePlaybackObserver_.ValueChanged -= onSamplePlaybackChanged_; samplePlaybackObserver_.ValueChanged -= onSamplePlaybackChanged_;
midiParser_.MidiMessageReceived -= onMidiMessageReceived_; midiParser_.MidiMessageReceived -= onMidiMessageReceived_;
midiParser_.MidiClockReceived -= onMidiClockReceived_;
} }
public void Update() public void Update()

View file

@ -1,6 +1,6 @@
#N canvas 216 302 670 468 10; #N canvas 758 125 670 468 10;
#X obj 125 345 adc~ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; #X obj 142 343 adc~ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16;
#X obj 55 386 dac~, f 6; #X obj 72 384 dac~, f 6;
#N canvas 215 619 730 390 analyzers_______________________________ #N canvas 215 619 730 390 analyzers_______________________________
0; 0;
#X obj 95 44 inlet~; #X obj 95 44 inlet~;
@ -51,11 +51,11 @@
#X connect 13 0 29 0; #X connect 13 0 29 0;
#X connect 14 0 30 0; #X connect 14 0 30 0;
#X connect 15 0 31 0; #X connect 15 0 31 0;
#X restore 126 385 pd analyzers_______________________________; #X restore 143 383 pd analyzers_______________________________;
#X obj 46 26 bang~; #X obj 63 24 bang~;
#X obj 46 49 count 16; #X obj 63 47 count 16;
#X obj 46 76 sel 0; #X obj 63 74 sel 0;
#X obj 46 102 s shmemupdate; #X obj 63 100 s shmemupdate;
#N canvas 763 290 408 235 window 0; #N canvas 763 290 408 235 window 0;
#N canvas 0 0 450 300 (subpatch) 0; #N canvas 0 0 450 300 (subpatch) 0;
#X array hann 1024 float 1; #X array hann 1024 float 1;
@ -194,7 +194,7 @@
0.000244498 0.000150442 9.39965e-005 3.75807e-005 1.87755e-005; 0.000244498 0.000150442 9.39965e-005 3.75807e-005 1.87755e-005;
#X coords 0 1 1023 0 300 100 1 0 0; #X coords 0 1 1023 0 300 100 1 0 0;
#X restore 39 32 graph; #X restore 39 32 graph;
#X restore 533 23 pd window; #X restore 550 21 pd window;
#N canvas 2182 728 332 292 commands_from_unity 0; #N canvas 2182 728 332 292 commands_from_unity 0;
#X obj 66 31 inlet; #X obj 66 31 inlet;
#X obj 130 130 s sample_playback; #X obj 130 130 s sample_playback;
@ -209,7 +209,7 @@
#X connect 4 0 6 0; #X connect 4 0 6 0;
#X connect 4 1 1 0; #X connect 4 1 1 0;
#X connect 6 0 7 0; #X connect 6 0 7 0;
#X restore 231 60 pd commands_from_unity; #X restore 248 58 pd commands_from_unity;
#N canvas 667 603 480 371 sample_playback 0; #N canvas 667 603 480 371 sample_playback 0;
#X obj 86 166 readsf~; #X obj 86 166 readsf~;
#X obj 95 6 r sample_playback; #X obj 95 6 r sample_playback;
@ -235,29 +235,30 @@
#X connect 9 0 7 0; #X connect 9 0 7 0;
#X connect 10 0 0 0; #X connect 10 0 0 0;
#X connect 11 0 0 0; #X connect 11 0 0 0;
#X restore 55 306 pd sample_playback; #X restore 72 304 pd sample_playback;
#X obj 231 22 netreceive -u -b 54345; #X obj 248 20 netreceive -u -b 54345;
#X obj 45 140 text define -k samplefiles; #X obj 62 138 text define -k samplefiles;
#A set Back_Back.wav \; Brutal_Synth.wav \; Dialog.wav \; Drums.wav #A set Back_Back.wav \; Brutal_Synth.wav \; Dialog.wav \; Drums.wav
\; Fox_Melo.wav \; Kick.wav \; Pads+Strings.wav \; Rose_Sax.wav \; \; Fox_Melo.wav \; Kick.wav \; Pads+Strings.wav \; Rose_Sax.wav \;
Roses_Front.wav; Roses_Front.wav;
#X obj 231 245 netsend -u -b; #X obj 248 243 netsend -u -b;
#X msg 231 144 connect localhost 56765; #X msg 248 142 connect localhost 56765;
#X obj 253 114 loadbang; #X obj 270 112 loadbang;
#X obj 86 199 list prepend send; #X obj 103 197 list prepend send;
#X obj 86 223 list trim; #X obj 103 221 list trim;
#X obj 86 175 midiin; #X obj 103 173 midiin;
#X msg 410 115 23 31; #X msg 427 113 23 31;
#X msg 457 115 80 84, f 10; #X msg 474 113 80 84, f 10;
#X text 422 89 sample CC; #X text 439 87 sample CC;
#X msg 424 161 send 176 \$1 \$2; #X msg 441 159 send 176 \$1 \$2;
#X text 423 183 176... control channel 1; #X text 440 181 176... control channel 1;
#N canvas 695 332 450 300 level 1; #N canvas 695 332 450 300 level 0;
#X obj 90 182 table level 16; #X obj 90 182 table level 16;
#X obj 89 152 shmem level 16; #X obj 89 152 shmem level 16;
#X obj 89 127 r shmemupdate; #X obj 89 127 r shmemupdate;
#X connect 2 0 1 0; #X connect 2 0 1 0;
#X restore 532 55 pd level; #X restore 549 53 pd level;
#X obj 12 173 midirealtimein;
#X connect 0 0 2 0; #X connect 0 0 2 0;
#X connect 0 1 2 1; #X connect 0 1 2 1;
#X connect 0 2 2 2; #X connect 0 2 2 2;
@ -290,3 +291,4 @@ Roses_Front.wav;
#X connect 18 0 21 0; #X connect 18 0 21 0;
#X connect 19 0 21 0; #X connect 19 0 21 0;
#X connect 21 0 12 0; #X connect 21 0 12 0;
#X connect 24 0 15 0;