Completed test csd for Ableton Link opcodes.

<CsoundSynthesizer>
<CsLicense>
T E S T S   F O R   A B L E T O N   L I N K   O P C O D E S

Michael Gogins
12 January 2017

These cover most of the things in Ableton Link's TEST-PLAN.md. This csd is to
be used on conjunction with the command-line LinkHut application in the
Ableton Link Git repository.

There are two sections. In the first, we start LinkHut first and use it to
control Csound's tempo. In the second, we start Csound first and use it to
conrol LinkHut's tempo.

This csd automatically performs its tests. The user has to manually operate
LinkHut as instructed below.

In all cases system audio should be enabled so the user can hear the beat, and
both Csound and LinkHut should be run from the command line in separate,
simultaeously visible terminal windows so the user can compare the diagnostic
messages.

FIRST LINKHUT, THEN CSOUND

Start LinkHut, then start this csd.

Test 1 -- Join Session without Changing Session Tempo, and Tempo Sync

At first, both peers should show a tempo of 120 which is the default for
LinkHut. Change tempo in LinkHut. Tempo in Csound should sync. This csd will
change tempo to 80 after 10 seconds. LinkHut should sync.

Test 2 -- Tempo Range

Use LinkHut to change tempo from 20 to 999. Both sessions should show the full
range.

Test 3 -- Tempo Stability

After 20 seconds this csd should disable Link, and after another 10 seconds
Link should be re-enabled. The session tempo and beat should not change. Then,
in LinkHut, manually disable and re-enable Link. The session tempo and beat
should not change.

Test 4 -- Beat stability

Kill and restart LinkHut while this csd is running. Enable and disable Link in
LinkHut. The beat should continue without any temporal or rhythmic discontinuity.

FIRST CSOUND, THEN LINKHUT

Start this csd, then start LinkHut.

Test 5 -- Join Session without Changing Session Tempo, and Tempo Sync

At first, both peers should show a tempo of 72 which is the default for this
csd. Change tempo in LinkHut. Tempo in Csound should sync. This csd will
change tempo to 80 after 10 seconds. LinkHut should sync.

Test 6 -- Beat stability

Kill and restart this csd while LinkHut is running. Enable and disable Link in
LinkHut. The beat should continue without any temporal or rhythmic
discontinuity.

NOTE: At this time, Csound does not implement delay compensation, so testing
for delay compensation is not applicable. By using the smallest practical
audio latency in Csound, however, the sync should not be much looser than
Ableton's minimum requirement of 3 milliseconds. This is approximately the
accuracy of a really good performing musician.

</CsLicense>
<CsOptions>
-odac -m0 -d
</CsOptions>

<CsInstruments>
sr=96000
ksmps=10
nchnls=2

alwayson "LinkMonitor"

gi_link init 0
gk_beat init 0

instr Beep
asignal oscils 20000, p4, 0
outs asignal, asignal
endin

instr TempoChange
link_tempo_set gi_link, 80
endin

instr LinkEnable
i_enabled = p4
link_enable gi_link, i_enabled
endin

instr LinkMonitor
i_kperiod_seconds = ksmps / sr
printf_i "kperiod: %9.6f seconds.\n", 1, i_kperiod_seconds
gi_link link_create 72
printf_i "gi_link: %g\n", 1, gi_link
link_enable gi_link, 1
k_trigger, gk_beat, k_phase, k_time link_metro gi_link, 1
k_peers link_peers gi_link
k_tempo link_tempo_get gi_link
k_enabled link_is_enabled gi_link
k_hz = 1000
if floor(gk_beat % 4) == 0 then
k_hz = 3000
else
k_hz = 2000
endif
schedkwhen k_trigger, 0, 1, "Beep", 0, 0.01, k_hz
printf "LinkMonitor: gi_link: %12g k_enabled: %9.4f k_trigger: %9.4f beat: %9.4f k_phase: %9.4f time: %9.4f tempo: %9.4f peers: %3d\n", k_trigger, gi_link, k_enabled, k_trigger, gk_beat, k_phase, k_time, k_tempo, k_peers
endin

</CsInstruments>

<CsScore>
f 0 360
i "TempoChange" 10 80
i "LinkEnable" 20 1 0
i "LinkEnable" 30 1 1
</CsScore>

</CsoundSynthesizer>

<CsoundSynthesizer>
<CsLicense>
T E S T S   F O R   A B L E T O N   L I N K   O P C O D E S
Michael Gogins
12 January 2017

These test most of the things in Ableton Link's TEST-PLAN.md.

Test 1 -- Tempo sync

Run LinkHut and enable it. Run this csd. This csd should show a tempo of 120.
Change tempo in LinkHut. Tempo in Csound should sync. This csd will change
tempo to 80 after 10 seconds. LinkHut should sync.

Test 2 -- Join session without changing session tempo.

Run LinkHut and enable it (defaults to 120 bpm). Run this csd (defaults to 72
bpm). The tempo should be 120 bpm in both sessions.

Run this csd. Run LinkHut. The tempo should be 72 bpm in both sessions.

Test 3 -- Tempo range

Run LinkHut and enable it. Run this csd. Use LinkHut to change tempo from 20
to 999. Both sessions should show the full range.

Test 4 -- Tempo stability

Run this csd. After 20 seconds Link should be disabled, and after another 10
seconds Link should be re-enabled. The tempo should not change.

Run LinkHut. Enable and disable Link. The tempo should not change.

Test 5 -- Beat stability

Kill LinkHut. Run this csd. The beat should continue with any time or rhythmic discontinuity as Link is enabled and disabled by this csd.

Restart LinkHut while this csd is running. Enable and disable Link in LinkHut. The beat should continue without any temporal or rhythmic discontinuity.

NOTE: At this time, Csound does not implement delay compensation, so testing
for delay compensation is not applicable. By using the smallest practical
audio latency in Csound, however, the sync should not be much looser than
Ableton's minimum requirement of 3 milliseconds. This is approximately the
accuracy of a really good performing musician.

</CsLicense>
<CsOptions>
-odac
</CsOptions>

<CsInstruments>
sr=44100
kr=441
ksmps=100
nchnls=2
alwayson "LinkMonitor"

instr Beep
asignal oscils 10000, 880, 0
outs asignal, asignal
endin

instr LinkMonitor
i_link link_create 72
printf_i "i_link: %g\n", 1, i_link
link_enable i_link, 1
k_trigger, k_beat, k_phase, k_time link_metro i_link, 4
k_peers link_peers i_link
k_tempo link_tempo_get i_link
k_enabled link_is_enabled i_link
schedkwhen k_trigger, 0, 1, "Beep", 0, 0.01
printf "LinkMonitor: i_link: %d k_enabled: %f k_trigger: %f beat: %f k_phase: %f time: %f tempo: %f peers: %f\n", k_trigger, i_link, k_enabled, k_trigger, k_beat, k_phase, k_time, k_tempo, k_peers
endin

</CsInstruments>

<CsScore>
f 0 360
</CsScore>

</CsoundSynthesizer>


Fixed luaopcode.csd example for STRINGDAT.

<CsoundSynthesizer>

<CsInstruments>
lua_opdef "luatest", {{
local ffi = require("ffi")
local string = require("string")
local csoundLibrary = ffi.load('csound64.dll')
ffi.cdef[[
    int csoundGetKsmps(void *);
    double csoundGetSr(void *);
    struct STRINGDAT {char *data; int size;};
    struct luatest_arguments {double *out; struct STRINGDAT *stringout; struct STRINGDAT *stringin; double *in1; double *in2; double sr; int ksmps; };
]]
function luatest_init(csound, opcode, carguments)
    local arguments = ffi.cast("struct luatest_arguments *", carguments)
    arguments.sr = csoundLibrary.csoundGetSr(csound)
    print(string.format('stringin: %s', ffi.string(arguments.stringin.data, arguments.stringin.size)))
    print(string.format('sr: %f', arguments.sr))
    arguments.ksmps = csoundLibrary.csoundGetKsmps(csound)
    print(string.format('ksmps: %d', arguments.ksmps))
    arguments.out[0] = arguments.in1[0] * arguments.in2[0]
    ffi.copy(arguments.stringout.data, 'Hello, world!')
    return 0
end
function luatest_kontrol(csound, opcode, carguments)
    local arguments = ffi.cast("struct luatest_arguments *", carguments)
    arguments.out[0] = arguments.in1[0] * arguments.in2[0]
    return 0
end
function luatest_noteoff(csound, opcode, carguments)
    local arguments = ffi.cast("struct luatest_arguments *", carguments)
    arguments.out[0] = arguments.in1[0] * arguments.in2[0]
    print('off')
    return 0
end
}}

instr 1
    iresult = 0
    Stringin = "stringin"
    Stringout = "stringout"
    lua_iopcall "luatest", iresult, Stringout, Stringin, p2, p3
    prints Stringout
