6 min read

廣告(第一回合)

廣告(第一回合)


回到條碼的命題,將其視為一種「遊戲規則」,而創作是試著給出可行的答案,這是遊戲的第一回合。

將詩集文字轉換為 unicode ,並以 code 39 條碼的方式呈現,聲音則是將二進制檔案轉成音訊資料,並設定序列播放,產生一種純粹資訊化的噪聲。

使用 #supercollider 與 #cablesgl ,如果對宅宅的技術原理有興趣可見下方原始碼:

Code

英數字 -> code39 ,有參考的部份應該都有列在下方。

(因為很酷所以加入了「檢查碼」和「檢查碼的所在位置」的輸出)

// 2025@ewd
// Ref:
// https://gist.githubusercontent.com/incubated-geek-cc/985c079f4ad9be869e4febcaa63e9893/raw/f60096838d9bb74e1c9c5c676de2c0b059607baf/code39Mapping.js

const charMapper = {
    0: ['2B', '2W', '2B', '5W', '5B', '2W', '5B', '2W', '2B', '2W'],
    1: ['5B', '2W', '2B', '5W', '2B', '2W', '2B', '2W', '5B', '2W'],
    2: ['2B', '2W', '5B', '5W', '2B', '2W', '2B', '2W', '5B', '2W'],
    3: ['5B', '2W', '5B', '5W', '2B', '2W', '2B', '2W', '2B', '2W'],
    4: ['2B', '2W', '2B', '5W', '5B', '2W', '2B', '2W', '5B', '2W'],
    5: ['5B', '2W', '2B', '5W', '5B', '2W', '2B', '2W', '2B', '2W'],
    6: ['2B', '2W', '5B', '5W', '5B', '2W', '2B', '2W', '2B', '2W'],
    7: ['2B', '2W', '2B', '5W', '2B', '2W', '5B', '2W', '5B', '2W'],
    8: ['5B', '2W', '2B', '5W', '2B', '2W', '5B', '2W', '2B', '2W'],
    9: ['2B', '2W', '5B', '5W', '2B', '2W', '5B', '2W', '2B', '2W'],
    '*': ['2B', '5W', '2B', '2W', '5B', '2W', '5B', '2W', '2B', '2W'],
    A: ['5B', '2W', '2B', '2W', '2B', '5W', '2B', '2W', '5B', '2W'],
    B: ['2B', '2W', '5B', '2W', '2B', '5W', '2B', '2W', '5B', '2W'],
    C: ['5B', '2W', '5B', '2W', '2B', '5W', '2B', '2W', '2B', '2W'],
    D: ['2B', '2W', '2B', '2W', '5B', '5W', '2B', '2W', '5B', '2W'],
    E: ['5B', '2W', '2B', '2W', '5B', '5W', '2B', '2W', '2B', '2W'],
    F: ['2B', '2W', '5B', '2W', '5B', '5W', '2B', '2W', '2B', '2W'],
    G: ['2B', '2W', '2B', '2W', '2B', '5W', '5B', '2W', '5B', '2W'],
    H: ['5B', '2W', '2B', '2W', '2B', '5W', '5B', '2W', '2B', '2W'],
    I: ['2B', '2W', '5B', '2W', '2B', '5W', '5B', '2W', '2B', '2W'],
    J: ['2B', '2W', '2B', '2W', '5B', '5W', '5B', '2W', '2B', '2W'],
    K: ['5B', '2W', '2B', '2W', '2B', '2W', '2B', '5W', '5B', '2W'],
    L: ['2B', '2W', '5B', '2W', '2B', '2W', '2B', '5W', '5B', '2W'],
    M: ['5B', '2W', '5B', '2W', '2B', '2W', '2B', '5W', '2B', '2W'],
    N: ['2B', '2W', '2B', '2W', '5B', '2W', '2B', '5W', '5B', '2W'],
    O: ['5B', '2W', '2B', '2W', '5B', '2W', '2B', '5W', '2B', '2W'],
    P: ['2B', '2W', '5B', '2W', '5B', '2W', '2B', '5W', '2B', '2W'],
    Q: ['2B', '2W', '2B', '2W', '2B', '2W', '5B', '5W', '5B', '2W'],
    R: ['5B', '2W', '2B', '2W', '2B', '2W', '5B', '5W', '2B', '2W'],
    S: ['2B', '2W', '5B', '2W', '2B', '2W', '5B', '5W', '2B', '2W'],
    T: ['2B', '2W', '2B', '2W', '5B', '2W', '5B', '5W', '2B', '2W'],
    U: ['5B', '5W', '2B', '2W', '2B', '2W', '2B', '2W', '5B', '2W'],
    V: ['2B', '5W', '5B', '2W', '2B', '2W', '2B', '2W', '5B', '2W'],
    W: ['5B', '5W', '5B', '2W', '2B', '2W', '2B', '2W', '2B', '2W'],
    X: ['2B', '5W', '2B', '2W', '5B', '2W', '2B', '2W', '5B', '2W'],
    Y: ['5B', '5W', '2B', '2W', '5B', '2W', '2B', '2W', '2B', '2W'],
    Z: ['2B', '5W', '5B', '2W', '5B', '2W', '2B', '2W', '2B', '2W'],
    '-': ['2B', '5W', '2B', '2W', '2B', '2W', '5B', '2W', '5B', '2W'],
    ',': ['5B', '5W', '2B', '2W', '2B', '2W', '5B', '2W', '2B', '2W'],
    $: ['2B', '5W', '2B', '5W', '2B', '5W', '2B', '2W', '2B', '2W'],
    '/': ['2B', '5W', '2B', '5W', '2B', '2W', '2B', '5W', '2B', '2W'],
    '+': ['2B', '5W', '2B', '2W', '2B', '5W', '2B', '5W', '2B', '2W'],
    '%': ['2B', '2W', '2B', '5W', '2B', '5W', '2B', '5W', '2B', '2W'],
    _: ['2B', '5W', '5B', '2W', '2B', '2W', '5B', '2W', '2B', '2W'],
};

