update
This commit is contained in:
parent
21265ad7fe
commit
140e6f5ad1
11 changed files with 1792 additions and 154 deletions
BIN
public/arrow.png
Normal file
BIN
public/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
11
public/arrow.svg
Normal file
11
public/arrow.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.5.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="레이어_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||||
|
y="0px" viewBox="0 0 38.7 21.7" style="enable-background:new 0 0 38.7 21.7;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="19.3,20.5 0.7,1.9 2.1,0.5 19.3,17.7 36.4,0.5 37.8,1.9 "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 516 B |
|
|
@ -15,6 +15,18 @@ body {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: 'Noto Sans KR', sans-serif;
|
font-family: 'Noto Sans KR', sans-serif;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg {
|
.bg {
|
||||||
|
|
@ -30,6 +42,14 @@ body {
|
||||||
z-index: -1; // this is optional
|
z-index: -1; // this is optional
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 30px;
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
#krtitle {
|
#krtitle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20%;
|
top: 20%;
|
||||||
|
|
@ -68,6 +88,12 @@ body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#parade {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -82,20 +108,28 @@ body {
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sticky {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
|
text-align: end;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
.first {
|
.first {
|
||||||
position: absolute;
|
display: inline-block;
|
||||||
top: 20px;
|
width: 10px;
|
||||||
right: 18px;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.second {
|
.second {
|
||||||
position: absolute;
|
display: inline-block;
|
||||||
top: 20px;
|
width: 10px;
|
||||||
right: 40px;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.third {
|
.third {
|
||||||
|
|
@ -127,6 +161,7 @@ audio {
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.list-sub {
|
.list-sub {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 36px;
|
top: 36px;
|
||||||
|
|
@ -387,12 +422,17 @@ button {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -65%);
|
transform: translate(-50%, -65%);
|
||||||
/* height: 600px; */
|
height: 350px;
|
||||||
writing-mode: vertical-lr;
|
writing-mode: vertical-lr;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
@media screen and (max-width: 700px) {
|
||||||
|
|
||||||
|
.sticky {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
#krtitle {
|
#krtitle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20%;
|
top: 20%;
|
||||||
|
|
|
||||||
BIN
public/thumbnail.jpg
Normal file
BIN
public/thumbnail.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 577 KiB |
82
server.js
82
server.js
|
|
@ -54,9 +54,16 @@ var io = require("socket.io")(fastify.server, {
|
||||||
|
|
||||||
//get '/'
|
//get '/'
|
||||||
fastify.get("/", async function (request, reply) {
|
fastify.get("/", async function (request, reply) {
|
||||||
//console.log(request.url);
|
reply.view("/src/pages/parade.html", {});
|
||||||
let url = request.url.replace(/\/$/, '');
|
});
|
||||||
|
|
||||||
|
//get '/en/'
|
||||||
|
fastify.get("/en/", async function (request, reply) {
|
||||||
|
reply.view("/src/pages/parade.en.html", {});
|
||||||
|
});
|
||||||
|
|
||||||
|
//get '/list'
|
||||||
|
fastify.get("/list", async function (request, reply) {
|
||||||
//get list
|
//get list
|
||||||
let list = await fs.readdir('/media/storage/public/sound-parade/');
|
let list = await fs.readdir('/media/storage/public/sound-parade/');
|
||||||
list.reverse();
|
list.reverse();
|
||||||
|
|
@ -83,9 +90,16 @@ fastify.get("/", async function (request, reply) {
|
||||||
// console.log(folders);
|
// console.log(folders);
|
||||||
|
|
||||||
//
|
//
|
||||||
reply.view("/src/pages/parade.html", {
|
reply.send(folders);
|
||||||
list: folders,
|
});
|
||||||
});
|
|
||||||
|
//get '/uploads/:foldername/:filename'
|
||||||
|
fastify.get("/uploads/:foldername/:filename", async function (request, reply) {
|
||||||
|
|
||||||
|
//get file & send
|
||||||
|
var file = await fs.readFile('/media/storage/public/sound-parade/' + request.params.foldername + '/' + request.params.filename);
|
||||||
|
|
||||||
|
reply.send(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
//get '/live
|
//get '/live
|
||||||
|
|
@ -98,58 +112,6 @@ fastify.get("/preview/:foldername", function (request, reply) {
|
||||||
reply.view("/src/pages/preview.html", {});
|
reply.view("/src/pages/preview.html", {});
|
||||||
});
|
});
|
||||||
|
|
||||||
//get '/entry', '/entry/', '/en/entry', '/en/entry/', '/entry/test'
|
|
||||||
["/entry", "/entry/", "/en/entry", "/en/entry/", "/entry/test"].forEach(function(item) {
|
|
||||||
fastify.get(item, async function (request, reply) {
|
|
||||||
|
|
||||||
//console.log(request.url);
|
|
||||||
let url = request.url.replace(/\/$/, '');
|
|
||||||
|
|
||||||
//get list
|
|
||||||
let list = await fs.readdir('/media/storage/public/sound-parade/');
|
|
||||||
list.reverse();
|
|
||||||
|
|
||||||
// console.log(list);
|
|
||||||
|
|
||||||
let folders = [];
|
|
||||||
for (const item of list) {
|
|
||||||
let json = await fs.readFile('/media/storage/public/sound-parade/' + item + '/fields.json')
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
if (json != undefined) {
|
|
||||||
var fields = JSON.parse(json.toString('utf8'));
|
|
||||||
folders.push({
|
|
||||||
foldername: item,
|
|
||||||
group: fields.group,
|
|
||||||
title: fields.title,
|
|
||||||
comment: fields.comment,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(folders);
|
|
||||||
|
|
||||||
//
|
|
||||||
if (url == "/entry") {
|
|
||||||
reply.view("/src/pages/entry.html", {
|
|
||||||
list: folders,
|
|
||||||
});
|
|
||||||
} else if (url == "/en/entry") {
|
|
||||||
reply.view("/src/pages/entry.en.html", {
|
|
||||||
list: folders,
|
|
||||||
});
|
|
||||||
} else if (url == "/entry/test") {
|
|
||||||
reply.view("/src/pages/entry.test.html", {
|
|
||||||
list: folders,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// --> https://stackoverflow.com/a/40899275
|
|
||||||
// all the regex didn't work for me -- a 'last resort' method
|
|
||||||
|
|
||||||
//get '/entries'
|
//get '/entries'
|
||||||
fastify.get("/entries", async function (request, reply) {
|
fastify.get("/entries", async function (request, reply) {
|
||||||
|
|
||||||
|
|
@ -214,8 +176,10 @@ fastify.get("/delete/:foldername/:pass", async function (request, reply) {
|
||||||
reply.send({ result: res });
|
reply.send({ result: res });
|
||||||
});
|
});
|
||||||
|
|
||||||
//post on '/entry'
|
//post on '/'
|
||||||
fastify.post("/entry", async function (request, reply) {
|
fastify.post("/", async function (request, reply) {
|
||||||
|
|
||||||
|
console.log("-- h.."); // got the event.
|
||||||
|
|
||||||
// stores files to tmp dir and return paths
|
// stores files to tmp dir and return paths
|
||||||
const files = await request.saveRequestFiles().catch(err => {
|
const files = await request.saveRequestFiles().catch(err => {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
<title>Walking Towards the Flow | entry</title>
|
<title>Walking towards the Flow | entry</title>
|
||||||
<link rel="stylesheet" href="/default.css" />
|
<link rel="stylesheet" href="/default.css" />
|
||||||
<style>
|
<style>
|
||||||
#defaultCanvas0 {
|
#defaultCanvas0 {
|
||||||
|
|
@ -45,32 +45,35 @@
|
||||||
<div class="second"><a href="/">parade</a></div>
|
<div class="second"><a href="/">parade</a></div>
|
||||||
<div class="notice">
|
<div class="notice">
|
||||||
<section>
|
<section>
|
||||||
<h1>Walking Towards the Flow</h1>
|
<h1>Introduction</h1>
|
||||||
<p>
|
<p>
|
||||||
Have you ever seen a parade outside the procession? Drums and shouts are heard in the distance, many beings shouting, flowing by, groups appearing and moving away...
|
«Walking towards the Flow» is a sound parade that creates a procession of sounds in the online space. Anyone can participate. This parade is made by collecting the sounds you sent.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
«Walking Toward the Flow» is a sound parade that creates a procession of sounds in the online space. Anyone can participate. This parade is made by collecting the sounds you sent.
|
The path of sound flows like the Milky Way through which the stars circulate, always there, whether you are listening to it or not.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
The permanence of this eternal parade gives us final consolation. Sometimes we think that we are space debris in infinite orbit, but The moment it is observed, it is no longer loneliness or forgotten, but remembered.
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
«Walking Toward the Flow» consists of a total of five 'flock' of sounds: the sounds of promises, the sounds of speaking, the sounds of the body, the sounds of objects around, and the sounds of someone. They will flow in groups for about 3 minutes each and a total of 15 minutes or more.
|
«Walking towards the Flow» will perform offline on November 27 (Sun). In the performance, the sounds gathered on the website can be heard in the physical space, and audiences can walk and stop together to hear the voices of those who empathize with the flow.
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
On the night of January 27 (Thursday), the «Walking to the Flow» parade will be broadcasted along with a live performance at the 'the Night of Ideas' hosted by Cultural Service of the Embassy of France in Korea, Liszt Institut Hungarian Cultural Center Seoul and Embassy of Belgium in Seoul and sponsored by UNESCO Korea and Institut Français. Thank you for your participation and interest.
|
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
We look forward to your interest and participation in the online recruitment and offline performances.
|
||||||
|
</p><br/>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<p>
|
<p>
|
||||||
Recruiting sounds<br/>
|
Recruiting sounds<br/>
|
||||||
January 17, 2022 (Mon) – January 26, 2022 (Wed)
|
November 13, 2022 (Sun) – November 26, 2022 (Sat)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Live performance<br/>
|
Live performance<br/>
|
||||||
January 27th, 2022 (Thursday)<br>
|
November 27th, 2022 (Sunday) 16:000, 17:00<br>
|
||||||
20:45-21:00 (KST), 12:45-13:00 (CET)<br/>
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Place<br/>
|
Performance Place<br/>
|
||||||
<a href="https://walkingtowardstheflow.xyz">walkingtowardstheflow.xyz</a>, <a href="https://www.youtube.com/c/franceencoree">France en Corée - YouTube channel</a>
|
<a
|
||||||
|
Seoul Museum of Art Seosomun Main Branch 1st floor Lobby, 2nd floor Learning Station, Rooftop Garden</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
artists<br/>
|
artists<br/>
|
||||||
|
|
@ -95,6 +98,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li>2. Upload the recorded sound file.</li>
|
<li>2. Upload the recorded sound file.</li>
|
||||||
<li>3. After filling out the title and description of the sound, draw the shape of the sound and submit it.</li>
|
<li>3. After filling out the title and description of the sound, draw the shape of the sound and submit it.</li>
|
||||||
|
<li>*If you upload a sound source that contains hate speech, it will be deleted.</li>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|
|
||||||
68
src/pages/list.html
Normal file
68
src/pages/list.html
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
|
<title>흐름을 향하여 걷는 | 소개</title>
|
||||||
|
<link rel="stylesheet" href="/default.css" />
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/p5.js"></script>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&display=swap" rel="stylesheet" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="bg"></div>
|
||||||
|
<section id="list_init_kr" style="display:none">
|
||||||
|
<h1>소리 목록</h1>
|
||||||
|
<p>제출해주신 소리는 아래 목록에 시간순으로 쌓입니다. 각각의 모양을 클릭하면, 모양에 해당하는 소리를 듣고 소리에 대한 정보를 확인할 수 있습니다.</p>
|
||||||
|
</section>
|
||||||
|
<section id="list_init_en" style="display:none">
|
||||||
|
<h1>sound list</h1>
|
||||||
|
<p>The sounds you submit are listed below in chronological order. If you click each shape, you can hear the sound corresponding to the shape and check information about the sound.</p>
|
||||||
|
</section>
|
||||||
|
<section id="list">
|
||||||
|
</section>
|
||||||
|
<script>
|
||||||
|
function setup() {
|
||||||
|
noCanvas();
|
||||||
|
noLoop();
|
||||||
|
list_reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function list_reload() {
|
||||||
|
loadJSON('/list', list => {
|
||||||
|
//if lang == kr
|
||||||
|
let list_init = select('#list_init_kr').html();
|
||||||
|
//if lang == en
|
||||||
|
// let list_init = select('#list_init_en').html();
|
||||||
|
|
||||||
|
//
|
||||||
|
var t = "";
|
||||||
|
list.forEach(item => {
|
||||||
|
t = t + `
|
||||||
|
<div class="items" foldername="${item.foldername}">
|
||||||
|
<details>
|
||||||
|
<summary><img class="drawing" src="/uploads/${item.foldername}/pixels.png" /></summary>
|
||||||
|
<audio class="sound" preload="none" controls>
|
||||||
|
<source src="/uploads/${item.foldername}/audio.mp3" type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
<ul class="soundinfo"><hr>
|
||||||
|
<li>유형 | <div class="group">${item.group}</div></li><hr>
|
||||||
|
<li>제목 | <div class="title">${item.title}</div></li><hr>
|
||||||
|
<li>묘사 | <div class="comment">${item.comment}</div></li><hr>
|
||||||
|
</ul>
|
||||||
|
<button class="preview" type="button" onclick="javascript:window.open('/preview/${item.foldername}', '_blank');">미리보기</button>
|
||||||
|
<button class="delete" type="button" onclick="del(this)">삭제</button>
|
||||||
|
</details>
|
||||||
|
</div>'
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
select('#list').html(list_init + t);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
773
src/pages/parade.en.html
Normal file
773
src/pages/parade.en.html
Normal file
|
|
@ -0,0 +1,773 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>흐름을 향하여 걷는 Walking towards the Flow</title>
|
||||||
|
<meta itemprop="description" content="A sound parade that creates a procession of sounds in the online space.">
|
||||||
|
<meta itemprop="image" content="/thumbnail.jpg">
|
||||||
|
<link rel="stylesheet" href="/default.css" />
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#defaultCanvas0 {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p5 {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
padding: 1em 1em;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.penselect {
|
||||||
|
margin: 1em 0em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="/js/p5-v1.1.9.min.js"></script>
|
||||||
|
<script src="/js/socket-v2.3.0.io.slim.js"></script>
|
||||||
|
<script src="/js/Tone-14.8.36.min.js"></script>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&display=swap" rel="stylesheet" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="bg"></div>
|
||||||
|
<div id="top"></div>
|
||||||
|
<div class="parade"></div>
|
||||||
|
<div class="entry">
|
||||||
|
<div class="notice">
|
||||||
|
<div class="sticky">
|
||||||
|
<div class="first"><a href="/">한글</a></div>
|
||||||
|
<div class="second"><a href="#top">Parade</a></div>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<h1>Introduction</h1>
|
||||||
|
<p>
|
||||||
|
«Walking towards the Flow» is a sound parade that creates a procession of sounds in the online space. Anyone can participate. This parade is made by collecting the sounds you sent.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The path of sound flows like the Milky Way through which the stars circulate, always there, whether you are listening to it or not.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The permanence of this eternal parade gives us final consolation. Sometimes we think that we are space debris in infinite orbit, but The moment it is observed, it is no longer loneliness or forgotten, but remembered.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
«Walking towards the Flow» will perform offline on November 27 (Sun). In the performance, the sounds gathered on the website can be heard in the physical space, and audiences can walk and stop together to hear the voices of those who empathize with the flow.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We look forward to your interest and participation in the online recruitment and offline performances.<br />
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<div class="info">
|
||||||
|
<p>
|
||||||
|
Recruiting sounds<br />
|
||||||
|
November 13, 2022 (Sun) – November 26, 2022 (Sat)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Live performance<br />
|
||||||
|
November 27th, 2022 (Sunday) 16:00
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Performance Place<br />
|
||||||
|
Seoul Museum of Art Seosomun Main Branch 1st floor Lobby, 2nd floor Learning Station, Rooftop Garden
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Parade Composition<br />
|
||||||
|
Flock0 promises — Flock1 flags — Flock2 bodies — Flock3 objects — Flock4 someone
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Performance Reservation<br />
|
||||||
|
<a href="https://zrr.kr/aHTS" target="_blank">https://zrr.kr/aHTS</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Artists<br />
|
||||||
|
<a href="http://dianaband.info" target="_blank">diana band</a> × <a href="https://cgyoon.kr/"
|
||||||
|
target="_blank">Choong-geun Yoon</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
(Inquiry: wonjung24@gmail.com)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="participation">
|
||||||
|
<h1>How to participate</h1>
|
||||||
|
<ol>
|
||||||
|
<li>1. Record the sound for about 30 seconds with a recording device or phone according to the characteristics of the flock.
|
||||||
|
<ol class="category">
|
||||||
|
<li>• Flock1 flags — Speak out five times about what you like or value.</li>
|
||||||
|
<li>• Flock2 bodies — Record the sound of your body. Applause and whistling.</li>
|
||||||
|
<li>• Flock3 objects — Find the sound of things around you. Bubble wrap sound, Beads sound.</li>
|
||||||
|
<li>• Flock4 someone — Collect the sound of others. animal, sound of water, place to take a walk.</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
<li>2. Upload the recorded sound file.</li>
|
||||||
|
<li>3. After filling out the title and description of the sound, draw the shape of the sound and submit it.</li>
|
||||||
|
<li>* If you upload a sound source that contains hate speech, it will be deleted.</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>Sound Submission</h1>
|
||||||
|
<form id="form" method="POST" enctype="multipart/form-data">
|
||||||
|
<ul class="submit">
|
||||||
|
<li>
|
||||||
|
<h3>1. Sound Type</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="flag" /> Flock1 flags
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="body" /> Flock2 bodies
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="object" /> Flock3 objects
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="any" required /> Flock4 someone
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>2. Sound File</h3>
|
||||||
|
<input type="file" name="audiofile" autocomplete="off" required />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>3. Title</h3>
|
||||||
|
<input id="title" name="title" type="text" autocomplete="off" required />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>4. Description of the sound</h3>
|
||||||
|
<input id="comment" name="comment" type="text" autocomplete="off" required />
|
||||||
|
</li>
|
||||||
|
<!-- pixels.png ~ made with p5.js -->
|
||||||
|
<li class="noscroll">
|
||||||
|
<h3>5. Draw an image of the sound</h3>
|
||||||
|
<div>
|
||||||
|
<div id="p5">
|
||||||
|
</div>
|
||||||
|
<div class="tools">
|
||||||
|
<div>
|
||||||
|
<input class="penselect" type="radio" value="pencil" name="penselect" id="penselect-pencil"
|
||||||
|
autocomplete="off" checked>
|
||||||
|
<label for="penselect-pencil">pencil</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input class="penselect" type="radio" value="erasor" name="penselect" id="penselect-erasor"
|
||||||
|
autocomplete="off">
|
||||||
|
<label for="penselect-erasor">erasor</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="clear">Erase all!</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>6. Passward</h3>
|
||||||
|
<input id="pass" name="pass" type="text" maxlength="2" pattern="^\d{2}$" title="Passward is 2-digit numbers."
|
||||||
|
required />
|
||||||
|
<small>Passward is 2-digit numbers. It's required if you delete it.</small>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input id="submit" type="submit" value="Upload!" />
|
||||||
|
<progress id="progress" max="100" value="70" style="display:none"></progress>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
↓
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<section id="list_init_kr" style="display:none">
|
||||||
|
<h1>소리 목록</h1>
|
||||||
|
<p>제출해주신 소리는 아래 목록에 시간순으로 쌓입니다. 각각의 모양을 클릭하면, 모양에 해당하는 소리를 듣고 소리에 대한 정보를 확인할 수 있습니다.</p>
|
||||||
|
</section>
|
||||||
|
<section id="list_init_en" style="display:none">
|
||||||
|
<h1>Sound List</h1>
|
||||||
|
<p>The sounds you submitted will be stacked in chronological order in the list below. By clicking on each shape, you can hear the sound corresponding to the shape and check the information about the sound.</p>
|
||||||
|
</section>
|
||||||
|
<section id="list">
|
||||||
|
</section>
|
||||||
|
</div><!-- div class="notice" -->
|
||||||
|
</div><!-- div class="entry" -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// // force https
|
||||||
|
// var http_confirm = location.href.split(":")[0];
|
||||||
|
// if (http_confirm == "http") {
|
||||||
|
// window.location.replace("https://" + location.host);
|
||||||
|
// }
|
||||||
|
|
||||||
|
function list_reload() {
|
||||||
|
loadJSON('/list', list => {
|
||||||
|
//if lang == kr
|
||||||
|
// let list_init = select('#list_init_kr').html();
|
||||||
|
//if lang == en
|
||||||
|
let list_init = select('#list_init_en').html();
|
||||||
|
|
||||||
|
//
|
||||||
|
var t = "";
|
||||||
|
list.forEach(item => {
|
||||||
|
t = t + `
|
||||||
|
<div class="items" foldername="${item.foldername}">
|
||||||
|
<details>
|
||||||
|
<summary><img class="drawing" src="/uploads/${item.foldername}/pixels.png" /></summary>
|
||||||
|
<audio class="sound" preload="none" controls>
|
||||||
|
<source src="/uploads/${item.foldername}/audio.mp3" type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
<ul class="soundinfo"><hr>
|
||||||
|
<li>Group | <div class="group">${item.group}</div></li><hr>
|
||||||
|
<li>Title | <div class="title">${item.title}</div></li><hr>
|
||||||
|
<li>Desc. | <div class="comment">${item.comment}</div></li><hr>
|
||||||
|
</ul>
|
||||||
|
<button class="preview" type="button" onclick="javascript:window.open('/preview/${item.foldername}', '_blank');">Preview</button>
|
||||||
|
<button class="delete" type="button" onclick="del(this)">Delete</button>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
select('#list').html(list_init + t);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var socket = io(location.host);
|
||||||
|
var n = 0;
|
||||||
|
var fr = 20;
|
||||||
|
var arr = [];
|
||||||
|
var looper;
|
||||||
|
var score;
|
||||||
|
var logo;
|
||||||
|
var silence;
|
||||||
|
var clap;
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//**ENTRY begin >>>
|
||||||
|
// -- https://stackoverflow.com/a/39577640
|
||||||
|
function dataURLtoBlob(dataURL) {
|
||||||
|
let array, binary, i, len;
|
||||||
|
binary = atob(dataURL.split(",")[1]);
|
||||||
|
array = [];
|
||||||
|
i = 0;
|
||||||
|
len = binary.length;
|
||||||
|
while (i < len) {
|
||||||
|
array.push(binary.charCodeAt(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return new Blob([new Uint8Array(array)], {
|
||||||
|
type: "image/png",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm(event) {
|
||||||
|
|
||||||
|
//TODO : first check if there is a drawing or not. (pixels)
|
||||||
|
|
||||||
|
//
|
||||||
|
var submit = document.getElementById("submit");
|
||||||
|
submit.setAttribute('disabled', 'disabled');
|
||||||
|
select("html").style('cursor', 'progress');
|
||||||
|
|
||||||
|
//
|
||||||
|
var form = document.getElementById("form");
|
||||||
|
var fd = new FormData(form);
|
||||||
|
|
||||||
|
//
|
||||||
|
var unit = 45; //mask&override 'unit' -> for crispy png.
|
||||||
|
var img2 = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||||
|
img2.clear();
|
||||||
|
img2.strokeWeight(0);
|
||||||
|
img2.fill(255);
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
if (p[c * cols + r] == 1) {
|
||||||
|
img2.rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dataurl = img2.elt.toDataURL();
|
||||||
|
var pixels = dataURLtoBlob(dataurl);
|
||||||
|
fd.append("pixels", pixels, "pixels.png");
|
||||||
|
|
||||||
|
for (var pair of fd.entries()) {
|
||||||
|
console.log(pair[0] + ', ' + pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
//
|
||||||
|
var progress = select("#progress");
|
||||||
|
progress.style('display', 'unset');
|
||||||
|
//
|
||||||
|
request.open("POST", "/");
|
||||||
|
request.upload.onprogress = (pe) => {
|
||||||
|
if (pe.lengthComputable) {
|
||||||
|
progress.elt.max = pe.total;
|
||||||
|
progress.elt.value = pe.loaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request.onload = function () {
|
||||||
|
console.log('received http response');
|
||||||
|
console.log('response status: ', request.status);
|
||||||
|
console.log('response headers: ', request.getAllResponseHeaders());
|
||||||
|
console.log('response body: ', request.response);
|
||||||
|
}
|
||||||
|
request.onloadend = () => {
|
||||||
|
// check server's final feedback here.
|
||||||
|
if (request.status == 200) {
|
||||||
|
alert('Thank you! Check out the uploaded sound in the list below.');
|
||||||
|
} else {
|
||||||
|
alert('Oh! There was a problem transferring the sound file.\n If you send the sound file to wonjung24@gmail.com once more, we will add it.\n Admin Information: Server Response [' + request.status + ']');
|
||||||
|
}
|
||||||
|
// re-enable next upload.
|
||||||
|
submit.removeAttribute('disabled');
|
||||||
|
select("html").style('cursor', 'auto');
|
||||||
|
form.reset();
|
||||||
|
for (let a = 0; a < p.length; a++) p[a] = 0; //clear pixel-drawing, too.
|
||||||
|
// reload list.
|
||||||
|
list_reload();
|
||||||
|
}
|
||||||
|
request.send(fd);
|
||||||
|
|
||||||
|
//
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = document.getElementById('form');
|
||||||
|
form.addEventListener('submit', submitForm);
|
||||||
|
|
||||||
|
//// ---- for 'list' rendering ----
|
||||||
|
|
||||||
|
function del(that) {
|
||||||
|
console.log();
|
||||||
|
let text;
|
||||||
|
let pass = prompt("Please enter password!", "2-digit numbers");
|
||||||
|
if (/^\d{2}$/.test(pass)) {
|
||||||
|
var target = that.parentElement.parentElement.getAttribute('foldername');
|
||||||
|
const trydelete = async () => {
|
||||||
|
const response = await fetch('/delete/' + target + '/' + pass);
|
||||||
|
if (response.ok) {
|
||||||
|
const json = await response.json();
|
||||||
|
return Promise.resolve(json);
|
||||||
|
} else {
|
||||||
|
return Promise.reject('no response.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trydelete().then((resp) => {
|
||||||
|
if (resp.result) {
|
||||||
|
alert("Delete Success!");
|
||||||
|
// location.reload();
|
||||||
|
list_reload();
|
||||||
|
} else {
|
||||||
|
alert("Delete Failed-");
|
||||||
|
}
|
||||||
|
}).catch(console.log);
|
||||||
|
} else {
|
||||||
|
alert("Reminder: Password was 2-digit numbers...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = [];
|
||||||
|
var cols = 17;
|
||||||
|
var rows = 17;
|
||||||
|
var unit = 15; //px
|
||||||
|
var img;
|
||||||
|
var penselect = "pencil";
|
||||||
|
var scrollable = true;
|
||||||
|
// <<< end of **ENTRY
|
||||||
|
|
||||||
|
async function setup() {
|
||||||
|
|
||||||
|
//**PARADE
|
||||||
|
noCanvas();
|
||||||
|
if (windowWidth > 1500 && windowWidth > windowHeight) {
|
||||||
|
fr = 30;
|
||||||
|
} else {
|
||||||
|
fr = 20;
|
||||||
|
}
|
||||||
|
frameRate(fr);
|
||||||
|
|
||||||
|
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||||
|
//noLoop(); // <-- BUT, we want 1 for ENTRY graphics!
|
||||||
|
// --> use custom looper.
|
||||||
|
|
||||||
|
//**ENTRY
|
||||||
|
list_reload();
|
||||||
|
|
||||||
|
var cnv = createCanvas(cols * unit + 2, rows * unit + 2);
|
||||||
|
cnv.parent("p5");
|
||||||
|
img = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||||
|
document.querySelectorAll('.penselect').forEach(item => item.onclick = () => {
|
||||||
|
penselect = document.querySelector('input[name="penselect"]:checked').value;
|
||||||
|
});
|
||||||
|
//
|
||||||
|
for (var i = 0; i < cols * rows; i++) p.push(0);
|
||||||
|
document.querySelectorAll('.clear').forEach(item => item.onclick = () => {
|
||||||
|
for (let a = 0; a < p.length; a++) p[a] = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//**ENTRY begin >>>
|
||||||
|
function draw() {
|
||||||
|
//clear
|
||||||
|
clear();
|
||||||
|
|
||||||
|
//draw the grid
|
||||||
|
stroke(255);
|
||||||
|
strokeWeight(0.2);
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
noFill();
|
||||||
|
rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//pointer
|
||||||
|
fill(0, 255, 0);
|
||||||
|
strokeWeight(0);
|
||||||
|
|
||||||
|
//mouse way
|
||||||
|
circle(mouseX, mouseY, 10);
|
||||||
|
if (mouseIsPressed && mouseButton === LEFT) {
|
||||||
|
//find slot under the pointer
|
||||||
|
var mouseC = int(mouseX / unit);
|
||||||
|
var mouseR = int(mouseY / unit);
|
||||||
|
if (mouseC >= 0 && mouseC < cols && mouseR >= 0 && mouseR < rows) {
|
||||||
|
if (penselect == "pencil") p[mouseC * cols + mouseR] = 1;
|
||||||
|
else if (penselect == "erasor") p[mouseC * cols + mouseR] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//touch way
|
||||||
|
if (touches.length > 0) {
|
||||||
|
circle(touches[0].x, touches[0].y, 10);
|
||||||
|
//find slot under the pointer
|
||||||
|
var mouseC = int(touches[0].x / unit);
|
||||||
|
var mouseR = int(touches[0].y / unit);
|
||||||
|
if (mouseC >= 0 && mouseC < cols && mouseR >= 0 && mouseR < rows) {
|
||||||
|
// if (scrollable == true) {
|
||||||
|
// scrollable = false;
|
||||||
|
// // firefox browser @ my android phone -> url bar auto-hiding kills drawing experience.
|
||||||
|
// // --> https://stackoverflow.com/a/63221105
|
||||||
|
// document.body.style.marginTop = `-${window.pageYOffset}px`;
|
||||||
|
// document.body.style.position = 'fixed';
|
||||||
|
// document.body.style.overflowY = 'scroll';
|
||||||
|
// }
|
||||||
|
if (penselect == "pencil") p[mouseC * cols + mouseR] = 1;
|
||||||
|
else if (penselect == "erasor") p[mouseC * cols + mouseR] = 0;
|
||||||
|
} else {
|
||||||
|
// if (scrollable == false) {
|
||||||
|
// scrollable = true;
|
||||||
|
// // firefox browser @ my android phone -> url bar auto-hiding kills drawing experience.
|
||||||
|
// // --> https://stackoverflow.com/a/63221105
|
||||||
|
// document.body.style.position = '';
|
||||||
|
// document.body.style.overflowY = '';
|
||||||
|
// if (document.body.style.marginTop) {
|
||||||
|
// const scrollTop = -parseInt(document.body.style.marginTop, 10);
|
||||||
|
// document.body.style.marginTop = '';
|
||||||
|
// window.scrollTo(window.pageXOffset, scrollTop);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw img
|
||||||
|
image(img, 0, 0);
|
||||||
|
img.clear();
|
||||||
|
img.strokeWeight(0);
|
||||||
|
img.fill(255);
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
if (p[c * cols + r] == 1) {
|
||||||
|
img.rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// <<< end of **ENTRY
|
||||||
|
|
||||||
|
//
|
||||||
|
var myroom = -1;
|
||||||
|
var intro;
|
||||||
|
var ready;
|
||||||
|
//
|
||||||
|
socket.on("connect", async function () {
|
||||||
|
console.log("connected!");
|
||||||
|
setTimeout(gotConnection, 1000);
|
||||||
|
console.log("- injecting small delay to make sure p5js loaded. ~ 1s");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function gotConnection() {
|
||||||
|
//
|
||||||
|
console.log("- after delay!");
|
||||||
|
//
|
||||||
|
silence = (await AudioImport("/audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("/audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
//TESTING... fixed to room 1.
|
||||||
|
// myroom = 1;
|
||||||
|
|
||||||
|
if (myroom == -1 && selectAll(".roomsel").length == 0) {
|
||||||
|
//initial connection -> ask the room number.
|
||||||
|
|
||||||
|
var roomsel = createDiv();
|
||||||
|
selectAll(".parade")[0].child(roomsel);
|
||||||
|
roomsel.class("roomsel");
|
||||||
|
///
|
||||||
|
var b = createButton("시작하기 Start!"); //, "1");
|
||||||
|
var d = createDiv("흐름을 향하여 걷는 Walking towards the Flow");
|
||||||
|
d.class("title");
|
||||||
|
let starter = function () {
|
||||||
|
silence.start();
|
||||||
|
clap.start();
|
||||||
|
// myroom = parseInt(this.value());
|
||||||
|
myroom = 1;
|
||||||
|
|
||||||
|
socket.emit("room", myroom, function (res) {
|
||||||
|
if (res) {
|
||||||
|
console.log("entered the room -> " + myroom);
|
||||||
|
|
||||||
|
// myroom-indicator
|
||||||
|
// var tag = createP(str(myroom));
|
||||||
|
// selectAll(".parade")[0].child(tag);
|
||||||
|
|
||||||
|
//
|
||||||
|
// setTimeout(function() {
|
||||||
|
// ready = createP("퍼레이드 시작합니다!!");
|
||||||
|
// ready.position(
|
||||||
|
// windowWidth / 2 - windowWidth / 10,
|
||||||
|
// windowHeight / 2
|
||||||
|
// );
|
||||||
|
// }, 1000);
|
||||||
|
} else {
|
||||||
|
console.log("rejected!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
selectAll(".roomsel").forEach(item => {
|
||||||
|
item.remove();
|
||||||
|
// 1 second popup '.intro' div
|
||||||
|
intro = createDiv(
|
||||||
|
"«흐름을 향하여 걷는» 소리 행렬이 곧 나타납니다. <br>오른쪽에서 왼쪽으로 지나가는 소리가 들리지 않는다면 볼륨을 확인해주시고, 스마트폰 환경에서는 진동 모드를 해제해주세요. <br>스크롤을 내리면 참여 방법을 확인하고 소리를 업로드할 수 있으며, 스크롤을 맨 위로 올리거나 오른쪽 위의 글씨를 클릭해 퍼레이드로 다시 돌아올 수 있습니다. <br><br>The sound parade «Walking Towards the Flow», appears soon. <br>If you can't hear the sound passing from right to left, check the volume, and if you're using a smartphone, turn off the vibrate mode. <br>Scroll down to see how to join and upload sound, scroll to the top or click the text “parade” on the top right to return to the parade.<br><img class='arrow' src='/arrow.svg'>"
|
||||||
|
);
|
||||||
|
intro.class("notice intro"); //-> fadeout & disapear by css animation style.
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
b.mouseClicked(starter);
|
||||||
|
d.mouseClicked(starter);
|
||||||
|
roomsel.child(d);
|
||||||
|
roomsel.child(b);
|
||||||
|
} else {
|
||||||
|
//re-connection -> just connect to remembered room!
|
||||||
|
socket.emit("room", myroom, function (res) {
|
||||||
|
if (res) {
|
||||||
|
console.log("entered the room -> " + myroom);
|
||||||
|
} else {
|
||||||
|
console.log("rejected!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//var fading_factor = 0.3; //30%
|
||||||
|
var fading_factor = 0.5; //50%
|
||||||
|
socket.on("post", async function (post) {
|
||||||
|
|
||||||
|
console.log(post);
|
||||||
|
var list = await new Promise((resolve, reject) => {
|
||||||
|
loadJSON("/entries", (json) => resolve(json));
|
||||||
|
})
|
||||||
|
console.log(list);
|
||||||
|
|
||||||
|
// var object = post.object;
|
||||||
|
var object = {
|
||||||
|
"id": 1,
|
||||||
|
"type": "abc",
|
||||||
|
"src": "/uploads/" + list[post] + "/pixels.png",
|
||||||
|
"audio": "/uploads/" + list[post] + "/audio.mp3",
|
||||||
|
"alt": "알트",
|
||||||
|
"size": {
|
||||||
|
"base": 40,
|
||||||
|
"random": 20
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"base": 20,
|
||||||
|
"random": 10
|
||||||
|
},
|
||||||
|
"showtime": 20000
|
||||||
|
};
|
||||||
|
console.log(object);
|
||||||
|
|
||||||
|
var img = createImg(object.src, object.alt, "", async function (im) {
|
||||||
|
//로딩이 끝나면, start!
|
||||||
|
var pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
var snd = await AudioImport(object.audio); // NOTE: url with spaces didn't work here.
|
||||||
|
snd.connect(pv).start();
|
||||||
|
snd.loop = true;
|
||||||
|
|
||||||
|
//로딩이 끝나면, show!
|
||||||
|
im.show();
|
||||||
|
|
||||||
|
//그림의 크기와 초기 위치 ==> 가로 보기인 경우
|
||||||
|
var width = 0;
|
||||||
|
if (windowWidth > windowHeight) {
|
||||||
|
|
||||||
|
width = (windowHeight * (object.size.base * 1.4 + object.size.random * Math.random())) / 100; // 좀더 크게 + 40% (ratio)
|
||||||
|
im.size(width, AUTO);
|
||||||
|
|
||||||
|
im.position(
|
||||||
|
windowWidth * (1 + fading_factor),
|
||||||
|
(windowHeight * (object.y.base + object.y.random * Math.random()) * 0.5) / 100 // 좀더 위로 위로 - 50% (ratio)
|
||||||
|
);
|
||||||
|
|
||||||
|
//그림의 크기와 초기 위치 ==> 세로 보기인 경우
|
||||||
|
} else {
|
||||||
|
|
||||||
|
width = (windowHeight * (object.size.base + object.size.random * Math.random())) / 100; // json에서 정한 크기 그대로. (ratio)
|
||||||
|
im.size(width, AUTO);
|
||||||
|
|
||||||
|
im.position(
|
||||||
|
windowWidth * (1 + fading_factor),
|
||||||
|
(windowHeight * (object.y.base + object.y.random * Math.random())) / 100 // json에서 정한 위치 그대로. (ratio)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//추가 정보들
|
||||||
|
im.attribute("data-type", object.type);
|
||||||
|
im.attribute("data-showtime", object.showtime / 1000); //milli-sec. -> seconds.
|
||||||
|
|
||||||
|
//'아이콘' 들은 애니메이션을 시켜줘야 함...
|
||||||
|
if (object.type == "icon") {
|
||||||
|
//
|
||||||
|
im.class("rotate");
|
||||||
|
im.style("animation-duration", object.rotate + "s");
|
||||||
|
var orgs = im.style("transform-origin").split(" ");
|
||||||
|
var str = parseFloat(orgs[0]) + object.pivot.x + "px";
|
||||||
|
str = str + " " + parseFloat(orgs[1]) + object.pivot.y + "px";
|
||||||
|
im.style("transform-origin", str);
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
//로딩이 다 되면, rendering array에 추가.
|
||||||
|
var bundle = {
|
||||||
|
ref: object,
|
||||||
|
img: im,
|
||||||
|
sound: snd,
|
||||||
|
panvol: pv,
|
||||||
|
width: width
|
||||||
|
};
|
||||||
|
arr.push(bundle);
|
||||||
|
});
|
||||||
|
|
||||||
|
//첨에는 hide
|
||||||
|
img.hide();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||||
|
// --> custom looper is ok.
|
||||||
|
var looper;
|
||||||
|
(looper = function (timeout) {
|
||||||
|
setTimeout(async function () {
|
||||||
|
|
||||||
|
//
|
||||||
|
for (var i = arr.length - 1; i >= 0; i -= 1) {
|
||||||
|
var bundle = arr[i];
|
||||||
|
var img = bundle.img;
|
||||||
|
var showtime = parseFloat(img.attribute("data-showtime"));
|
||||||
|
var type = img.attribute("data-type");
|
||||||
|
var x = img.position().x;
|
||||||
|
var y = img.position().y;
|
||||||
|
y = y + random(-1, 1);
|
||||||
|
x = x - windowWidth / (fr * showtime);
|
||||||
|
|
||||||
|
//
|
||||||
|
if (type == "icon") {
|
||||||
|
img.style("z-index", "-1");
|
||||||
|
}
|
||||||
|
3;
|
||||||
|
|
||||||
|
img.position(x, y);
|
||||||
|
var pan = (x / windowWidth) * 2 - 1;
|
||||||
|
|
||||||
|
//panning
|
||||||
|
var snd = bundle.sound;
|
||||||
|
var pv = bundle.panvol;
|
||||||
|
if (x >= -bundle.width && x < windowWidth) {
|
||||||
|
pan = ((x + bundle.width) / (windowWidth + bundle.width)) * 2 - 1;
|
||||||
|
pv.pan.value = pan;
|
||||||
|
pv.volume.value = 0;//(dB)
|
||||||
|
} else {
|
||||||
|
var range;
|
||||||
|
var knob;
|
||||||
|
if (x >= windowWidth) {
|
||||||
|
range = windowWidth * fading_factor
|
||||||
|
knob = x - windowWidth;
|
||||||
|
pv.pan.value = 1;
|
||||||
|
pv.volume.value = knob / range * -20;
|
||||||
|
} else if (x < -bundle.width) {
|
||||||
|
range = windowWidth * fading_factor
|
||||||
|
knob = (x + bundle.width) * -1;
|
||||||
|
pv.pan.value = -1;
|
||||||
|
pv.volume.value = knob / range * -20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//scroll down -> volume down
|
||||||
|
// if (userscroll > windowHeight*2) {
|
||||||
|
// pv.volume.value = 0; //off
|
||||||
|
// } else if (userscroll >= windowHeight && userscroll < windowHeight*2) {
|
||||||
|
// pv.volume.value = (1 - (userscroll - windowHeight)/windowHeight) * pv.volume.value; //fade out
|
||||||
|
// }
|
||||||
|
|
||||||
|
//remove with sound fade-out
|
||||||
|
var exit_x = -bundle.width - windowWidth * fading_factor;
|
||||||
|
if (x < exit_x) {
|
||||||
|
img.remove();
|
||||||
|
snd.stop();
|
||||||
|
delete snd;
|
||||||
|
delete pv;
|
||||||
|
arr.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
looper(1000 / fr);
|
||||||
|
}, timeout);
|
||||||
|
})(1000 / fr);
|
||||||
|
|
||||||
|
// function randomvoiceplay() {
|
||||||
|
// (looper = function(timeout) {
|
||||||
|
// setTimeout(function() {
|
||||||
|
// voice[int(random(19))].play();
|
||||||
|
// looper(random(8000, 12000));
|
||||||
|
// }, timeout);
|
||||||
|
// })(8000);
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>흐름을 향하여 걷는</title>
|
<title>흐름을 향하여 걷는 Walking towards the Flow</title>
|
||||||
|
<meta itemprop="description" content="온라인 공간에 소리의 행렬을 만드는 사운드 퍼레이드입니다.">
|
||||||
|
<meta itemprop="image" content="/thumbnail.jpg">
|
||||||
<link rel="stylesheet" href="/default.css" />
|
<link rel="stylesheet" href="/default.css" />
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
|
|
@ -49,60 +51,62 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="bg"></div>
|
<div class="bg"></div>
|
||||||
|
<div id="top"></div>
|
||||||
<div class="parade"></div>
|
<div class="parade"></div>
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
<div class="first"><a href="/en/entry">EN</a></div>
|
|
||||||
<div class="second"><a href="/" target="_blank">퍼레이드</a></div>
|
|
||||||
<div class="notice">
|
<div class="notice">
|
||||||
|
<div class="sticky">
|
||||||
|
<div class="first"><a href="/en/">EN</a></div>
|
||||||
|
<div class="second"><a href="#top">퍼레이드</a></div>
|
||||||
|
</div>
|
||||||
<section>
|
<section>
|
||||||
<h1>흐름을 향하여 걷는</h1>
|
<h1>소개</h1>
|
||||||
<p>
|
<p>
|
||||||
《흐름을 향하여 걷는》은 사운드 퍼레이드가 열리는 온라인 공간입니다.
|
«흐름을 향하여 걷는»은 온라인 공간에서 열리는 사운드 퍼레이드입니다. 이 소리의 행렬은 여러분이 보내주시는 소리가 모여 만들어지며, 누구나 참여하실 수 있습니다.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
퍼레이드가 진행되는 이 곳, <a href="https://walkingtowardstheflow.xyz">walkingtowardstheflow.xyz</a>는 누구나 소리를 업로드하고 감상할 수 있는 사운드 피드(feed)이자 소리의 행렬이 지나는 길이기도 합니다. 이는 각자의 시공간에 흩어져 살아가는 우리가 고개를 잠깐 돌리기만 하면 함께 걸을 수 있는 흐름이 아주 가까운 곳에 있음을 말합니다.
|
소리가 흐르는 길은 별들이 순회하는 저 은하수와 같이, 언제나 그 자리에 있으면서 여러분이 소리를 듣고 있든 그렇지 않든 흐르고 있습니다.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
《흐름을 향하여 걷는》은 오프라인 공연으로 함께 하실 수도 있습니다. 11월 27일(일) 공연 당일에는 웹사이트에 모인 소리들이 물리적 공간에 펼쳐집니다. 이 공연은 함께 걷고 멈추며, 흐름에 공감해 주신 분들의 소리를 듣는 참여형 공연입니다.
|
이 영원한 퍼레이드의 영속성은 우리에게 최후의 안도를 줍니다. 때로는 자신이 위성궤도를 무한히 돌고 있는 우주 부스러기라는 생각이 들다가도, 그것이 관측되는 순간 더는 외로움이나 잊혀짐이 아니라
|
||||||
|
기억됨이 됩니다.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
소리는 본질적으로 공기를 진동하는 에너지이며, 이 진동은 우리의 고막 뿐 아니라 온 몸과 피부를 온통 진동하는 촉각적인 접촉입니다. 우리는 소리들의 부드러운 접촉을 통해 그 소리의 발신자들이 부드럽게 우리의 손을 잡아 퍼레이드로 초대하고 있다는 것을 어렵지 않게 눈치챌 수 있습니다.
|
«흐름을 향하여 걷는»은 온사이트 공연으로 함께 하실 수도 있습니다. 11월 27일(일) 진행하는 공연에서는 웹사이트에 모인 소리들이 물리적 공간에 펼쳐지고, 관객은 함께 걷고 멈추며 흐름에
|
||||||
|
공감해주신 분들의 소리를 듣습니다.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
소리가 흐르는 길은 영혼이 회귀하는 저 광맥과 같이, 별들이 순회하는 저 은하수와 같이, 언제나 그 자리에 있으면서 당신이 소리를 듣고 있든 그렇지 않든 무관하게 흐르고 있습니다.
|
온라인으로 진행하는 소리 모집과 온사이트 공연에 많은 관심과 참여 바랍니다.<br />
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
당신은 혼자이지만 언제나 원하는 순간에 이 흐름을 향해 걸어 들어올 수 있으며, 당신이 원한다면 함께 걷고, 따라걷고, 멈춰서서 바라보고, 당신의 소리, 당신의 만지는 손길들을 이 흐름에 띄워 보낼 수도 있습니다. 우리는 그렇게 손을 잡을 수 있습니다.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
이 영원한 퍼레이드의 영속성은 우리에게 최후의 안도를 줍니다. 때로는 자신이 위성궤도를 무한히 돌고 있는 우주 부스러기(debris)라는 생각이 들다가도, 그것이 관측되는 순간 더는 외롭거나 잊혀짐이 아니라 기억됨이 됩니다.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
소리 모집에 많은 참여 부탁드립니다.<br />
|
|
||||||
*혐오표현은 《흐름을 향하여 걷는》에 모인 사람들이 보호해야 할 표현의 자유에 포함되지 않으므로 삭제될 수 있습니다.
|
|
||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<p>
|
<p>
|
||||||
소리 모집<br />
|
소리 모집<br />
|
||||||
2022년 11월 13일(일)–2022년 11월 26일(토)
|
2022년 11월 13일(일)–26일(토)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
퍼포먼스<br />
|
공연 일정<br />
|
||||||
2022년 11월 27일(일), 16:00
|
2022년 11월 27일(일), 16:00
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
장소<br />
|
공연 장소<br />
|
||||||
서울시립미술관 서소문본관 1층 로비, 2층 러닝스테이션, 옥상정원
|
서울시립미술관 서소문본관 1층 로비, 2층 러닝스테이션, 옥상정원
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
작가<br />
|
공연 구성<br />
|
||||||
<a href="https://dianaband.info" target="_blank">다이애나밴드</a> X <a href="https://cgyoon.kr/"
|
무리0 약속들 — 무리1 깃발들 — 무리2 신체들 — 무리3 사물들 — 무리4 누구들
|
||||||
target="_blank">윤충근</a> (문의: wonjung24@gmail.com)
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
퍼레이드 구성<br />
|
공연 신청<br />
|
||||||
무리0 약속들 — 무리1 깃발들 — 무리2 신체들 — 무리3 사물들 — 무리4 누구들
|
<a href="https://zrr.kr/aHTS" target="_blank">https://zrr.kr/aHTS</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
작가<br />
|
||||||
|
<a href="http://dianaband.info" target="_blank">다이애나밴드</a> × <a href="https://cgyoon.kr/"
|
||||||
|
target="_blank">윤충근</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
문의<br />wonjung24@gmail.com
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -119,6 +123,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li>2. 녹음한 소리 파일을 업로드해주세요.</li>
|
<li>2. 녹음한 소리 파일을 업로드해주세요.</li>
|
||||||
<li>3. 소리의 제목과 묘사을 입력한 뒤, 소리의 모양을 그려 제출해주세요.</li>
|
<li>3. 소리의 제목과 묘사을 입력한 뒤, 소리의 모양을 그려 제출해주세요.</li>
|
||||||
|
<li>* 혐오 표현이 포함된 음원을 업로드하시면 삭제됩니다.</li>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|
@ -183,6 +188,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input id="submit" type="submit" value="보내기!" />
|
<input id="submit" type="submit" value="보내기!" />
|
||||||
|
<progress id="progress" max="100" value="70" style="display:none"></progress>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -190,36 +196,16 @@
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section id="list_init_kr" style="display:none">
|
||||||
<h1>소리 목록</h1>
|
<h1>소리 목록</h1>
|
||||||
<p>제출해주신 소리는 아래 목록에 시간순으로 쌓입니다. 각각의 모양을 클릭하면, 모양에 해당하는 소리를 듣고 소리에 대한 정보를 확인할 수 있습니다.</p>
|
<p>제출해주신 소리는 아래 목록에 시간순으로 쌓입니다. 각각의 모양을 클릭하면, 모양에 해당하는 소리를 듣고 소리에 대한 정보를 확인할 수 있습니다.</p>
|
||||||
{{#each list}}
|
</section>
|
||||||
<div class="items" foldername="{{this.foldername}}">
|
<section id="list_init_en" style="display:none">
|
||||||
<details>
|
<h1>Sound List</h1>
|
||||||
<summary><img class="drawing"
|
<p>The sounds you submitted will be stacked in chronological order in the list below. By clicking on each shape,
|
||||||
src="https://p.dianaband.info/public/sound-parade/{{this.foldername}}/pixels.png" /></summary>
|
you can hear the sound corresponding to the shape and check the information about the sound.</p>
|
||||||
<audio class="sound" preload="none" controls>
|
</section>
|
||||||
<source src="https://p.dianaband.info/public/sound-parade/{{this.foldername}}/audio.mp3"
|
<section id="list">
|
||||||
type="audio/mpeg">
|
|
||||||
</audio>
|
|
||||||
<ul class="soundinfo">
|
|
||||||
<hr>
|
|
||||||
<li>유형 | <div class="group">{{this.group}}</div>
|
|
||||||
</li>
|
|
||||||
<hr>
|
|
||||||
<li>제목 | <div class="title">{{this.title}}</div>
|
|
||||||
</li>
|
|
||||||
<hr>
|
|
||||||
<li>묘사 | <div class="comment">{{this.comment}}</div>
|
|
||||||
</li>
|
|
||||||
<hr>
|
|
||||||
</ul>
|
|
||||||
<button class="preview" type="button"
|
|
||||||
onclick="javascript:window.open('/preview/{{this.foldername}}', '_blank');">미리보기</button>
|
|
||||||
<button class="delete" type="button" onclick="del(this)">삭제</button>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</section>
|
</section>
|
||||||
</div><!-- div class="notice" -->
|
</div><!-- div class="notice" -->
|
||||||
</div><!-- div class="entry" -->
|
</div><!-- div class="entry" -->
|
||||||
|
|
@ -231,6 +217,38 @@
|
||||||
// window.location.replace("https://" + location.host);
|
// window.location.replace("https://" + location.host);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
function list_reload() {
|
||||||
|
loadJSON('/list', list => {
|
||||||
|
//if lang == kr
|
||||||
|
let list_init = select('#list_init_kr').html();
|
||||||
|
//if lang == en
|
||||||
|
// let list_init = select('#list_init_en').html();
|
||||||
|
|
||||||
|
//
|
||||||
|
var t = "";
|
||||||
|
list.forEach(item => {
|
||||||
|
t = t + `
|
||||||
|
<div class="items" foldername="${item.foldername}">
|
||||||
|
<details>
|
||||||
|
<summary><img class="drawing" src="/uploads/${item.foldername}/pixels.png" /></summary>
|
||||||
|
<audio class="sound" preload="none" controls>
|
||||||
|
<source src="/uploads/${item.foldername}/audio.mp3" type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
<ul class="soundinfo"><hr>
|
||||||
|
<li>유형 | <div class="group">${item.group}</div></li><hr>
|
||||||
|
<li>제목 | <div class="title">${item.title}</div></li><hr>
|
||||||
|
<li>묘사 | <div class="comment">${item.comment}</div></li><hr>
|
||||||
|
</ul>
|
||||||
|
<button class="preview" type="button" onclick="javascript:window.open('/preview/${item.foldername}', '_blank');">미리보기</button>
|
||||||
|
<button class="delete" type="button" onclick="del(this)">삭제</button>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
select('#list').html(list_init + t);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var socket = io(location.host);
|
var socket = io(location.host);
|
||||||
var n = 0;
|
var n = 0;
|
||||||
var fr = 20;
|
var fr = 20;
|
||||||
|
|
@ -264,7 +282,7 @@
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitForm(event) {
|
function submitForm(event) {
|
||||||
|
|
||||||
//TODO : first check if there is a drawing or not. (pixels)
|
//TODO : first check if there is a drawing or not. (pixels)
|
||||||
|
|
@ -301,11 +319,37 @@
|
||||||
|
|
||||||
//
|
//
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
request.open("POST", "/entry");
|
//
|
||||||
request.onload = () => {
|
var progress = select("#progress");
|
||||||
alert('감사합니다!');
|
progress.style('display', 'unset');
|
||||||
location.reload();
|
//
|
||||||
//location.assign("/submit");
|
request.open("POST", "/");
|
||||||
|
request.upload.onprogress = (pe) => {
|
||||||
|
if (pe.lengthComputable) {
|
||||||
|
progress.elt.max = pe.total;
|
||||||
|
progress.elt.value = pe.loaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request.onload = function () {
|
||||||
|
console.log('received http response');
|
||||||
|
console.log('response status: ', request.status);
|
||||||
|
console.log('response headers: ', request.getAllResponseHeaders());
|
||||||
|
console.log('response body: ', request.response);
|
||||||
|
}
|
||||||
|
request.onloadend = () => {
|
||||||
|
// check server's final feedback here.
|
||||||
|
if (request.status == 200) {
|
||||||
|
alert('감사합니다. 아래에 소리목록에서 업로드된 음원을 확인해보세요.');
|
||||||
|
} else {
|
||||||
|
alert('앗! 소리 파일 전송에 문제가 생겼어요\n wonjung24@gmail.com에 소리 파일을 한번 더 보내주시면 추가하겠습니다.\n 관리자 정보: 서버 응답 [' + request.status + ']');
|
||||||
|
}
|
||||||
|
// re-enable next upload.
|
||||||
|
submit.removeAttribute('disabled');
|
||||||
|
select("html").style('cursor', 'auto');
|
||||||
|
form.reset();
|
||||||
|
for (let a = 0; a < p.length; a++) p[a] = 0; //clear pixel-drawing, too.
|
||||||
|
// reload list.
|
||||||
|
list_reload();
|
||||||
}
|
}
|
||||||
request.send(fd);
|
request.send(fd);
|
||||||
|
|
||||||
|
|
@ -336,7 +380,8 @@
|
||||||
trydelete().then((resp) => {
|
trydelete().then((resp) => {
|
||||||
if (resp.result) {
|
if (resp.result) {
|
||||||
alert("지우기 성공!");
|
alert("지우기 성공!");
|
||||||
location.reload();
|
// location.reload();
|
||||||
|
list_reload();
|
||||||
} else {
|
} else {
|
||||||
alert("지우기 실패-");
|
alert("지우기 실패-");
|
||||||
}
|
}
|
||||||
|
|
@ -366,11 +411,16 @@
|
||||||
}
|
}
|
||||||
frameRate(fr);
|
frameRate(fr);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
//p5 'draw()' doesn't work if user is not looking at the tab.
|
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||||
//noLoop(); // <-- BUT, we want 1 for ENTRY graphics!
|
//noLoop(); // <-- BUT, we want 1 for ENTRY graphics!
|
||||||
// --> use custom looper.
|
// --> use custom looper.
|
||||||
|
|
||||||
//**ENTRY
|
//**ENTRY
|
||||||
|
list_reload();
|
||||||
|
|
||||||
var cnv = createCanvas(cols * unit + 2, rows * unit + 2);
|
var cnv = createCanvas(cols * unit + 2, rows * unit + 2);
|
||||||
cnv.parent("p5");
|
cnv.parent("p5");
|
||||||
img = createGraphics(cols * unit + 2, rows * unit + 2);
|
img = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||||
|
|
@ -470,7 +520,13 @@
|
||||||
//
|
//
|
||||||
socket.on("connect", async function () {
|
socket.on("connect", async function () {
|
||||||
console.log("connected!");
|
console.log("connected!");
|
||||||
|
setTimeout(gotConnection, 1000);
|
||||||
|
console.log("- injecting small delay to make sure p5js loaded. ~ 1s");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function gotConnection() {
|
||||||
|
//
|
||||||
|
console.log("- after delay!");
|
||||||
//
|
//
|
||||||
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
@ -485,13 +541,14 @@
|
||||||
selectAll(".parade")[0].child(roomsel);
|
selectAll(".parade")[0].child(roomsel);
|
||||||
roomsel.class("roomsel");
|
roomsel.class("roomsel");
|
||||||
///
|
///
|
||||||
var b = createButton("시작하기 Start!", "1");
|
var b = createButton("시작하기 Start!"); //, "1");
|
||||||
var d = createDiv("<a href='/entry'>흐름을 향하여 걷는 Walking towards the Flow</a>");
|
var d = createDiv("흐름을 향하여 걷는 Walking towards the Flow");
|
||||||
d.class("title");
|
d.class("title");
|
||||||
b.mouseClicked(function () {
|
let starter = function () {
|
||||||
silence.start();
|
silence.start();
|
||||||
clap.start();
|
clap.start();
|
||||||
myroom = parseInt(this.value());
|
// myroom = parseInt(this.value());
|
||||||
|
myroom = 1;
|
||||||
|
|
||||||
socket.emit("room", myroom, function (res) {
|
socket.emit("room", myroom, function (res) {
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -519,12 +576,14 @@
|
||||||
item.remove();
|
item.remove();
|
||||||
// 1 second popup '.intro' div
|
// 1 second popup '.intro' div
|
||||||
intro = createDiv(
|
intro = createDiv(
|
||||||
"«흐름을 향하여 걷는»은 온라인 공간에 소리의 행렬을 만드는 사운드 퍼레이드입니다. 누구나 참여할 수 있는 이 퍼레이드는 여러분이 보내주시는 소리가 모여 만들어집니다.<br>소리가 들리지 않는다면, 볼륨을 확인해주시고, 스마트폰 환경에서는 진동해제해주세요. <br><br> «Walking Toward the Flow» is a sound parade that creates a procession of sounds in the online space. Anyone can participate. This parade is made by collecting the sounds you sent.<br>If there is no sound, check the volume and turn off the vibration in the smartphone environment."
|
"«흐름을 향하여 걷는» 소리 행렬이 곧 나타납니다. <br>오른쪽에서 왼쪽으로 지나가는 소리가 들리지 않는다면 볼륨을 확인해주시고, 스마트폰 환경에서는 진동 모드를 해제해주세요. <br>스크롤을 내리면 참여 방법을 확인하고 소리를 업로드할 수 있으며, 스크롤을 맨 위로 올리거나 오른쪽 위의 글씨를 클릭해 퍼레이드로 다시 돌아올 수 있습니다. <br><br>The sound parade «Walking towards the Flow», appears soon. <br>If you can't hear the sound passing from right to left, check the volume, and if you're using a smartphone, turn off the vibrate mode. <br>Scroll down to see how to join and upload sound, scroll to the top or click the text “parade” on the top right to return to the parade.<br><img class='arrow' src='arrow.svg'>"
|
||||||
);
|
);
|
||||||
intro.class("notice intro"); //-> fadeout & disapear by css animation style.
|
intro.class("notice intro"); //-> fadeout & disapear by css animation style.
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
};
|
||||||
|
b.mouseClicked(starter);
|
||||||
|
d.mouseClicked(starter);
|
||||||
roomsel.child(d);
|
roomsel.child(d);
|
||||||
roomsel.child(b);
|
roomsel.child(b);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -537,7 +596,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
//var fading_factor = 0.3; //30%
|
//var fading_factor = 0.3; //30%
|
||||||
var fading_factor = 0.5; //50%
|
var fading_factor = 0.5; //50%
|
||||||
|
|
@ -553,8 +612,8 @@
|
||||||
var object = {
|
var object = {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"type": "abc",
|
"type": "abc",
|
||||||
"src": "https://p.dianaband.info/public/sound-parade/" + list[post] + "/pixels.png",
|
"src": "/uploads/" + list[post] + "/pixels.png",
|
||||||
"audio": "https://p.dianaband.info/public/sound-parade/" + list[post] + "/audio.mp3",
|
"audio": "/uploads/" + list[post] + "/audio.mp3",
|
||||||
"alt": "알트",
|
"alt": "알트",
|
||||||
"size": {
|
"size": {
|
||||||
"base": 40,
|
"base": 40,
|
||||||
|
|
@ -683,6 +742,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//scroll down -> volume down
|
||||||
|
// if (userscroll > windowHeight*2) {
|
||||||
|
// pv.volume.value = 0; //off
|
||||||
|
// } else if (userscroll >= windowHeight && userscroll < windowHeight*2) {
|
||||||
|
// pv.volume.value = (1 - (userscroll - windowHeight)/windowHeight) * pv.volume.value; //fade out
|
||||||
|
// }
|
||||||
|
|
||||||
//remove with sound fade-out
|
//remove with sound fade-out
|
||||||
var exit_x = -bundle.width - windowWidth * fading_factor;
|
var exit_x = -bundle.width - windowWidth * fading_factor;
|
||||||
if (x < exit_x) {
|
if (x < exit_x) {
|
||||||
|
|
|
||||||
712
src/pages/parade.old.html
Normal file
712
src/pages/parade.old.html
Normal file
|
|
@ -0,0 +1,712 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>흐름을 향하여 걷는 Walking towards the Flow</title>
|
||||||
|
<meta itemprop="description" content="온라인 공간에 소리의 행렬을 만드는 사운드 퍼레이드입니다.">
|
||||||
|
<meta itemprop="image" content="/thumbnail.jpg">
|
||||||
|
<link rel="stylesheet" href="/default.css" />
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#defaultCanvas0 {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#p5 {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
padding: 1em 1em;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.penselect {
|
||||||
|
margin: 1em 0em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="/js/p5-v1.1.9.min.js"></script>
|
||||||
|
<script src="/js/socket-v2.3.0.io.slim.js"></script>
|
||||||
|
<script src="/js/Tone-14.8.36.min.js"></script>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&display=swap" rel="stylesheet" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="bg"></div>
|
||||||
|
<div id="top"></div>
|
||||||
|
<div class="parade"></div>
|
||||||
|
<div class="entry">
|
||||||
|
<div class="notice">
|
||||||
|
<div class="sticky">
|
||||||
|
<div class="first"><a href="#">EN</a></div>
|
||||||
|
<div class="second"><a href="#top">퍼레이드</a></div>
|
||||||
|
</div>
|
||||||
|
<section>
|
||||||
|
<h1>소개</h1>
|
||||||
|
<p>
|
||||||
|
«흐름을 향하여 걷는»은 온라인 공간에서 열리는 사운드 퍼레이드입니다. 이 소리의 행렬은 여러분이 보내주시는 소리가 모여 만들어지며, 누구나 참여하실 수 있습니다.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
소리가 흐르는 길은 별들이 순회하는 저 은하수와 같이, 언제나 그 자리에 있으면서 여러분이 소리를 듣고 있든 그렇지 않든 흐르고 있습니다.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
이 영원한 퍼레이드의 영속성은 우리에게 최후의 안도를 줍니다. 때로는 자신이 위성궤도를 무한히 돌고 있는 우주 부스러기라는 생각이 들다가도, 그것이 관측되는 순간 더는 외로움이나 잊혀짐이 아니라 기억됨이 됩니다.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
«흐름을 향하여 걷는»은 오프라인 공연으로 함께 하실 수도 있습니다. 11월 27일(일) 진행하는 공연에서는 웹사이트에 모인 소리들이 물리적 공간에 펼쳐지고, 관객은 함께 걷고 멈추며 흐름에 공감해주신 분들의 소리를 듣습니다.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
온라인으로 진행하는 소리 모집과 오프라인 공연에 많은 관심과 참여 바랍니다.<br />
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<div class="info">
|
||||||
|
<p>
|
||||||
|
소리 모집<br />
|
||||||
|
2022년 11월 13일(일)–26일(토)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
공연 일정<br />
|
||||||
|
2022년 11월 27일(일), 16:00
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
공연 장소<br />
|
||||||
|
서울시립미술관 서소문본관 1층 로비, 2층 러닝스테이션, 옥상정원
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
공연 구성<br />
|
||||||
|
무리0 약속들 — 무리1 깃발들 — 무리2 신체들 — 무리3 사물들 — 무리4 누구들
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
작가<br />
|
||||||
|
<a href="https://dianaband.info" target="_blank">다이애나밴드</a> × <a href="https://cgyoon.kr/"
|
||||||
|
target="_blank">윤충근</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
문의<br />wonjung24@gmail.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="participation">
|
||||||
|
<h1>참여 방법</h1>
|
||||||
|
<ol>
|
||||||
|
<li>1. 무리의 특성에 따라 녹음기기나 핸드폰으로 30초 가량의 소리를 녹음해주세요.
|
||||||
|
<ol class="category">
|
||||||
|
<li>• 무리1 깃발들 — 좋아하는 것, 가치에 대해 다섯 번 외쳐주세요.</li>
|
||||||
|
<li>• 무리2 신체들 — 몸에서 나는 소리를 녹음해 보아요. 박수, 휘파람도 좋아요.</li>
|
||||||
|
<li>• 무리3 사물들 — 주변 사물들의 소리를 찾아 주세요. 뽁뽁이 소리, 구슬 소리</li>
|
||||||
|
<li>• 무리4 누구들 — 누구의 소리를 모아주세요. 반려동물, 물 소리, 산책의 장소</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
<li>2. 녹음한 소리 파일을 업로드해주세요.</li>
|
||||||
|
<li>3. 소리의 제목과 묘사을 입력한 뒤, 소리의 모양을 그려 제출해주세요.</li>
|
||||||
|
<li>* 혐오 표현이 포함된 음원을 업로드하시면 삭제됩니다.</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>소리 제출</h1>
|
||||||
|
<form id="form" method="POST" enctype="multipart/form-data">
|
||||||
|
<ul class="submit">
|
||||||
|
<li>
|
||||||
|
<h3>1. 소리 유형</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="flag" /> 무리1 깃발들
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="body" /> 무리2 신체들
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="object" /> 무리3 사물들
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="group" autocomplete="off" value="any" required /> 무리4 누구들
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>2. 소리 파일</h3>
|
||||||
|
<input type="file" name="audiofile" autocomplete="off" required />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>3. 소리 제목</h3>
|
||||||
|
<input id="title" name="title" type="text" autocomplete="off" required />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>4. 소리 묘사</h3>
|
||||||
|
<input id="comment" name="comment" type="text" autocomplete="off" required />
|
||||||
|
</li>
|
||||||
|
<!-- pixels.png ~ made with p5.js -->
|
||||||
|
<li class="noscroll">
|
||||||
|
<h3>5. 소리 모양</h3>
|
||||||
|
<div>
|
||||||
|
<div id="p5">
|
||||||
|
</div>
|
||||||
|
<div class="tools">
|
||||||
|
<div>
|
||||||
|
<input class="penselect" type="radio" value="pencil" name="penselect" id="penselect-pencil"
|
||||||
|
autocomplete="off" checked>
|
||||||
|
<label for="penselect-pencil">연필</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input class="penselect" type="radio" value="erasor" name="penselect" id="penselect-erasor"
|
||||||
|
autocomplete="off">
|
||||||
|
<label for="penselect-erasor">지우개</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="clear">다시그리기!</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3>6. 비밀 번호</h3>
|
||||||
|
<input id="pass" name="pass" type="text" maxlength="2" pattern="^\d{2}$" title="암호는 숫자x2개로 해주세요."
|
||||||
|
required />
|
||||||
|
<small>두 자리 숫자를 입력해주세요. 업로드한 소리를 삭제할 때 쓰입니다.</small>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input id="submit" type="submit" value="보내기!" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
↓
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>소리 목록</h1>
|
||||||
|
<p>제출해주신 소리는 아래 목록에 시간순으로 쌓입니다. 각각의 모양을 클릭하면, 모양에 해당하는 소리를 듣고 소리에 대한 정보를 확인할 수 있습니다.</p>
|
||||||
|
{{#each list}}
|
||||||
|
<div class="items" foldername="{{this.foldername}}">
|
||||||
|
<details>
|
||||||
|
<summary><img class="drawing"
|
||||||
|
src="/uploads/{{this.foldername}}/pixels.png" /></summary>
|
||||||
|
<audio class="sound" preload="none" controls>
|
||||||
|
<source src="/uploads/{{this.foldername}}/audio.mp3"
|
||||||
|
type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
<ul class="soundinfo">
|
||||||
|
<hr>
|
||||||
|
<li>유형 | <div class="group">{{this.group}}</div>
|
||||||
|
</li>
|
||||||
|
<hr>
|
||||||
|
<li>제목 | <div class="title">{{this.title}}</div>
|
||||||
|
</li>
|
||||||
|
<hr>
|
||||||
|
<li>묘사 | <div class="comment">{{this.comment}}</div>
|
||||||
|
</li>
|
||||||
|
<hr>
|
||||||
|
</ul>
|
||||||
|
<button class="preview" type="button"
|
||||||
|
onclick="javascript:window.open('/preview/{{this.foldername}}', '_blank');">미리보기</button>
|
||||||
|
<button class="delete" type="button" onclick="del(this)">삭제</button>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</section>
|
||||||
|
</div><!-- div class="notice" -->
|
||||||
|
</div><!-- div class="entry" -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// // force https
|
||||||
|
// var http_confirm = location.href.split(":")[0];
|
||||||
|
// if (http_confirm == "http") {
|
||||||
|
// window.location.replace("https://" + location.host);
|
||||||
|
// }
|
||||||
|
|
||||||
|
var socket = io(location.host);
|
||||||
|
var n = 0;
|
||||||
|
var fr = 20;
|
||||||
|
var arr = [];
|
||||||
|
var looper;
|
||||||
|
var score;
|
||||||
|
var logo;
|
||||||
|
var silence;
|
||||||
|
var clap;
|
||||||
|
|
||||||
|
//promisify -> new Tone.Player
|
||||||
|
function AudioImport(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var audio = new Tone.Player(url, () => resolve(audio));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//**ENTRY begin >>>
|
||||||
|
// -- https://stackoverflow.com/a/39577640
|
||||||
|
function dataURLtoBlob(dataURL) {
|
||||||
|
let array, binary, i, len;
|
||||||
|
binary = atob(dataURL.split(",")[1]);
|
||||||
|
array = [];
|
||||||
|
i = 0;
|
||||||
|
len = binary.length;
|
||||||
|
while (i < len) {
|
||||||
|
array.push(binary.charCodeAt(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return new Blob([new Uint8Array(array)], {
|
||||||
|
type: "image/png",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm(event) {
|
||||||
|
|
||||||
|
//TODO : first check if there is a drawing or not. (pixels)
|
||||||
|
|
||||||
|
//
|
||||||
|
var submit = document.getElementById("submit");
|
||||||
|
submit.setAttribute('disabled', 'disabled');
|
||||||
|
select("html").style('cursor', 'progress');
|
||||||
|
|
||||||
|
//
|
||||||
|
var form = document.getElementById("form");
|
||||||
|
var fd = new FormData(form);
|
||||||
|
|
||||||
|
//
|
||||||
|
var unit = 45; //mask&override 'unit' -> for crispy png.
|
||||||
|
var img2 = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||||
|
img2.clear();
|
||||||
|
img2.strokeWeight(0);
|
||||||
|
img2.fill(255);
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
if (p[c * cols + r] == 1) {
|
||||||
|
img2.rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dataurl = img2.elt.toDataURL();
|
||||||
|
var pixels = dataURLtoBlob(dataurl);
|
||||||
|
fd.append("pixels", pixels, "pixels.png");
|
||||||
|
|
||||||
|
for (var pair of fd.entries()) {
|
||||||
|
console.log(pair[0] + ', ' + pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.open("POST", "/entry");
|
||||||
|
request.onload = () => {
|
||||||
|
alert('감사합니다!');
|
||||||
|
location.reload();
|
||||||
|
//location.assign("/submit");
|
||||||
|
}
|
||||||
|
request.send(fd);
|
||||||
|
|
||||||
|
//
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = document.getElementById('form');
|
||||||
|
form.addEventListener('submit', submitForm);
|
||||||
|
|
||||||
|
//// ---- for 'list' rendering ----
|
||||||
|
|
||||||
|
function del(that) {
|
||||||
|
console.log();
|
||||||
|
let text;
|
||||||
|
let pass = prompt("패스워드를 맞춰보세요!", "숫자2개");
|
||||||
|
if (/^\d{2}$/.test(pass)) {
|
||||||
|
var target = that.parentElement.parentElement.getAttribute('foldername');
|
||||||
|
const trydelete = async () => {
|
||||||
|
const response = await fetch('/delete/' + target + '/' + pass);
|
||||||
|
if (response.ok) {
|
||||||
|
const json = await response.json();
|
||||||
|
return Promise.resolve(json);
|
||||||
|
} else {
|
||||||
|
return Promise.reject('no response.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trydelete().then((resp) => {
|
||||||
|
if (resp.result) {
|
||||||
|
alert("지우기 성공!");
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert("지우기 실패-");
|
||||||
|
}
|
||||||
|
}).catch(console.log);
|
||||||
|
} else {
|
||||||
|
alert("암호는 숫자2개...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = [];
|
||||||
|
var cols = 17;
|
||||||
|
var rows = 17;
|
||||||
|
var unit = 15; //px
|
||||||
|
var img;
|
||||||
|
var penselect = "pencil";
|
||||||
|
var scrollable = true;
|
||||||
|
// <<< end of **ENTRY
|
||||||
|
|
||||||
|
async function setup() {
|
||||||
|
|
||||||
|
//**PARADE
|
||||||
|
noCanvas();
|
||||||
|
if (windowWidth > 1500 && windowWidth > windowHeight) {
|
||||||
|
fr = 30;
|
||||||
|
} else {
|
||||||
|
fr = 20;
|
||||||
|
}
|
||||||
|
frameRate(fr);
|
||||||
|
|
||||||
|
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||||
|
//noLoop(); // <-- BUT, we want 1 for ENTRY graphics!
|
||||||
|
// --> use custom looper.
|
||||||
|
|
||||||
|
//**ENTRY
|
||||||
|
var cnv = createCanvas(cols * unit + 2, rows * unit + 2);
|
||||||
|
cnv.parent("p5");
|
||||||
|
img = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||||
|
document.querySelectorAll('.penselect').forEach(item => item.onclick = () => {
|
||||||
|
penselect = document.querySelector('input[name="penselect"]:checked').value;
|
||||||
|
});
|
||||||
|
//
|
||||||
|
for (var i = 0; i < cols * rows; i++) p.push(0);
|
||||||
|
document.querySelectorAll('.clear').forEach(item => item.onclick = () => {
|
||||||
|
for (let a = 0; a < p.length; a++) p[a] = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//**ENTRY begin >>>
|
||||||
|
function draw() {
|
||||||
|
//clear
|
||||||
|
clear();
|
||||||
|
|
||||||
|
//draw the grid
|
||||||
|
stroke(255);
|
||||||
|
strokeWeight(0.2);
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
noFill();
|
||||||
|
rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//pointer
|
||||||
|
fill(0, 255, 0);
|
||||||
|
strokeWeight(0);
|
||||||
|
|
||||||
|
//mouse way
|
||||||
|
circle(mouseX, mouseY, 10);
|
||||||
|
if (mouseIsPressed && mouseButton === LEFT) {
|
||||||
|
//find slot under the pointer
|
||||||
|
var mouseC = int(mouseX / unit);
|
||||||
|
var mouseR = int(mouseY / unit);
|
||||||
|
if (mouseC >= 0 && mouseC < cols && mouseR >= 0 && mouseR < rows) {
|
||||||
|
if (penselect == "pencil") p[mouseC * cols + mouseR] = 1;
|
||||||
|
else if (penselect == "erasor") p[mouseC * cols + mouseR] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//touch way
|
||||||
|
if (touches.length > 0) {
|
||||||
|
circle(touches[0].x, touches[0].y, 10);
|
||||||
|
//find slot under the pointer
|
||||||
|
var mouseC = int(touches[0].x / unit);
|
||||||
|
var mouseR = int(touches[0].y / unit);
|
||||||
|
if (mouseC >= 0 && mouseC < cols && mouseR >= 0 && mouseR < rows) {
|
||||||
|
// if (scrollable == true) {
|
||||||
|
// scrollable = false;
|
||||||
|
// // firefox browser @ my android phone -> url bar auto-hiding kills drawing experience.
|
||||||
|
// // --> https://stackoverflow.com/a/63221105
|
||||||
|
// document.body.style.marginTop = `-${window.pageYOffset}px`;
|
||||||
|
// document.body.style.position = 'fixed';
|
||||||
|
// document.body.style.overflowY = 'scroll';
|
||||||
|
// }
|
||||||
|
if (penselect == "pencil") p[mouseC * cols + mouseR] = 1;
|
||||||
|
else if (penselect == "erasor") p[mouseC * cols + mouseR] = 0;
|
||||||
|
} else {
|
||||||
|
// if (scrollable == false) {
|
||||||
|
// scrollable = true;
|
||||||
|
// // firefox browser @ my android phone -> url bar auto-hiding kills drawing experience.
|
||||||
|
// // --> https://stackoverflow.com/a/63221105
|
||||||
|
// document.body.style.position = '';
|
||||||
|
// document.body.style.overflowY = '';
|
||||||
|
// if (document.body.style.marginTop) {
|
||||||
|
// const scrollTop = -parseInt(document.body.style.marginTop, 10);
|
||||||
|
// document.body.style.marginTop = '';
|
||||||
|
// window.scrollTo(window.pageXOffset, scrollTop);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw img
|
||||||
|
image(img, 0, 0);
|
||||||
|
img.clear();
|
||||||
|
img.strokeWeight(0);
|
||||||
|
img.fill(255);
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
if (p[c * cols + r] == 1) {
|
||||||
|
img.rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// <<< end of **ENTRY
|
||||||
|
|
||||||
|
//
|
||||||
|
var myroom = -1;
|
||||||
|
var intro;
|
||||||
|
var ready;
|
||||||
|
//
|
||||||
|
socket.on("connect", async function () {
|
||||||
|
console.log("connected!");
|
||||||
|
|
||||||
|
//
|
||||||
|
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||||
|
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||||
|
|
||||||
|
//TESTING... fixed to room 1.
|
||||||
|
// myroom = 1;
|
||||||
|
|
||||||
|
if (myroom == -1 && selectAll(".roomsel").length == 0) {
|
||||||
|
//initial connection -> ask the room number.
|
||||||
|
|
||||||
|
var roomsel = createDiv();
|
||||||
|
selectAll(".parade")[0].child(roomsel);
|
||||||
|
roomsel.class("roomsel");
|
||||||
|
///
|
||||||
|
var b = createButton("시작하기 Start!", "1");
|
||||||
|
var d = createDiv("흐름을 향하여 걷는 Walking towards the Flow");
|
||||||
|
d.class("title");
|
||||||
|
b.mouseClicked(function () {
|
||||||
|
silence.start();
|
||||||
|
clap.start();
|
||||||
|
myroom = parseInt(this.value());
|
||||||
|
|
||||||
|
socket.emit("room", myroom, function (res) {
|
||||||
|
if (res) {
|
||||||
|
console.log("entered the room -> " + myroom);
|
||||||
|
|
||||||
|
// myroom-indicator
|
||||||
|
// var tag = createP(str(myroom));
|
||||||
|
// selectAll(".parade")[0].child(tag);
|
||||||
|
|
||||||
|
//
|
||||||
|
// setTimeout(function() {
|
||||||
|
// ready = createP("퍼레이드 시작합니다!!");
|
||||||
|
// ready.position(
|
||||||
|
// windowWidth / 2 - windowWidth / 10,
|
||||||
|
// windowHeight / 2
|
||||||
|
// );
|
||||||
|
// }, 1000);
|
||||||
|
} else {
|
||||||
|
console.log("rejected!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
selectAll(".roomsel").forEach(item => {
|
||||||
|
item.remove();
|
||||||
|
// 1 second popup '.intro' div
|
||||||
|
intro = createDiv(
|
||||||
|
"«흐름을 향하여 걷는» 소리 행렬이 곧 나타납니다. <br>오른쪽에서 왼쪽으로 지나가는 소리가 들리지 않는다면 볼륨을 확인해주시고, 스마트폰 환경에서는 진동 모드를 해제해주세요. <br>스크롤을 내리면 참여 방법을 확인하고 소리를 업로드할 수 있으며, 스크롤을 맨 위로 올리거나 오른쪽 위의 글씨를 클릭해 퍼레이드로 다시 돌아올 수 있습니다. <br><br>The sound parade «Walking Toward the Flow» will appear soon. <br>If there is no sound, check the volume and turn off the vibration mode in the smartphone environment.<br> You can see how to participate and upload sound by scrolling down, and you can return to the parade by clicking on the text in the upper right corner.<br><img class='arrow' src='arrow.svg'>"
|
||||||
|
);
|
||||||
|
intro.class("notice intro"); //-> fadeout & disapear by css animation style.
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
roomsel.child(d);
|
||||||
|
roomsel.child(b);
|
||||||
|
} else {
|
||||||
|
//re-connection -> just connect to remembered room!
|
||||||
|
socket.emit("room", myroom, function (res) {
|
||||||
|
if (res) {
|
||||||
|
console.log("entered the room -> " + myroom);
|
||||||
|
} else {
|
||||||
|
console.log("rejected!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//var fading_factor = 0.3; //30%
|
||||||
|
var fading_factor = 0.5; //50%
|
||||||
|
socket.on("post", async function (post) {
|
||||||
|
|
||||||
|
console.log(post);
|
||||||
|
var list = await new Promise((resolve, reject) => {
|
||||||
|
loadJSON("/entries", (json) => resolve(json));
|
||||||
|
})
|
||||||
|
console.log(list);
|
||||||
|
|
||||||
|
// var object = post.object;
|
||||||
|
var object = {
|
||||||
|
"id": 1,
|
||||||
|
"type": "abc",
|
||||||
|
"src": "/uploads/" + list[post] + "/pixels.png",
|
||||||
|
"audio": "/uploads/" + list[post] + "/audio.mp3",
|
||||||
|
"alt": "알트",
|
||||||
|
"size": {
|
||||||
|
"base": 40,
|
||||||
|
"random": 20
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"base": 20,
|
||||||
|
"random": 10
|
||||||
|
},
|
||||||
|
"showtime": 20000
|
||||||
|
};
|
||||||
|
console.log(object);
|
||||||
|
|
||||||
|
var img = createImg(object.src, object.alt, "", async function (im) {
|
||||||
|
//로딩이 끝나면, start!
|
||||||
|
var pv = new Tone.PanVol(0, -99).toDestination();
|
||||||
|
var snd = await AudioImport(object.audio); // NOTE: url with spaces didn't work here.
|
||||||
|
snd.connect(pv).start();
|
||||||
|
snd.loop = true;
|
||||||
|
|
||||||
|
//로딩이 끝나면, show!
|
||||||
|
im.show();
|
||||||
|
|
||||||
|
//그림의 크기와 초기 위치 ==> 가로 보기인 경우
|
||||||
|
var width = 0;
|
||||||
|
if (windowWidth > windowHeight) {
|
||||||
|
|
||||||
|
width = (windowHeight * (object.size.base * 1.4 + object.size.random * Math.random())) / 100; // 좀더 크게 + 40% (ratio)
|
||||||
|
im.size(width, AUTO);
|
||||||
|
|
||||||
|
im.position(
|
||||||
|
windowWidth * (1 + fading_factor),
|
||||||
|
(windowHeight * (object.y.base + object.y.random * Math.random()) * 0.5) / 100 // 좀더 위로 위로 - 50% (ratio)
|
||||||
|
);
|
||||||
|
|
||||||
|
//그림의 크기와 초기 위치 ==> 세로 보기인 경우
|
||||||
|
} else {
|
||||||
|
|
||||||
|
width = (windowHeight * (object.size.base + object.size.random * Math.random())) / 100; // json에서 정한 크기 그대로. (ratio)
|
||||||
|
im.size(width, AUTO);
|
||||||
|
|
||||||
|
im.position(
|
||||||
|
windowWidth * (1 + fading_factor),
|
||||||
|
(windowHeight * (object.y.base + object.y.random * Math.random())) / 100 // json에서 정한 위치 그대로. (ratio)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//추가 정보들
|
||||||
|
im.attribute("data-type", object.type);
|
||||||
|
im.attribute("data-showtime", object.showtime / 1000); //milli-sec. -> seconds.
|
||||||
|
|
||||||
|
//'아이콘' 들은 애니메이션을 시켜줘야 함...
|
||||||
|
if (object.type == "icon") {
|
||||||
|
//
|
||||||
|
im.class("rotate");
|
||||||
|
im.style("animation-duration", object.rotate + "s");
|
||||||
|
var orgs = im.style("transform-origin").split(" ");
|
||||||
|
var str = parseFloat(orgs[0]) + object.pivot.x + "px";
|
||||||
|
str = str + " " + parseFloat(orgs[1]) + object.pivot.y + "px";
|
||||||
|
im.style("transform-origin", str);
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
//로딩이 다 되면, rendering array에 추가.
|
||||||
|
var bundle = {
|
||||||
|
ref: object,
|
||||||
|
img: im,
|
||||||
|
sound: snd,
|
||||||
|
panvol: pv,
|
||||||
|
width: width
|
||||||
|
};
|
||||||
|
arr.push(bundle);
|
||||||
|
});
|
||||||
|
|
||||||
|
//첨에는 hide
|
||||||
|
img.hide();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||||
|
// --> custom looper is ok.
|
||||||
|
var looper;
|
||||||
|
(looper = function (timeout) {
|
||||||
|
setTimeout(async function () {
|
||||||
|
|
||||||
|
//
|
||||||
|
for (var i = arr.length - 1; i >= 0; i -= 1) {
|
||||||
|
var bundle = arr[i];
|
||||||
|
var img = bundle.img;
|
||||||
|
var showtime = parseFloat(img.attribute("data-showtime"));
|
||||||
|
var type = img.attribute("data-type");
|
||||||
|
var x = img.position().x;
|
||||||
|
var y = img.position().y;
|
||||||
|
y = y + random(-1, 1);
|
||||||
|
x = x - windowWidth / (fr * showtime);
|
||||||
|
|
||||||
|
//
|
||||||
|
if (type == "icon") {
|
||||||
|
img.style("z-index", "-1");
|
||||||
|
}
|
||||||
|
3;
|
||||||
|
|
||||||
|
img.position(x, y);
|
||||||
|
var pan = (x / windowWidth) * 2 - 1;
|
||||||
|
|
||||||
|
//panning
|
||||||
|
var snd = bundle.sound;
|
||||||
|
var pv = bundle.panvol;
|
||||||
|
if (x >= -bundle.width && x < windowWidth) {
|
||||||
|
pan = ((x + bundle.width) / (windowWidth + bundle.width)) * 2 - 1;
|
||||||
|
pv.pan.value = pan;
|
||||||
|
pv.volume.value = 0;//(dB)
|
||||||
|
} else {
|
||||||
|
var range;
|
||||||
|
var knob;
|
||||||
|
if (x >= windowWidth) {
|
||||||
|
range = windowWidth * fading_factor
|
||||||
|
knob = x - windowWidth;
|
||||||
|
pv.pan.value = 1;
|
||||||
|
pv.volume.value = knob / range * -20;
|
||||||
|
} else if (x < -bundle.width) {
|
||||||
|
range = windowWidth * fading_factor
|
||||||
|
knob = (x + bundle.width) * -1;
|
||||||
|
pv.pan.value = -1;
|
||||||
|
pv.volume.value = knob / range * -20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove with sound fade-out
|
||||||
|
var exit_x = -bundle.width - windowWidth * fading_factor;
|
||||||
|
if (x < exit_x) {
|
||||||
|
img.remove();
|
||||||
|
snd.stop();
|
||||||
|
delete snd;
|
||||||
|
delete pv;
|
||||||
|
arr.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
looper(1000 / fr);
|
||||||
|
}, timeout);
|
||||||
|
})(1000 / fr);
|
||||||
|
|
||||||
|
// function randomvoiceplay() {
|
||||||
|
// (looper = function(timeout) {
|
||||||
|
// setTimeout(function() {
|
||||||
|
// voice[int(random(19))].play();
|
||||||
|
// looper(random(8000, 12000));
|
||||||
|
// }, timeout);
|
||||||
|
// })(8000);
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -213,8 +213,8 @@
|
||||||
var object = {
|
var object = {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"type": "abc",
|
"type": "abc",
|
||||||
"src": "https://p.dianaband.info/public/sound-parade/" + list[post] + "/pixels.png",
|
"src": "/uploads/" + list[post] + "/pixels.png",
|
||||||
"audio": "https://p.dianaband.info/public/sound-parade/" + list[post] + "/audio.mp3",
|
"audio": "/uploads/" + list[post] + "/audio.mp3",
|
||||||
"alt": "알트",
|
"alt": "알트",
|
||||||
"size": {
|
"size": {
|
||||||
"base": 40,
|
"base": 40,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue