Problem?
When making videos, sometimes you want to add some text animations. And since not everyone has access to commercial tools like After Effects, it would be nice if a free alternative existed.
Hobbyist blog about all things software. Any correspondence of this blog's name to names of individuals, organizations, companies, products or other blogs is pure coincidence.
timer = Timer.new()timer.autostart = truetimer.one_shot = falsetimer.wait_time = 0.1timer.connect("timeout", self, "_on_timeout")add_child(timer)
extends Sprite
func _ready():
get_tree().get_root().get_node("RootNode").register_dual_arg_callback("/set/xy", get_node("."), "set_xy")
func set_xy(x, y):
self.position.x = x
self.position.y = y
(
b = NetAddr.new("127.0.0.1", 4242); // create the NetAddr
b.sendMsg("/set/xy", 650, 200);
)
extends Node2D var IP_CLIENT var PORT_CLIENT var PORT_SERVER = 4242 # change me if you like! var IP_SERVER = "127.0.0.1" # change me if you like! var socketUDP = PacketPeerUDP.new() var observers = Dictionary() func register_single_arg_callback(oscaddress, node, functionname): observers[oscaddress] = [1, node, functionname] func register_dual_arg_callback(oscaddress, node, functionname): observers[oscaddress] = [2, node, functionname] func _ready(): OS.set_low_processor_usage_mode(true) start_server() func all_zeros(lst): if lst == []: return true for el in lst: if el != 0: return false return true func _process(_delta): if socketUDP.get_available_packet_count() > 0: var array_bytes = socketUDP.get_packet() #var IP_CLIENT = socketUDP.get_packet_ip() #var PORT_CLIENT = socketUDP.get_packet_port() var stream = StreamPeerBuffer.new() stream.set_data_array(array_bytes) stream.set_big_endian(true) var address_finished = false var type_finished = false var address = "" var type = ""
# parse osc address
while not address_finished:
for _i in range(4):
var addrpart = stream.get_u8()
if addrpart != 0:
address += char(addrpart)
if addrpart == 0:
address_finished = true
# parse osc type list
while not type_finished:
for _i in range(4):
var c = stream.get_u8()
if c != 0 and char(c) != ",":
type += char(c)
if c == 0:
type_finished = true
# decode values from the stream
var values = []
for type_id in type:
if type_id == "i":
var intval = stream.get_32()
values.append(intval)
elif type_id == "f":
var floatval = stream.get_float()
values.append(floatval)
elif type_id == "s":
var stringval = ""
var string_finished = false
while not string_finished:
for _i in range(4):
var ch = stream.get_u8()
if ch != 0:
stringval += char(ch)
else:
string_finished = true
values.append(stringval)
elif type_id == "b":
var data = []
var count = stream.get_u32()
var idx = 0
var blob_finished = false
while not blob_finished:
for _i in range(4):
var ch = stream.get_u8()
if idx < count:
data.append(ch)
idx += 1
if idx >= count:
blob_finished = true
values.append(data)
else:
printt("type " + type_id +" not yet supported")
if observers.has(address):
var observer = observers[address]
var number_args = observer[0]
var nodepath = observer[1]
var funcname = observer[2]
if number_args == 1:
nodepath.call(funcname, values[0])
elif number_args == 2:
nodepath.call(funcname, values[0], values[1])
func start_server():
if (socketUDP.listen(PORT_SERVER) != OK):
printt("Error listening on port: " + str(PORT_SERVER))
else:
printt("Listening on port: " + str(PORT_SERVER))
func _exit_tree():
socketUDP.close()
~n = [60, 64, 67]; // c major chord
Quarks.install("https://github.com/shimpe/panola");
~n = Panola("c4 e4 g4").midinotePattern.asStream.all;
(
s.waitForBoot({
var arp = Pbind(
\instrument, \default,
\midinote, Plazy {
var n0, n1, n2;
~n = ~n ?? [Rest(1)];
n0 = ~n[0] ?? Rest(1);
n1 = ~n[1] ?? ~n[0];
n2 = ~n[2] ?? ~n[0];
Pseq([n0, n2, n1, n2])
},
\dur, Pseq([1,1,1,1].normalizeSum*2)
);
if (~player.notNil) { ~player.stop; };
~player = Pn(arp).play;
});
)
if (~player.notNil) { ~player.stop; }; // call stop if not stopped already
~player = Pn(arp).play;
~n = [60, 64, 67 ];
~n =[ 60, 65, 69 ];
~n =[ 59, 65, 67 ];
Exercise: adapt the code to make an arpeggio based on the first four notes of a list of input notes.
Exercise: adapt the code to use different durations for different notes
(
s.waitForBoot({
var right, left;
~n = ~n ?? [Rest(1)];
right = Pbind(
\instrument, \default,
\midinote, Plazy {
var n0, n1, n2;
~n = ~n ?? [Rest(1)];
n0 = ~n[0] ?? Rest(1);
n1 = ~n[1] ?? ~n[0];
n2 = ~n[2] ?? ~n[0];
Pseq([ n0, n2, n1, n2 ] ++ (([ n0, n2, n1, n2 ] + 12)!2).flatten)
},
\dur, Pseq([1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ].normalizeSum*2)
);
left = Pbind(
\instrument, \default,
\midinote, Plazy {
var n0, n1, n2;
~n = ~n ?? [Rest(1)];
n0 = ~n[0] ?? Rest(1);
n1 = ~n[1] ?? ~n[0];
n2 = ~n[2] ?? ~n[0];
Pseq([ n0, n2, n0, n2, n0, n2, n0, n2] - 12)
},
\dur, Pseq([1, 1, 1, 1, 1, 1, 1, 1].normalizeSum*2)
);
if (~player.notNil) { ~player.stop; };
~player = Pn(Ppar([right,left])).play;
});
)
Exercise: define some percussion instruments and add some percussion to the fragment.
( MIDIdef.freeAll; MIDIClient.init; MIDIIn.connectAll; )
~n = ~note_table.selectIndices({|item, i| item != 0});
if (~n == []) { ~n = nil; };
(
MIDIdef.freeAll;
MIDIClient.init;
MIDIIn.connectAll;
)
(
s.waitForBoot({
var right, left;
~note_table = 0!127;
MIDIdef.noteOn(
\mynoteonhandler, // just a name for this handler
{
|val, num, chan, src|
num.debug("num");
~note_table[num] = 1; // update note table and update ~n
~n = ~note_table.selectIndices({|item, i| item != 0}).postln;
}
);
MIDIdef.noteOff(
\mynoteoffhandler, // just a name for this handler
{
|val, num, chan, src|
num.debug("num");
~note_table[num] = 0; // update note table and update ~n
}
);
right = Pbind(
\instrument, \default,
\midinote, Plazy {
var n0, n1, n2;
~n = ~n ?? [Rest(1)];
n0 = ~n[0] ?? Rest(1);
n1 = ~n[1] ?? ~n[0];
n2 = ~n[2] ?? ~n[0];
Pxrand([ n0, n2, n1, n2 ] ++ (([ n0, n2, n1, n2 ] + 12)!2).flatten)
},
\dur, Pseq([1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ].normalizeSum*2)
);
left = Pbind(
\instrument, \default,
\midinote, Plazy {
var n0, n1, n2;
~n = ~n ?? [Rest(1)];
n0 = ~n[0] ?? Rest(1);
n1 = ~n[1] ?? ~n[0];
n2 = ~n[2] ?? ~n[0];
Pseq([ n0, n2, n0, n2, n0, n2, n0, n2] - 12)
},
\dur, Pseq([1, 1, 1, 1, 1, 1, 1, 1].normalizeSum*2)
);
if (~player.notNil) { ~player.stop; };
~player = Pn(Ppar([right,left])).play;
});
)
(
s.waitForBoot({
var right, left;
var n0, n1, n2;
MIDIdef.freeAll;
if (~midi_initialized.isNil) {
MIDIClient.init;
MIDIIn.connectAll;
~midi_initialized = 1;
};
~note_table = 0!127;
~n = nil;
MIDIdef.noteOn(
\mynoteonhandler, // just a name for this handler
{
|val, num, chan, src|
~note_table[num] = 1; // update note table and update ~n
~n = ~note_table.selectIndices({|item, i| item != 0});
}
);
MIDIdef.noteOff(
\mynoteoffhandler, // just a name for this handler
{
|val, num, chan, src|
~note_table[num] = 0; // update note table and update ~n
/*
// enable next two lines only if you want arpeggios to stop playing
// when you release the midi keys
~n = ~note_table.selectIndices({|item, i| item != 0});
if (~n == []) { ~n = nil; };
*/
}
);
n0 = Plazy {
if (~n.isNil) {
Pseq([Rest(1)]);
} {
~n[0] ?? Pseq([Rest(1)]);
};
};
n1 = Plazy {
if (~n.isNil) {
Pseq([Rest(1)]);
} {
Pseq([~n[1] ?? ~n[0]]);
};
};
n2 = Plazy {
if (~n.isNil) {
Pseq([Rest(1)]);
} {
Pseq([~n[2] ?? ~n[0]]);
};
};
right = Pbind(
\instrument, \default,
\midinote, Pseq([ n0, n2, n1, n2] ++ (([ n0, n2, n1, n2] + 12)!2).flatten),
\dur, Pseq([1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ].normalizeSum*2)
);
left = Pbind(
\instrument, \default,
\midinote, Pseq([ n0, n2, n0, n2, n0, n2, n0, n2] - 12),
\dur, Pseq([1, 1, 1, 1, 1, 1, 1, 1].normalizeSum*2)
);
if (~player.notNil) { ~player.stop; };
~player = Pn(Ppar([right,left])).play;
});
)
(
s.waitForBoot({
var right, left;
var n0, n1, n2;
var note_getter;
MIDIdef.freeAll;
if (~midi_initilized.isNil) {
MIDIClient.init;
MIDIIn.connectAll;
~midi_initialized = 1;
};
~note_table = 0!127;
~n = nil;
MIDIdef.noteOn(
\mynoteonhandler, // just a name for this handler
{
|val, num, chan, src|
~note_table[num] = 1; // update note table and update ~n
~n = ~note_table.selectIndices({|item, i| item != 0});
}
);
MIDIdef.noteOff(
\mynoteoffhandler, // just a name for this handler
{
|val, num, chan, src|
~note_table[num] = 0; // update note table and update ~n
/*
// only enable the following lines if you want the arpeggio to stop as soon as you release the keys
~n = ~note_table.selectIndices({|item, i| item != 0});
if (~n == []) { ~n = nil; };
*/
}
);
note_getter = {
| index |
Plazy {
if (~n.isNil) {
Pseq([Rest(1)]);
} {
~n[index] ?? (~n[0] ?? Pseq([Rest(1)]));
};
};
};
n0 = note_getter.(0);
n1 = note_getter.(1);
n2 = note_getter.(2);
right = Pbind(
\instrument, \default,
\midinote, Pseq([ n0, n2, n1, n2] ++ (([ n0, n2, n1, n2] + 12)!2).flatten),
\dur, Pseq([1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ].normalizeSum*2)
);
left = Pbind(
\instrument, \default,
\midinote, Pseq([ n0, n2, n0, n2, n0, n2, n0, n2] - 12),
\dur, Pseq([1, 1, 1, 1, 1, 1, 1, 1].normalizeSum*2)
);
if (~player.notNil) { ~player.stop; };
~player = Pn(Ppar([right,left])).play;
});
)
)
~walsh_transform.(values:[-1,1,0,2]);we get back the expected result:
[0.5,-0.5,0,-1]And to check that the inverse transform works as expected:
~walsh_transform.(values:~walsh_transform.(values:[-1,1,0,2]), rescale:false);gives the original signal:
[-1,1,0,2].