웹사이트 바로가기
이 웹사이트의 기능은 다음과 같습니다.
- 스케일 목록을 데이터베이스에서 읽어서 테이블 형태로 나열 (PHP)
- 각 목록을 클릭하면 스케일의 정보와 악보, 소리듣기 기능을 제공 (abcjs 이용)
- 스케일의 정보는 이름, 별칭(Alias), 설명(Description), 중요도(Priority), 패턴이 있음
- 악보 및 사운드 생성은 스케일의 Pattern 정보를 이용하여 생성 (PHP 음악 스케일 구하기)
- 조옮김(Transpose), 이명동음 표시기능 제공
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ko"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Musical Scales</title> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> | |
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script> | |
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> | |
<link href="./res/abcjs-audio.css" media="all" rel="stylesheet" type="text/css" /> | |
<script src="./res/abcjs_basic_5.9.1-min.js" type="text/javascript"></script> | |
<style> | |
</style> | |
</head> | |
<body class="container"> | |
<?php | |
include "init-db.php"; | |
// if($mysqli){echo "MySQL 접속 성공";} | |
// else{echo "MySQL 접속 실패";} | |
$sql = 'SELECT * FROM music_scale order by priority desc, name'; | |
$res = $mysqli -> query($sql); | |
?> | |
<header> | |
<h2>Musical Scales</h2> | |
</header> | |
<nav> | |
</nav> | |
<section> | |
<table id="freq-table" class="table table-hover"> | |
<thead> | |
<tr class="table-info"> | |
<th>Name</th> | |
<th>Alias</th> | |
<th>Priority</th> | |
</tr> | |
</thead> | |
<tbody id="freq-tbody"> | |
<?php while($row = mysqli_fetch_array($res)) { ?> | |
<tr class="each-scale" data-description="<?=$row['description']?>" data-pattern="<?=$row['pattern']?>" data-priority="<?=$row['priority']?>"> | |
<td class="scale-title"> | |
<?php | |
$flt0 = str_replace("Double-Flat ", "𝄫", $row['name']); | |
$flt1 = str_replace("Flat ", "♭" , $flt0); | |
$flt2 = str_replace("Sharp ", "♯", $flt1); | |
echo str_replace("Nat. ", "♮", $flt2); | |
?> | |
</td> | |
<td class="scale-alias"> | |
<?=$row['alias']?> | |
</td> | |
<td class="area-priority"> | |
<span class="badge badge-warning"> | |
<?php | |
for($i = 1; $i <= $row['priority']; $i++ ){ | |
echo "★"; | |
} | |
?> | |
</span> | |
</td> | |
</tr> | |
<?php }?> | |
</tbody> | |
</table> | |
</section> | |
<article> | |
<div class="modal fade"> | |
<div class="modal-dialog modal-xl" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title">Modal title</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
<table> | |
<tbody> | |
<tr> | |
<th>Name</th> | |
<td id="mod-cont-name"></td> | |
</tr> | |
<tr> | |
<th>Alias</th> | |
<td id="mod-cont-alias"></td> | |
</tr> | |
<tr> | |
<th>Pattern</th> | |
<td id="mod-cont-pattern"></td> | |
</tr> | |
<tr> | |
<th>Priority</th> | |
<td><span id="mod-cont-prior" class="badge badge-warning"></span></td> | |
</tr> | |
<tr> | |
<th colspan="2">Description</th> | |
</tr> | |
<tr> | |
<td id="mod-cont-desc" colspan="2"></td> | |
</tr> | |
</tbody> | |
</table> | |
<hr> | |
<div> | |
<label>Transpose</label> | |
<select id=transpose> | |
<option value="0">C</option> | |
<option value="1">C♯(D♭)</option> | |
<option value="2">D</option> | |
<option value="3">E♭(D♯)</option> | |
<option value="4">E</option> | |
<option value="5">F</option> | |
<option value="6">F♯(G♭)</option> | |
<option value="7">G</option> | |
<option value="8">A♭(G♯)</option> | |
<option value="9">A</option> | |
<option value="10">B♭(A♯)</option> | |
<option value="11">B</option> | |
</select> | |
</div> | |
<div> | |
<label>Display Enharmonic Notes</label> | |
<select id="change-enharmonic"> | |
<option value="common">Standard</option> | |
<option value="sharp">Sharp(♯) only</option> | |
<option value="flat">Flat(♭) only</option> | |
</select> | |
</div> | |
<hr> | |
<div id="notation"></div> | |
<div id="audio"></div> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</article> | |
<footer> | |
Copyright by <a href="http://yoonbumtae.com" target="_blank">BGSMM</a> | |
</footer> | |
<script> | |
const NAME_COMMON = ["", "C", "C+", "D", "E-", "E", "F", "F+", "G", "A-", "A", "B-", "B"]; | |
const NAME_FLAT = ["", "C", "D-", "D", "E-", "E", "F", "G-", "G", "A-", "A", "B-", "B"]; | |
const NAME_SHARP = ["", "C", "C+", "D", "D+", "E", "F", "F+", "G", "G+", "A", "A+", "B"]; | |
let glb_currentName = NAME_COMMON | |
function getScale(param) { | |
const pattern = param.pattern | |
const transpose = param.transpose | |
const patternArr = pattern.split('') | |
let sum = 1; | |
const output = { | |
scaleByName: [], | |
scaleBySemitone: [] | |
} | |
const input_ = (semitone) => { | |
if (semitone > 12) { | |
output.scaleByName.push(glb_currentName[semitone – 12]) | |
} else { | |
output.scaleByName.push(glb_currentName[semitone]) | |
} | |
output.scaleBySemitone.push(semitone) | |
} | |
input_(parseInt(transpose) + sum) | |
for (let o of patternArr) { | |
sum += parseInt(o) | |
const dispNum = parseInt(transpose) + sum | |
input_(dispNum) | |
} | |
//console.log(output) | |
return output | |
} | |
// C2 ^C2 _E2 =E2 ^F2 G2 A2 _B2 c2| | |
// ^: sharp | |
// =: natural | |
// _: flat | |
function translateToAbcjs(scaleBySemitone) { | |
//console.log(scaleBySemitone) | |
const outArr = [] | |
const changedNotes = [] | |
for (let each of scaleBySemitone) { | |
//onsole.log(each) | |
const nameIndex = each > 12 ? each – 12 : each | |
const octave = each > 12 ? 1 : 0 | |
//console.log("octave", octave) | |
const note = (_ => { | |
if (octave == 0) { | |
return glb_currentName[nameIndex].substr(0, 1) | |
} else if (octave == 1) { | |
return glb_currentName[nameIndex].substr(0, 1).toLowerCase() | |
} | |
})() | |
const postfix = glb_currentName[nameIndex].substr(glb_currentName[nameIndex].length – 1, 1) | |
//console.log(note, postfix) | |
if (note.toUpperCase() != postfix) { | |
const prefix = postfix == "+" ? "^" : "_" | |
changedNotes.push(note) | |
outArr.push(prefix + note + "2") | |
} else { | |
if (changedNotes.indexOf(note) != -1) { | |
outArr.push("=" + note + "2") | |
} else { | |
outArr.push(note + "2") | |
} | |
} | |
} | |
//console.log(outArr) | |
return outArr.join(" ") | |
} | |
function displayScore(scaleObj, title, key) { | |
const scaleStr = `Scale by Name: ${scaleObj.scaleByName}\nScale by Semitone: ${scaleObj.scaleBySemitone}` | |
console.log(scaleStr) | |
const translatedScale = translateToAbcjs(scaleObj.scaleBySemitone) | |
key = key || "C" | |
var cooleys = ` | |
X: 1 | |
T: | |
I: speed 50 | |
M: | |
L: 1/8 | |
R: ${key.trim()} ${title.trim()} | |
K: C | |
${translatedScale}|`; | |
//alert(cooleys) | |
var visualObj = ABCJS.renderAbc('notation', cooleys, { | |
responsive: "resize", | |
staffwidth: 500, | |
paddingright: 5, | |
paddingleft: 5, | |
})[0]; | |
var synthControl = new ABCJS.synth.SynthController(); | |
synthControl.load("#audio", null, { | |
displayRestart: true, | |
displayPlay: true, | |
displayProgress: true | |
}); | |
synthControl.setTune(visualObj, false); | |
} | |
$(".each-scale").on("click", e => { | |
const eachScale = $(e.target).closest(".each-scale") | |
const title = eachScale.find(".scale-title").text() | |
const pattern = eachScale.data("pattern") + "" | |
const alias = eachScale.data("alias") | |
const desc = eachScale.data("description") | |
const prior = eachScale.data("priority") | |
const priorStar = (_ => { | |
let str = "" | |
for (let i = 1; i <= parseInt(prior); i++) { | |
str += "★" | |
} | |
return str | |
})(); | |
console.log(pattern) | |
const scaleObj = getScale({ | |
pattern, | |
transpose: $("#transpose").val() | |
}) | |
displayScore(scaleObj, title, $("#transpose option:selected").text()) | |
$(".modal-title").text(title) | |
$("#mod-cont-name").text(title) | |
$("#mod-cont-alias").text(alias) | |
$("#mod-cont-prior").text(priorStar) | |
$("#mod-cont-desc").text(desc) | |
$("#mod-cont-pattern").text(pattern) | |
$(".modal").modal("show") | |
}) | |
$("#transpose").on("change", e => { | |
const scaleObj = getScale({ | |
pattern: $("#mod-cont-pattern").text(), | |
transpose: $(e.target).val() | |
}) | |
displayScore(scaleObj, $("#mod-cont-name").text(), $("#transpose option:selected").text()) | |
}) | |
$("#change-enharmonic").on("change", e => { | |
const value = $(e.target).val() | |
switch (value) { | |
case "common": | |
glb_currentName = NAME_COMMON; | |
break; | |
case "sharp": | |
glb_currentName = NAME_SHARP; | |
break; | |
case "flat": | |
glb_currentName = NAME_FLAT; | |
break; | |
} | |
const scaleObj = getScale({ | |
pattern: $("#mod-cont-pattern").text(), | |
transpose: $("#transpose").val() | |
}) | |
displayScore(scaleObj, $("#mod-cont-name").text(), $("#transpose option:selected").text()) | |
}) | |
</script> | |
</body> | |
</html> |
0개의 댓글