Thursday, 23 May 2024

NZ Society

increasing non-attendance in schools over the last 10-15 years

From a Reddit thread, TW dramatic visions of malcontents

"Parents simply didn't believe you could close schools for months and then go back to insisting that every day of school was essential."

If those parents are anything like me they're seeing that the social contract has been broken, it's dog eat dog, money over everything, and we're one more toilet paper shortage away from societal collapse.


One of my sons is a poster child for the problem....

* Repeated absences. Often I wasn't even told he was absent.

* Desperately, curl up naked on a icy cold winter bathroom floor rather than climb in shower and go to school, level of unhappy.

* Totally lost in many subjects, but got no help from a high decile school.

* Still can't count his change.

If I had a do over, I wouldn't have sent him to school at all.

All it did was permanently damage him, especially his self esteem, and my relationship with him and taught him nothing.

I now have absolutely totally zero respect for the NZ education system.

If you fit nicely in their box like my other children did. Sort of OK. Not good, but at least they got a little bit out of it.

If you don't.

It is literally soul destroying, for way less than zero benefit.

We go on and on and on and on about mental health and youth suicide.... but when it comes to facing the source and fixing the education system.... we're all talk and zero do.

Sure we hand out psychiatric medicine like lollies.... but address the dysfunction in society....

Nah Escitalopram is much cheaper.


Giving up your energy, joy, and libido to better handle being forced to do shit you don't want to do has never felt like a very good deal.

I wagged 3 months of 6th form in the 90’s and still passed in the top 10% of the class, that’s how memorisation focused it was back then, and I hear little has changed.

It’s antiquated knowledge that in no way prepares kids for the real world imho. In many ways it’s a complete hindrance.

 

It now takes both parents working full time to be able to afford a relatively modest life.

Our children are being raised by strangers, and yet no one seems to think this is a problem.


We won't have anything to teach men who to process emotions but we'll spend a ton (and not enough ironically) about what to do when a man has bashed you, his partner.


They still need an education, and it's bizarre to suggest they don't

No one suggested that. That's your little addition, without which you would have no response to make.

Ah I see you're one of those people that can only handle one-point replies, otherwise you just pick the most convenient point and reply to that

... 

Not at all surprising. My daughter was a high achiever at primary and intermediate but hates high school. She says that the teachers don't respect the kids, so the kids don't respect the teachers, and that what they are learning doesn't feel relevant.


What Would You Need A Brain For


Just a reminder about the Tui Oil Field.

So tell me Mr. Jones how does operations like this benefit our environment and communities? 


Jones says: "Some people argue against minerals extraction, but gladly rely on the conveniences of modern society that are enabled by those resources"

Is this really the level we're on? The idea we should simply do the bad thing on purpose KNOWING that it's bad... just to spite people the govt percieves as hypocritical??


ACT's Resources spokesperson Simon Court says: "A high-wage economy depends on affordable and reliable energy. In other words, we need to drill, baby, drill."

I've noticed that the way this government talks about mining is almost sexual, They can't hide their lust and glee for those RICH ENDOWMENTS, that land OVERFLOWING with minerals, just WAITING to be DRILLED, baby. Like 🤮🤮🤮


From Russia Waft Twats

Also somewhere in a world news thread:

there's a reason there were so many economic ties with russia when the war started. basically for the last 30 years, the west has basically been trying to hug russia into international cooperation, and while a fair number of us thought we were succeeding for a while, they were mostly just biding their time while they got their tentacles into our ....let's say more easily influenced parties.





Monday, 29 April 2024

bitzliten: sound looper with Svelte 5



Following on from the basics,

we now want the nice features.

Recap

<Zone />

Loads a file to use, 

// these reset per file:

// the selection
let sel = $state({})
// push|pull their modes/seek|length representation
let sel_dominant = true

Uses sel to make up jobs:

// generate a bunch of tiles for your ears to walk on
function update_playlets() {
let {n_chunks} = get_timespace(sel)
// a set of dublets stretching across it
let nublets = create_unfulfilled_dublets(sel,n_chunks,chunk_length)

And passes a bunch of stuff to:

<Schaud {playlets} {needle_uplink} {sel}/>

...which is mostly about:

type acuelet = adublet & {startTime:number,source,needle,el}
let cuenow:null|adublet = null
// a new cuenow needs to .source and .play()
function scheduleNextSound(c?) {
...
// this shall remain the same for source.onended()'s callback
let cuelet = cuenow
// one of these while playing
if (!cuenow.source) {
    ...
let source = cuenow.source = source_cuelet(cuenow)
    ...
// play
cuenow.startTime = audioContext.currentTime
source.start(audioContext.currentTime);
    ...
source.onended = () => {
// on the cuelet that was cuenow when it started playing
delete cuelet.source
delete cuelet.needle
// cuenext will already be cuenow and playing (has .source) if crossfading
if (cuelet == cuenow) {
// nobody else (crossfading, ~cuelets) has altered cuenow since we started
cuenow = cuenext
scheduleNextSound();
}
};
}

Notice we keep in let cuelet what cuenow was, so we can compare it during callbacks.

So there's little structure to time.

Onward

I find that sel does not like being set from other components, despite being a $state.


The Zone side hosts an input() function:

// reset per file
let last_file
$effect(() => {
if (file && file != last_file) {
sel = {in:40,out:46}
sel_dominant = true
dublets = []
playlets = []
last_file = file
sel.input = (o) => sel_input(o)
}
})
let le
// ~sel -> modes
$effect(() => {
let see = sel.in
le = see
console.log("~sel ",sel)
})
// these in|out or start|end come from yonder
function sel_input(o) {
if (o.in != null) sel.in = o.in
sel_to_modes()
// will letsgo() if modes changes
}

Weirdly, that console.log("~sel ",sel) is never reached unless it sets le = see.

The input to sel comes from Schaud:

let selin = $state()
let selout = $state()
let selmo = $state()
let sel_adjustable = $state(false)
$effect(() => {
if (selin == null) return
let o = {in: selin}
console.log("Shaud -> sel",o)
sel.input(o)
})
$effect(() => {
if (sel?.out) {
sel_adjustable = true
selin = sel.in
selout = sel.out
let o = {in: selin}
console.log("Shaud <- sel",o)
}
})

If these two $effect()s are written the other way around, with the pull|import direction first, your change to eg selin via:

                    <Knob min={sel.in-10} max={sel.in+10}
bind:value={selin} ></Knob>

will be overwritten by selin = sel.in before {in: selin} is taken, etc.

The order being important was mentioned in the docs.

I wander along for a while and lose the ability to not be overwritten by selin = sel.in anyway.

When I crank in up, it logs this:


Unless I:

$inspect(sel)
$inspect(selin)
// pull from sel
let precise = $state('')
$effect(() => {
if (sel?.out) {
sel_adjustable = true
setTimeout(() => {
selin = sel.in
selout = sel.out
},10)
// precise = sel.start +' -- '+ sel.end
let o = {in:selin,out:selout}
console.log("Shaud <- sel",o)
}
})

Which then logs:


I decide to lean into {#each selections as se (se.id)}

As I did for needles, see 'must be declared in the top level of the component'

Svelte 5: snippets allow you to inject content, eg:

<Schaud {playlets} {needle_uplink} {sel} {on_reselection}>
{#snippet leftend(width_per_s)}
<span>
in
<Knob min={sel.in-10} max={sel.in+10}
bind:value={selin}
commit={push_to_sel} ></Knob>

So Schaud can decide the scale of time for visual feedback... At some point.

I am reading around for clues to master this Knob's reactivity.

Separating selection to Selection feels good. I try this:

// accept adjustments, may quickly adjust play
// set into an object owned by Zone...
sel.set({
in_time: writable(50),
out_time: writable(56),
})

This goes wrong.

Somewhere

Zone supplies the many sel with playlets: 2 second chunks of encoded audio, covering the selection.

let {sel,needle_uplink,on_reselection,chunk_length} = $props()

let playlets = $state([])
$effect(() => {
playlets = sel.playlets || []
})

It only matters to Zone if the selection changes enough to alter playlets, so we figure that out:

// accept adjustments, may quickly adjust play
// set into an object owned by Zone...
let in_time = $state(50)
let out_time = $state(56)
sel.set({
in_time,
out_time,
})
// shunt upwards when time needs expanding
$effect(() => {
// inclusively select dublet spaces
let fel = {
in: Math.floor(in_time / chunk_length) * chunk_length,
out: Math.ceil(out_time / chunk_length) * chunk_length,
}
if (fel.in != sel.in || fel.out != sel.out) {
let first_ever = sel.in == null
// non-reactively set it here
sel.set(fel)
console.log("Selection Woke",sel)
// then cause a reaction
!first_ever && on_reselection()
}
else {
console.log("Selection zzzz",sel)
}
})

As soon as in_time is <50 we must spawn a preceding cuelet|playlet|dublet|joblet (same thing in a different part of the production line), then until in_time < 48 we are done!




That should be in order. The production line involves syncing (primary keyed|indexed|resolved|deduplicated by each cuelet.in) into Schaud:


function sync_cuelets(playlets) {
let tr = transact_goners(cuelets)
let unhad:acuelet[] = cuelets.slice()
playlets.map((playlet:adublet) => {
let cuelet:any = cuelets.find((cuelet) => cuelet.in == playlet.in)
if (cuelet) {
tr.keep(cuelet)
}
else {
// create it
cuelet = {in:playlet.in,out:playlet.out}
cuelets.push(cuelet)
}
sync_cuelet(cuelet,playlet)
})
tr.done()
// and put them in the right order
cuelets = cuelets.sort((a,b) => a.in - b.in)
}
// maintain|whittle an array
// removing items without keep(z) before done().
// you may push more in meanwhile
function transact_goners(N) {
let unhad = N.slice()
return {
keep: (z) => {
unhad = unhad.filter(s => s != z)
},
done: (z) => {
let deletes = unhad.map((gone) => {
let i = N.indexOf(gone)
if (i < 0) throw "gone goner"
return i
})
deletes.reverse().map(i => N.splice(i,1))
},
}
}

Enormous subjects are best approached in thin, deep slices.

Nice

I found I could not transition those cuelets without them becoming zombies. This might be a bug:


Perhaps we should add billing

Svelte 5's Proxied state PR is interesting, the dwellings of the svelte community|vision are here.
I found my way onto their discord, surfing the threads it seemed a bit washed out, slightly confused cowboys scratching in the sand, near an invisible building.

The Reflect object in JavaScript provides a set of methods that allow you to perform interceptable operations on objects in a more functional way, often used in conjunction with Proxy objects for meta-programming and interception of object operations.

AlsoWith a web-of-trust, friends or family could vouch for your name, age or location; landlords could vouch for your address; employers could vouch for your skills; customers could vouch for businesses; and so on. As it doesn’t rely on government databases, but rather the people you know, it is truly decentralized and accessible.

A little more shoving things into libraries would be good now. I spring a reactivity problem immediately, in decodeAudio(cuelet):
// if (untrack(() => cuelet.buffer)) return
if (cuelet.buffer) return
This makes SYNC CLUELETS happen 4 times, when it does.
Whereas:
// if (untrack(() => cuelet.buffer)) return
// if (cuelet.buffer) return
cuelet.buffer && console.log("We already had a buffer")
Causes an infinite loop of noticing one thing already had a buffer about fifty times, then two things (we are doing the joblets), then I stop it after a hundred of them...
if (untrack(() => cuelet.buffer)) return
if (cuelet.buffer) return
// cuelet.buffer && console.log("We already had a buffer")
This makes SYNC CLUELETS happen 3 times, when it does.
And simply:
if (untrack(() => cuelet.buffer)) return
Makes it happen... twice.
Which is what we want, I believe.


Anyway that's in orch.sync_cuelets(playlets) now.
That's great to get out of Zone
    it feels maintainable again now.
I then move on to
    17 instances of audioContext

Here's a function in class ModusCueletSeq extends Modus
Any subclass of Modus provides this for momentary awareness, this one does the cuelets playing in sequence:
attend({def,c}) {
if (!this.orch.cuelets[0]) debugger

// c.go may be passed in|along
if (!this.cuenow) c.go = 1
if (c.go) {
this.cuenow = this.orch.next_cuelet(this.cuenow)
this.go_cuenow({def,c})
console.log("Playing "+this.cuenow.in)
}
}

Approximately this functionality was less abstract before in scheduleNextSound():

...
        // one of these while playing
if (!cuenow.source) {
remarks.push("new")
let source = cuenow.source = source_cuelet(cuenow)

// this will find the same one unless crossfading
let needle = cuenow.needle = find_unused_needle()
// crossfade from last
if (c.fadein) {
source.fadein(c.fadein)
}

// play
cuenow.startTime = audioContext.currentTime
            source.start(audioContext.currentTime);
...

The middle of ModusCueletSeq is:

go_cuenow({def,c}) {
if (!this.cuenow?.buffer) return console.warn("!ModusCueletSeq.cuenow.buffer",this)
// this one time we play this cuelet
// < can coexist with another time we play this same cuelet?
this.zip = new Ziplet({orch:this.orch, mo:this, cuelet:this.cuenow})
// crossfade from last
if (c.fadein) {
fadein(this.zip,c.fadein)
}
// play
this.zip.start()

this.plan_crossfade({def,c})
}

The fade stuff seems lopsided.
Perhaps Ziplet should do fades, by seeing what else is going on in mo:

class Ziplet {
    ...
constructor({orch,mo,cuelet}) {
        ...
// we may fade over the rest of whatever is playing
if (mo.zip) this.fade_in_over_zip = mo.zip
    ...
start() {
let time = this.orch.time
this.cuelet.startTime = time
this.source.start(time)
let ozip = this.fade_in_over_zip
if (ozip) this.fade_in_over(ozip)
} ...

this.orch.time is provided by modern Javascript: objects can have getters

We also use them here:

class Ziplet {
    ...
get duration():number {
let dur = this.source?.buffer?.duration
if (dur == null) throw "!duration"
return dur
}
get ends_at() {
if (this.cuelet.startTime == null) throw "!startTime"
return this.cuelet.startTime + this.duration
}
get time_left() {
let left = this.ends_at - this.orch.time
if (left < 0) left = 0
return left
}     ...

Ziplet == Needle

Perhaps Ziplet should do needles too - the representation of the play heads.
In Ziplet.start() we:
    Get a needle for it via this.orch.needle_uplink.find_unused_needle()
    Dispatch animation via this.orch.needle_uplink.up_displaytime()

They are both hanging out in Schaud.
    displaytime is reactive
    needles resides there
It keeps calling itself back to do other drudgery:

needle_uplink.up_displaytime = (the) => {
if (the && the != theone) return
the = theone = {}
setTimeout(() => up_displaytime(the), 166)

if (!needle_uplink.stat) return
let {cuenow,cue_time,loop_time} = needle_uplink.stat()
if (!(cuenow && cuenow.el)) return
displaytime = dec(loop_time)
check_time_is_passing(cue_time)
if (audioContext.state == 'running') needle_moves()
    }

We define needle_uplink.stat in ModusCueletSeq, it provides progress info for Schaud to be somewhere in time.

Here's where the needle moves:
function needle_moves() {
let {cuenow,cue_time,loop_time,zip} = needle_uplink.stat()
        ...
// 1-2 needles exist
let needle = zip.needle
        ...
needle.left.set(cuenow.el.offsetLeft*1 + some_left)
needle.left.set(cuenow.el.offsetLeft*1 + cueletswidth*1,{duration:duration*1000})
needle.top.set(cuenow.el.offsetTop)
        ...
}

And it feels its way along the blocks of time:


Looks to have broken the needle sharing - one is supposed to be used for each loop through.
We need to remove them from this list:

needle_uplink.find_unused_needle = () => {
        ...
        let used = cuelets.map(cuelet => cuelet.needle?.id)

when they finish:

export class ModusCueletSeq extends Modus {
    ...
plan_crossfade({def,c}) {
let zip = this.zip
let cuenow = this.cuenow
        ...
if (!continuity) {
// crossfading
            ...
        }
zip.source.onended = () => {
delete cuenow.needle
            ...


Thus commit: NeedleableZiplet

We're now back where we were at the start, but with better modelling.

We almost made it to new features
    before this blog post got laggy to type up
     in this WYSIWYG editor.

We have paved the way to controlling gain per Modus, abstracting the "playing cuelets" logic.

Perhaps we increased the purity of the code by one karat? Half a karat?

By the way, the word "carat" (or karat) comes from the carob seed, which was once used as a standard unit of measure due to its relatively consistent weight.

And along the way I made this great commit: