index

廣告(第一回合)

· 6min
ewd · 廣告(第一回合)

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

將詩集文字轉換為 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