In acest tutorial, vom discuta despre cum sa realizezi o aplicatie web pentru creat beat-uri, pe care o vom numi Beat Maker. Aceasta aplicatie va permite sa creati si sa redati beat-uri. Nu ca m-as pricepe in vre-un fel la muzica, dar proiectul pare interesant din perspesctiva unui web developer si, cine stie, poate este utila cuiva si este un punct de pornire pentru a realiza ceva mai complex.
Pentru cei care nu au rabdare sa treaca prin tot tutorialul fisierele pot fi descarcate de aici. Iar daca vreti sa intelegeti ce se petrece in cod, puteti citi in continuare. 🙂
In primul rand sa stabilim ce vrem sa facem in acest proiect.
- Vom avea 5 instrumente din care putem alege pentru a construi beat-ul.
- Vom avea un tempo prestabilit de 120 BPM, dar care poate fi ajustat de utilizator.
- Vom avea 8 timpi si vom putea seta fiecare instrument in acest interval.
- Apasand butonul Play vom avea un loop (bucla) pentru a asculta beat-ul creat.
- Va trebui sa putem salva fiecare beat intr-o baza de date MySQL si sa-i dam un nume.
- Va trebui sa avem afisata o biblioteca ce va contine beat-urile salvate.
- Va trebui sa putem alege un beat din biblioteca pentru a-l asculta / modifica.
Vom avea nevoie si de niste sunete pentru proiectul nostru. Pe acestea le-am descarcat de aici. Cele pe care le-am descarcat sunt fisiere wav, dar puteti descarca si mp3 daca doriti, evident schimband extensia in functia de redare.
Aceasta aplicatie web este creata utilizând PHP, MySQL, jQuery si CSS. In continuare vom prezenta fisierele si continutul acestora, cu accent pe functia JavaScript, pentru ca acolo se va intampla toata magia.
Structura
In primul rand va trebui sa adaugam o baza de date in MySQL (folosind PHP MyAdmin). In exemplul nostru am numit-o beats
.
Acum putem rula o comanda SQL pentru a crea tabelul necesar:
1 2 3 4 5 |
CREATE TABLE `beats` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `data` text NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; |
Aici vom salva beat-urile create cu un nume si datele necesare.
Vom crea un folder numit sounds
in care vom avea cele 5 fisiere audio: clap.wav
, hihat.wav
, kick.wav
, percussion.wav
si snare.wav
. Cele 5 fisiere vor genera sunetele de care avem nevoie pentru beat. Puteti sa le inlocuiti cu orice alte fisiere audio care se preteaza mai bine pentru proiectul vostru, sau adauga altele dupa caz.
Fisiere:
config.php:
– Acest fisier contine informatiile de conexiune la baza de date. Va trebui sa înlocuiti informatiile de conexiune cu cele proprii. Deoarece vom avea nevoie de conexiune la baza de date in mai multe fisiere si pentru a nu repeta acelasi cod in mai multe parti, dar si pentru a modifica datele conexiunii intr-un singur loc, am creat acest fisier care va fi inclus ori de cate ori va fi nevoie in alte sectiuni ale aplicatiei.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php // Replace with your own database connection information $db_host = ‘localhost’; $db_user = ‘root’; $db_pass = ”; $db_name = ‘beats’; $conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name); if (!$conn) { die(‘Connection failed: ‘ . mysqli_connect_error()); } global $conn; ?> |
fetch_beats.php:
– Acest fisier obtine toate beat-urile din baza de date si le afiseaza în format JSON. Dupa cum vedeti, acest fisier include fisierul config.php:
pentru a realiza conexiunea la baza de date.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php // Connect to database include ‘config.php’; $query = „SELECT id, name FROM beats ORDER BY id DESC”; $result = mysqli_query($conn, $query); $beats = []; while ($row = mysqli_fetch_assoc($result)) { $beats[] = $row; } header(‘Content-Type: application/json’); echo json_encode($beats); $conn->close(); ?> |
get_beat.php:
– Acest fisier obtine un beat specific din baza de date în functie de ID-ul sau si îl afiseaza în format JSON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?php include ‘config.php’; if (isset($_GET[‘id’])) { $id = $_GET[‘id’]; $stmt = $conn->prepare(„SELECT data FROM beats WHERE id = ?”); $stmt->bind_param(„i”, $id); if ($stmt->execute()) { $result = $stmt->get_result(); $row = $result->fetch_assoc(); $beatData = $row[‘data’]; header(‘Content-Type: application/json’); echo $beatData; } else { header(‘Content-Type: application/json’); echo json_encode([‘status’ => ‘error’, ‘message’ => ‘Error fetching beat’]); } $stmt->close(); } else { header(‘Content-Type: application/json’); echo json_encode([‘status’ => ‘error’, ‘message’ => ‘Invalid request’]); } $conn->close(); ?> |
save_beat.php:
– Acest fisier salveaza beat-ul creat în baza de date.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php // Connect to database include ‘config.php’; if ($_SERVER[‘REQUEST_METHOD’] === ‘POST’ && isset($_POST[‘name’]) && isset($_POST[‘data’])) { $name = $_POST[‘name’]; $data = $_POST[‘data’]; $stmt = $conn->prepare(„INSERT INTO beats (name, data) VALUES (?, ?)”); $stmt->bind_param(„ss”, $name, $data); if ($stmt->execute()) { echo json_encode([‘status’ => ‘success’, ‘message’ => ‘Beat saved successfully’]); } else { echo json_encode([‘status’ => ‘error’, ‘message’ => ‘Error saving beat’]); } $stmt->close(); } else { echo json_encode([‘status’ => ‘error’, ‘message’ => ‘Invalid request’]); } $conn->close(); ?> |
Nimic spectaculos pana aici, doar niste operatii standard cu baze de date MySQL. Rog tineti cont ca in acest tutorial nu s-a pus accent pe securitate, deci daca doriti sa utilizati acest cod in viata reala, va trebui sa tineti cont si de acest aspect.
index.php:
– Aceasta este pagina principala a aplicatiei. Include HTML si CSS necesare si, de asemenea, include fisierul JavaScript pentru a face aplicatia interactiva.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<!DOCTYPE html> <html lang=„en”> <head> <meta charset=„UTF-8”> <meta name=„viewport” content=„width=device-width, initial-scale=1.0”> <title>Beat Maker</title> <link rel=„stylesheet” href=„style.css”> </head> <body> <div class=„container”> <h1>Beat Maker</h1> <p>A project by <a href=„https://www.ahoi.ro/”>AHOI</a></p> <div class=„beat-editor”> <h2>Beat Editor</h2> <div class=„controls”> <label for=„tempo”>Tempo:</label> <input type=„number” id=„tempo” value=„120” min=„60” max=„240” step=„1”> <button id=„save-beat”>Save Beat</button> </div> <div class=„grid”> <!– Rows of buttons will be generated here –> </div> <div class=„controls-bottom”> <button id=„play”>Play</button> <button id=„stop”>Stop</button> </div> </div> <div class=„library”> <h2>Library</h2> <div class=„library-list”> <!– Library items will be generated here –> </div> </div> </div> <script src=„https://code.jquery.com/jquery-3.6.0.min.js”></script> <script src=„script.js”></script> </body> </html> |
style.css:
– Acest fisier contine toate stilurile CSS necesare pentru a face aplicatia sa arate bine.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
body { font–family: Arial, sans–serif; background–color: #222; color: #eee; margin: 0; padding: 0; } a, a:hover { color: #fff; } .container { max–width: 500px; margin: 0 auto; padding: 20px; } .container h1 { text–align: center; margin–bottom: 0; color: #fff; line–height: 1; } .container p { text–align: center; margin: 0 0 30px 0; line–height: 1; font–size: 14px; } h2 { margin–bottom: 20px; color: #ddd; } .beat–editor, .library { background–color: #333; padding: 20px; border–radius: 5px; box–shadow: 0 2px 4px rgba(0, 0, 0, 0.5); } .beat–editor { margin–bottom: 40px; } .library { margin–bottom: 40px; } .controls { margin–bottom: 20px; } .controls–bottom { margin–top: 20px; } .controls label { color: #ddd; margin–right: 5px; } input[type=„number”] { background–color: #444; color: #eee; border: 1px solid #555; padding: 4px 5px; margin–right: 10px; } button { background–color: #555; color: #eee; border: 1px solid #666; padding: 5px 10px; cursor: pointer; margin–right: 5px; } button:hover { background–color: #666; } button:active { background–color: #777; } #play, #stop, #pause { padding: 10px 30px; font–size: 16px; } #save-beat { background–color: #333; border–color: #444; } .track { margin–bottom: 10px; width: 100%; } .track–label { display: block; width: 100%; clear: left; } .step { background–color: #444; border: 1px solid #555; height: 35px; cursor: pointer; width: 11%; } .step.playing { background–color: rgba(0, 255, 0, 0.3); } .step.active, .step.playing.active { background–color: #ee6e73; border–color: #dd5b66; } .library–item { margin–bottom: 10px; } .library–item button { width: 100%; } |
script.js:
– Acest fisier contine toate functiile JavaScript necesare pentru a face aplicatia interactiva. Aceasta include functia care reda sunetul atunci când se face clic pe un buton, functia care creeaza grila pentru a crea beat-uri, etc.
Functia JavaScript principala din acest fisier este functia createGrid()
. Aceasta functie creeaza o grila de butoane care poate fi utilizata pentru a crea beat-uri. Butoanele pot fi selectate pentru a fi activate si dezactivate si pot fi redata sunetul atunci când se face clic pe ele.
Ne folosim de constantele:
– numberOfTracks = 5, adica utilizam cele 5 instrumente. Aici puteti modifica daca adaugati mai multe.
– numberOfSteps = 8, adica timpi utilizati. Din ce am inteles, 8 este un standard, dar puteti utiliza si alte valori.
– const samples, aici setam calea catre fisierele de sunet. Daca mai adaugati instrumente va trebui sa le setati calea aici.
Functia createGrid()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const numberOfTracks = 5; // Number of tracks (rows) const numberOfSteps = 8; // Number of steps (columns) const samples = [ { name: ‘Kick’, path: ‘sounds/kick.wav’ }, { name: ‘Snare’, path: ‘sounds/snare.wav’ }, { name: ‘Hi-hat’, path: ‘sounds/hihat.wav’ }, { name: ‘Clap’, path: ‘sounds/clap.wav’ }, { name: ‘Percussion’, path: ‘sounds/percussion.wav’ } ]; function createGrid() { const grid = $(‘.grid’); for (let track = 0; track < numberOfTracks; track++) { const trackDiv = $(‘<div>’).addClass(‘track’); const trackLabel = $(‘<div>’).addClass(‘track-label’).text(samples[track].name); trackDiv.append(trackLabel); grid.append(trackDiv); for (let step = 0; step < numberOfSteps; step++) { const stepButton = $(‘<button>’) .addClass(‘step’) .attr(‘data-track’, track) .attr(‘data-step’, step) .on(‘click’, function() { $(this).toggleClass(‘active’); playSample(track); }); trackDiv.append(stepButton); } } } |
Aceasta este functia care creeaza butoanele si le adauga la grila.
Apelam metoda getJSON() pentru a prelua continutul fisierului fetch_beats.php
, care contine o lista cu toate beat-urile salvate in baza de date si afisam aceste beat-uri in sectiunea biblioteca.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function createLibrary() { const libraryList = $(‘.library-list’); $.getJSON(‘fetch_beats.php’, function(beats) { beats.forEach(beat => { const listItem = $(‘<div>’).addClass(‘library-item’); const button = $(‘<button>’) .text(beat.name) .attr(„data-id”, beat.id) .attr(„data-name”, beat.name) // Add data-name attribute .on(‘click’, function() { loadBeat(beat.id); }); listItem.append(button); libraryList.append(listItem); }); }); } |
In continuare, am creat functia loadBeat(id)
care incarca un beat salvat in baza de date si il afiseaza in editor. Mai intai, se face un request GET la fisierul get_beat.php
si se transmite id-ul beatului care trebuie incarcat. Acest fisier returneaza un JSON care contine datele beatului.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function loadBeat(id) { $.getJSON(`get_beat.php?id=${id}`, function(beatData) { // Clear the grid $(‘.step’).removeClass(‘active’); // Update the grid with the beat data for (let track = 0; track < numberOfTracks; track++) { for (let step = 0; step < numberOfSteps; step++) { if (beatData[track][step] === 1) { $(`.step[data–track=„${track}”][data–step=„${step}”]`).addClass(‘active’); } } } currentBeatData = beatData; }); } |
De asemenea, am adaugat si functia saveBeat()
care salveaza beat-ul curent in baza de date prin intermediul fisierului save_beat.php
. Aceasta functie extrage datele din grila si le salveaza in baza de date sub forma unui JSON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function saveBeat() { const beatName = prompt(‘Enter a name for your beat:’); if (!beatName) { return; } const beatData = []; for (let track = 0; track < numberOfTracks; track++) { const trackData = []; for (let step = 0; step < numberOfSteps; step++) { const isActive = $(`.step[data–track=„${track}”][data–step=„${step}”]`).hasClass(‘active’); trackData.push(isActive ? 1 : 0); } beatData.push(trackData); } $.post(‘save_beat.php’, { name: beatName, data: JSON.stringify(beatData) }, function(response) { if (response.status === ‘success’) { alert(response.message); createLibrary(); // Refresh the library after saving } else { alert(response.message); } }, ‘json’); } |
Functia playSequence()
realizeaza un loop (bucla) si reda sunetele asa cum au fost setate. Pentru a sti ce timp este activ, adauga clasa „playing” care este stilizata in fisierul CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function playSequence() { if (currentStep >= numberOfSteps) { currentStep = 0; } $(‘.step’).removeClass(‘playing’); $(‘.grid .track’).each(function(track) { const stepButton = $(this).find(‘.step’).eq(currentStep); // Add „playing” class to the current step stepButton.addClass(‘playing’); if (stepButton.hasClass(‘active’)) { playSample(track); } }); currentStep++; } |
Acum ca avem functiile create, nu trebuie decat sa le facem active. Cand actionam butonul Play sau Stop vom invoca functiile aferente utilizand codul de mai jos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$(‘#play’).on(‘click’, function() { const bpm = parseInt($(‘#tempo’).val(), 10); const interval = (60 / bpm) * 1000 / 2; // Convert BPM to milliseconds (divided by 2 for 8th notes) if (!intervalId) { intervalId = setInterval(playSequence, interval); $(this).text(‘Pause’); } else { clearInterval(intervalId); intervalId = null; $(this).text(‘Play’); } }); $(‘#stop’).on(‘click’, function() { if (intervalId) { clearInterval(intervalId); intervalId = null; currentStep = 0; $(‘#play’).text(‘Play’); } }); |
De asemenea la salvarea unui beat vom invoca functia ultilizand codul:
1 2 3 |
$(‘#save-beat’).on(‘click’, function() { saveBeat(); }); |
Pentru a incarca un beat din cele salvate, functia loadBeat(id)
este invocata prin felul in care biblioteca de beat-uri salvate a fost realizata in functia createLibrary()
.
Pentru cei care au ajuns pana aici, felicitari! Fisierele pot fi descarcate de aici
Puteti utiliza fisierele in ce fel doriti, inclusiv in scopuri comerciale. Rog tineti cont ca acesta nu este un produs ci doar un proiect de weekend pentru un tutorial.
Puteti lasa pareri in sectiunea de comentarii.
Ahoi!
Lasă un răspuns