const strIn = op.inString('String', 'test');
const arrOut = op.outArray('Array');
const uniOut = op.outString('Unicode');
const checkDigitOut = op.outString('Check Digit');
const checkDigitIdxOut = op.outNumber('checkDigitIdxOut');

const toReplaceUni = {
    '2B': '▌',
    '5B': '█▌',
    '2W': '',
    '5W': ' ',
};

const toReplaceArr = {
    '2B': 0,
    '5B': 1,
    '2W': 0,
    '5W': 1,
};

let charMapperObj = {};
let binMapperObj = {};

for (let char in charMapper) {
    let arr = charMapper[char];
    let barcode = '';
    let binStr = [];

    for (let bw of arr) {
        barcode += toReplaceUni[bw];
        binStr += toReplaceArr[bw];
    }

    charMapperObj[char] = barcode.trim();
    binMapperObj[char] = binStr;
}

let check = [];

for (let char in charMapper) {
    check.push(char);
}

let uniBarcode = '';
let binBarcode = [];
let checkDigitIdx = 0;
let strInArr = [];

strIn.onChange = update;

function update() {
    strInArr = strIn.get() ? strIn.get().trim().toUpperCase().split('') : [];

    for (let strInChar of strInArr) {
        if (
            !'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*-,$/+_%'.includes(strInChar)
        ) {
            strIn.set('');
            break;
        }
        uniBarcode += charMapperObj[strInChar];
        let bin = binMapperObj[strInChar];
        let binArr = bin.split('').map((item) => parseInt(item, 10));

        binBarcode.push(binArr);
    }

    // check digit
    let indexSum = 0;
    let checkDigit = 0;

    strInArr.map((item) => {
        indexSum += check.indexOf(item);
    });

    checkDigit = indexSum ? check[indexSum % check.length] : '';
    checkDigitIdx = checkDigit ? check.indexOf(checkDigit) : null;

    uniOut.set(uniBarcode);
    arrOut.set(binBarcode);
    checkDigitOut.set(checkDigit);
    checkDigitIdxOut.set(checkDigitIdx);
}

supercollider databending,原碼有點混亂有點懶得整理,不過大致原理是:

readAllSignal 讀取 -> 丟進 buffer -> 丟進 Dictionary

~path = PathName(thisProcess.nowExecutingPath).parentPath++"assets/";

~makeBuffers = {
	d = Dictionary.new;
	PathName(~path).entries.do{
		arg subfolder, index;
		d.add(
			subfolder.folderName.asSymbol ->
			subfolder.entries.collect {
				arg item, index;
				Buffer.loadCollection(s, File.readAllSignal(item.fullPath).clip2(1));
			}
		);
	};
};

播放的時候用 SynthDef,在 bufnum 的位置放入 buffer 在 Dictionary 裡的 index 就可以了……

SynthDef.new(\data, {
	arg out, bufnum, amp = 0.3, rate = 1, spos = 0, spd = 2.0, pan = 0;
	var sig, env, gate;

	gate = Pulse.kr(spd);
	sig = PlayBuf.ar(1, bufnum, rate, startPos: spos * BufFrames.kr(bufnum), doneAction: 2);
	env = EnvGen.kr(Env([0, 1, 0], [0.01, 0.5], \hold), gate, doneAction: 2);
	sig = sig * env * amp;
	sig = Pan2.ar(sig, pan);
	Out.ar(out, sig);
}).add;

// ......

~blend0 = Pbind(
		\instrument, \data,
		\bufnum, d[\blend][0],
		\dur, 2,
		\spd, Pseq([0.5, 0.25, 2.0, 1.75, Rest(0.5), 20.0], inf),
		\spos, 0.4,
		\amp, 0.3,
		\rate, Pfunc { [1, 2, 0.8, 4, 0.5].choose },
		\pan, Pseq([0.25, 0.5, -0.5, -0.3, 0.25], inf),
		\group, ~mainGrp,
		\out, ~out
).play;

看不懂?沒關係,我也不是很懂,但至少跟在 audacity 測試的結果差不多。

這東西真夠複雜,難怪需要在 IDE 內建文檔。

Ref