endin
instr 2
    iresult = 0
    Stringin = "stringin"
    Stringout = "initial value"
    lua_iopcall_off "luatest", iresult, Stringout, Stringin, p2, p3
    print iresult
    prints Stringout
endin
</CsInstruments>

<CsScore>
i 1 1 2
i 2 2 2
i 1 3 2
e
</CsScore>

</CsoundSynthesizer>
<bsbPanel>
 Widgets
 <objectName/>
 <x>72</x>
 <y>179</y>
 <width>400</width>
 <height>200</height>
 <visible>true</visible>
 <uuid/>
 <bgcolor mode="nobackground">
  <r>231</r>
  <g>46</g>
  255
 </bgcolor>
 <bsbObject version="2" type="BSBVSlider">
  <objectName>slider1</objectName>
  <x>5</x>
  <y>5</y>
  <width>20</width>
  <height>100</height>
  <uuid>{68aef6e9-fc92-45d7-9c08-0ba0f7162849}</uuid>
  <visible>true</visible>
  <midichan>0</midichan>
  <midicc>-3</midicc>
  <minimum>0.00000000</minimum>
  <maximum>1.00000000</maximum>
  <value>0.00000000</value>
  <mode>lin</mode>
  <mouseControl act="jump">continuous</mouseControl>
  <resolution>-1.00000000</resolution>
  <randomizable group="0">false</randomizable>
 </bsbObject>
</bsbPanel>
<bsbPresets>
</bsbPresets>
<MacOptions>
Version: 3
Render: Real
Ask: Yes
Functions: ioObject
Listing: Window
WindowBounds: -900 -700 400 200
CurrentView: io
IOViewEdit: On
Options:
</MacOptions>

<MacGUI>
ioView nobackground {59367, 11822, 65535}
ioSlider {5, 5} {20, 100} 0.000000 1.000000 0.000000 slider1
</MacGUI>

<CsoundSynthesizer>

<CsInstruments>
lua_opdef "luatest", {{
local ffi = require("ffi")
local string = require("string")
local csoundLibrary = ffi.load('libcsound64.so')
ffi.cdef[[
    int csoundGetKsmps(void *);
    double csoundGetSr(void *);
    struct luatest_arguments {double *out; double *stringout; char *stringin; double *in1; double *in2; double sr; int ksmps; };
]]
function luatest_init(csound, opcode, carguments)
    local arguments = ffi.cast("struct luatest_arguments *", carguments)
    arguments.sr = csoundLibrary.csoundGetSr(csound)
    print(string.format('stringin: %s', ffi.string(arguments.stringin)))
    print(string.format('sr: %f', arguments.sr))
    arguments.ksmps = csoundLibrary.csoundGetKsmps(csound)
    print(string.format('ksmps: %d', arguments.ksmps))
    arguments.out[0] = arguments.in1[0] * arguments.in2[0]
    ffi.copy(arguments.stringout, 'Hello, world!')
    return 0
end
function luatest_kontrol(csound, opcode, carguments)
    local arguments = ffi.cast("struct luatest_arguments *", carguments)
    arguments.out[0] = arguments.in1[0] * arguments.in2[0]
    return 0
end
function luatest_noteoff(csound, opcode, carguments)
    local arguments = ffi.cast("struct luatest_arguments *", carguments)
    arguments.out[0] = arguments.in1[0] * arguments.in2[0]
    print('off')
    return 0
end
}}

instr 1
    iresult = 0
    Stringin = "stringin"
    Stringout = "stringout"
    lua_iopcall "luatest", iresult, Stringout, Stringin, p2, p3
    prints Stringout
endin
instr 2
    iresult = 0
    Stringin = "stringin"
    Stringout = "initial value"
    lua_iopcall_off "luatest", iresult, Stringout, Stringin, p2, p3
    print iresult
    prints Stringout
endin
</CsInstruments>

<CsScore>
i 1 1 2
i 2 2 2
i 1 3 2
e
</CsScore>

</CsoundSynthesizer>
<bsbPanel>
 Widgets
 <objectName/>
 <x>72</x>
 <y>179</y>
 <width>400</width>
 <height>200</height>
 <visible>true</visible>
 <uuid/>
 <bgcolor mode="nobackground">
  <r>231</r>
  <g>46</g>
  255
 </bgcolor>
 <bsbObject version="2" type="BSBVSlider">
  <objectName>slider1</objectName>
  <x>5</x>
  <y>5</y>
  <width>20</width>
  <height>100</height>
  <uuid>{68aef6e9-fc92-45d7-9c08-0ba0f7162849}</uuid>
  <visible>true</visible>
  <midichan>0</midichan>
  <midicc>-3</midicc>
  <minimum>0.00000000</minimum>
  <maximum>1.00000000</maximum>
  <value>0.00000000</value>
  <mode>lin</mode>
  <mouseControl act="jump">continuous</mouseControl>
  <resolution>-1.00000000</resolution>
  <randomizable group="0">false</randomizable>
 </bsbObject>
</bsbPanel>
<bsbPresets>
</bsbPresets>
<MacOptions>
Version: 3
Render: Real
Ask: Yes
Functions: ioObject
Listing: Window
WindowBounds: -900 -700 400 200
CurrentView: io
IOViewEdit: On
Options:
</MacOptions>

<MacGUI>
ioView nobackground {59367, 11822, 65535}
ioSlider {5, 5} {20, 100} 0.000000 1.000000 0.000000 slider1
</MacGUI>


updated test

updated test

<CsoundSynthesizer>
<CsOptions>
-d
</CsOptions>
<CsInstruments>

opcode Test,k,[k]

kS[] xin
k1 = kS[0]
   xout k1

endop

instr 1

kS[] init 2
ks[0] = 1
k2 Test kS
   printk2 k2

endin

</CsInstruments>
<CsScore>

i1 0 1

</CsScore>
</CsoundSynthesizer>
<bsbPanel>
 Widgets
 <objectName/>
 <x>614</x>
 <y>61</y>
 <width>695</width>
 <height>747</height>
 <visible>true</visible>
 <uuid/>
 <bgcolor mode="nobackground">
  <r>231</r>
  <g>46</g>
  255
 </bgcolor>
 <bsbObject version="2" type="BSBVSlider">
  <objectName>slider1</objectName>
  <x>5</x>
  <y>5</y>
  <width>20</width>
  <height>100</height>
  <uuid>{fe924818-39b1-44b5-b240-331a49ca0049}</uuid>
  <visible>true</visible>
  <midichan>0</midichan>
  <midicc>-3</midicc>
  <minimum>0.00000000</minimum>
  <maximum>1.00000000</maximum>
  <value>0.00000000</value>
  <mode>lin</mode>
  <mouseControl act="jump">continuous</mouseControl>
  <resolution>-1.00000000</resolution>
  <randomizable group="0">false</randomizable>
 </bsbObject>
</bsbPanel>
<bsbPresets>
</bsbPresets>
<MacGUI>
ioView nobackground {59367, 11822, 65535}
ioSlider {5, 5} {20, 100} 0.000000 1.000000 0.000000 slider1
</MacGUI>

<CsoundSynthesizer>
<CsOptions>
-d
</CsOptions>
<CsInstruments>

opcode Test,k,[k]

kS[] xin
k1 = kS[0]
   xout ks1

endop

instr 1

;kS[] init 2
; ks[0] = 1
;k2 Test kS[]
   ;printk2 k2

endin

</CsInstruments>
<CsScore>

i1 0 1

</CsScore>
</CsoundSynthesizer>
<bsbPanel>
 Widgets
 <objectName/>
 <x>614</x>
 <y>61</y>
 <width>695</width>
 <height>747</height>
 <visible>true</visible>
 <uuid/>
 <bgcolor mode="nobackground">
  <r>231</r>
  <g>46</g>
  255
 </bgcolor>
 <bsbObject version="2" type="BSBVSlider">
  <objectName>slider1</objectName>
  <x>5</x>
  <y>5</y>
  <width>20</width>
  <height>100</height>
  <uuid>{fe924818-39b1-44b5-b240-331a49ca0049}</uuid>
  <visible>true</visible>
  <midichan>0</midichan>
  <midicc>-3</midicc>
  <minimum>0.00000000</minimum>
  <maximum>1.00000000</maximum>
  <value>0.00000000</value>
  <mode>lin</mode>
  <mouseControl act="jump">continuous</mouseControl>
  <resolution>-1.00000000</resolution>
  <randomizable group="0">false</randomizable>
 </bsbObject>
</bsbPanel>
<bsbPresets>
</bsbPresets>
<MacGUI>
ioView nobackground {59367, 11822, 65535}
ioSlider {5, 5} {20, 100} 0.000000 1.000000 0.000000 slider1
</MacGUI>