DALL·E 3 দ্বারা অ্যালবামের জন্য কভার
এই নিবন্ধে, আমি শেয়ার করব কিভাবে আমি আমার অ্যালবাম ( ) প্রকাশ করার জন্য একটি সপ্তাহান্তে একটি প্রকল্প করেছি। আমি একজন প্রশিক্ষিত সঙ্গীতজ্ঞ বা সুরকার নই, কিন্তু মাঝে মাঝে আমার মনে সুর উঠে। আমি সেগুলো লিখে রাখি, এবং তারপর কম্পিউটারকে সেগুলি খেলতে দিন।
2021 সালে, আমি "সবাই খুশি, সবাই হাসছে" শিরোনামে আমার অ্যালবাম লঞ্চ করেছি। এটি একটি অপরিচিত "সুরকার" এর একটি সাধারণ অ্যালবাম - এটি আমি।
আমি শুধু সঙ্গীতের মধ্যে নই; আমিও একজন বিকাশকারী, প্রধানত সম্প্রতি ফ্রন্টএন্ড কাজের উপর ফোকাস করছি। আমি ভাবলাম, কেন এই দুই ভালোবাসা একত্রিত হয় না? তাই, আমি আমার অ্যালবামকে দৃশ্যমানভাবে উপস্থাপন করার জন্য একটি ওয়েবসাইট ডিজাইন করার জন্য সেট করেছি।
এই নিবন্ধটি প্রতিটি প্রযুক্তিগত বিশদে গভীরভাবে ডুব দেবে না - এটি খুব দীর্ঘ হবে এবং সবার কাছে আবেদন নাও করতে পারে। পরিবর্তে, আমি মূল ধারণাগুলি এবং আমার সম্মুখীন হওয়া বাধাগুলি হাইলাইট করব। যারা আগ্রহী তাদের জন্য, সমস্ত কোড এ পাওয়া যাবে।
আমার অ্যালবামটি পিয়ানোর জন্য তৈরি করা হয়েছে, সিদ্ধান্তটি সহজবোধ্য করে। কল্পনা করুন আয়তক্ষেত্রগুলি পিয়ানো কীগুলির উপর নেমে আসছে। একটি সঙ্গীত প্রবণতা আছে যে কেউ সম্ভবত YouTube এ এই পদ্ধতিতে নোট চিত্রিত অসংখ্য ভিডিও সম্মুখীন হয়েছে. একটি আয়তক্ষেত্র একটি কী স্পর্শ করে, এটিকে আলোকিত করে, নোটটিকে আঘাত করার সুনির্দিষ্ট মুহূর্ত নির্দেশ করে।
আমি এই চাক্ষুষ শৈলীর উত্স সম্পর্কে অনিশ্চিত, কিন্তু একটি দ্রুত Google অনুসন্ধান প্রধানত সিন্থেসিয়ার স্ক্রিনশট দেয়৷
ইউটিউবে, এমন নির্মাতারা আছেন যারা দৃশ্যত অত্যাশ্চর্য প্রভাব তৈরি করতে পরিচালনা করেন। নান্দনিক এবং সঙ্গীত উভয় দৃষ্টিকোণ থেকে এই ধরনের ভিডিও দেখা একটি ট্রিট। বা দেখুন.
আমরা বাস্তবায়ন করতে হবে কি?
আসুন প্রতিটি পয়েন্ট মোকাবেলা করা যাক এবং এই সমস্তগুলিকে কর্মের মধ্যে রাখি।
প্রাথমিকভাবে, আমি ধরে নিয়েছিলাম যে কীগুলি বাস্তবায়ন করা সবচেয়ে বড় চ্যালেঞ্জ হবে। যাইহোক, একটি দ্রুত অনলাইন অনুসন্ধান উদাহরণের আধিক্য প্রকাশ করে এবং কীভাবে এটি করতে হয় তার নির্দেশিকা। কমনীয়তার স্পর্শ সহ একটি ডিজাইনের লক্ষ্যে, আমি দ্বারা তৈরি একটি বেছে নিয়েছি।
আমার জন্য যা বাকি ছিল তা হল কীগুলিকে বেশ কয়েকবার প্রতিলিপি করা এবং নোটগুলি জুড়ে যাওয়ার জন্য একটি গ্রিড স্থাপন করা। আমি ফ্রন্টএন্ড ফ্রেমওয়ার্ক হিসাবে Vue.js নিযুক্ত করেছি এবং নীচে কম্পোনেন্ট কোড রয়েছে।
<template> <ul style="transform: translate3d(0, 0, 0)"> <li :id="`key_${OFFSET - 1}`" style="display: none"></li> <template v-for="key in keys" :key="key.number"> <li :class="`${key.color} ${key.name}`" :id="`key_${key.number}`"></li> </template> </ul> </template> <script setup lang="ts"> import { ref } from 'vue' import type { Key } from '@/components/types' const OFFSET = 24 const template = [ { color: 'white', name: 'c' // Do }, { color: 'black', name: 'cs' // Do-diez }, { color: 'white', name: 'd' // Re }, /* ... */ ] const keys = ref<Key[]>([]) for (let i = 0; i < 72; i++) { keys.value.push({ ...template[i % 12], number: i + OFFSET }) } </script>
আমি উল্লেখ করতে চাই যে আমি প্রতিটি কীতে একটি id
অ্যাট্রিবিউট যুক্ত করেছি, যা তাদের অ্যানিমেশন শুরু করার সময় অপরিহার্য হবে।
যদিও এটিকে সবচেয়ে সহজ সেগমেন্ট বলে মনে হতে পারে, তবে কয়েকটি চ্যালেঞ্জ সরল দৃষ্টিতে লুকিয়ে আছে।
অবরোহী নোটের প্রভাব কিভাবে সম্পন্ন করা যেতে পারে?
বর্তমান নোট পুনরুদ্ধার করার জন্য জিজ্ঞাসা করা যেতে পারে এমন একটি কাঠামো বজায় রাখার প্রয়োজন আছে কি?
এই ধরনের প্রশ্নের ফলাফল রেন্ডার করার সেরা পদ্ধতি কি?
প্রতিটি প্রশ্ন নির্বিঘ্নে পছন্দসই প্রভাব অর্জন করতে নেভিগেট করার জন্য একটি বাধা উপস্থাপন করে।
আমি প্রতিটি প্রশ্নে দীর্ঘস্থায়ী হব না তবে সরাসরি তাড়া করতে চাই। গতিশীল পদ্ধতির সাথে যুক্ত অগণিত চ্যালেঞ্জের পরিপ্রেক্ষিতে, ওকামের রেজারে মনোযোগ দেওয়া এবং একটি স্থির সমাধান বেছে নেওয়া বুদ্ধিমানের কাজ।
আমি এটিকে কীভাবে সম্বোধন করেছি তা এখানে: আমি একটি একক বিস্তৃত ক্যানভাসে একই সাথে সমস্ত 6215 নোট রেন্ডার করেছি৷ এই ক্যানভাসটি overflow: hidden
সম্পত্তি সহ স্টাইল করা একটি পাত্রের মধ্যে রাখা হয়েছে। পতনশীল নোটের প্রভাব অর্জন করা কেবলমাত্র এই ধারকটির scrollTop
অ্যানিমেট করার বিষয়।
যাইহোক, একটি দীর্ঘস্থায়ী প্রশ্ন থেকে যায়: আমি কিভাবে প্রতিটি নোটের জন্য স্থানাঙ্কগুলি পেতে পারি?
সৌভাগ্যবশত, আমার কাছে একটি MIDI ফাইল আছে যেখানে এই সমস্ত নোট সংরক্ষণ করা হয়েছে, অ্যালবামের সুরকার হওয়ার কারণে এটি একটি সুবিধা। এটি MIDI ফাইল থেকে নিষ্কাশিত ডেটা ব্যবহার করে নোট রেন্ডার করার জন্য ফুটে ওঠে।
প্রদত্ত যে MIDI ফাইলটি বাইনারি বিন্যাসে রয়েছে এবং আমার নিজের এটি পার্স করার কোন ইচ্ছা ছিল না, আমি লাইব্রেরির সাহায্য তালিকাভুক্ত করেছি।
midi-file
লাইব্রেরি MIDI ফাইলগুলি থেকে কাঁচা ডেটা বের করতে দক্ষ, কিন্তু আমার প্রয়োজনের জন্য, এটি যথেষ্ট নয়। আমি এই ডেটাটিকে আরও অ্যাক্সেসযোগ্য এবং অ্যাপ্লিকেশন-বান্ধব ফর্ম্যাটে রূপান্তর করার লক্ষ্য রাখি যাতে অ্যাপের মধ্যে নির্বিঘ্ন রেন্ডারিং সহজতর হয়।
একটি MIDI ফাইলে, এটি সাধারণ অর্থে নোট নয় যে আমি কাজ করছি, কিন্তু ঘটনা। এই ইভেন্টগুলির একটি সম্পূর্ণ অ্যারে আছে, কিন্তু আমি প্রাথমিকভাবে দুটি ধরণের উপর ফোকাস করছি: 'নোটঅন', যা একটি কী চাপলে ট্রিগার হয় এবং 'নোটঅফ', যখন কী প্রকাশ করা হয়।
'নোটঅন' এবং 'নোটঅফ' উভয় ইভেন্টই নির্দিষ্ট নোট নম্বর নির্দিষ্ট করে যা যথাক্রমে চাপা বা ছেড়ে দেওয়া হয়েছিল। সময়, প্রচলিত অর্থে, MIDI-তে অনুপস্থিত। পরিবর্তে, আমাদের "টিকস" আছে। MIDI ফাইলের শিরোনামে বিট প্রতি টিক সংখ্যা বিস্তারিত আছে।
প্রকৃতপক্ষে, বিবেচনা করার জন্য আরো আছে. একটি টেম্পো ট্র্যাকও উপস্থিত রয়েছে, যেখানে 'সেট টেম্পো' ইভেন্ট রয়েছে যা প্রক্রিয়াটির অবিচ্ছেদ্য, প্লেব্যাকের সময় টেম্পো পরিবর্তন হতে পারে বিবেচনা করে। আমার প্রাথমিক পদ্ধতিতে টেম্পোর সাথে সারিবদ্ধ করার জন্য কন্টেইনারের scrollTop
সম্পত্তির অ্যানিমেশন গতি সামঞ্জস্য করা জড়িত ছিল।
যাইহোক, আমি শীঘ্রই বুঝতে পেরেছিলাম যে এটি অত্যধিক ত্রুটি সঞ্চয়ের কারণে প্রত্যাশিত ফলাফল দেবে না। একটি 'রৈখিক' সময় প্রসারিত scrollTop
অ্যানিমেট করার জন্য আরও কার্যকর প্রমাণিত হয়েছে।
এমনকি অ্যানিমেশন দিকটি সাজানো হলেও, টেম্পোটির এখনও অন্তর্ভুক্তি প্রয়োজন। আমি নোটের আয়তক্ষেত্রগুলির দৈর্ঘ্য সামঞ্জস্য করে এটিকে সম্বোধন করেছি। সর্বোত্তম সমাধান না হলেও (গতি পরিবর্তন করা আদর্শ হবে), এই পদ্ধতিটি মসৃণ অপারেশন নিশ্চিত করেছে।
এই সমাধানটি নিখুঁত নয়, প্রধানত কারণ আমি একটি টেম্পো ইভেন্টকে একটি নোট ইভেন্টের সাথে সংযুক্ত করি যে তাদের একই বা কম সময় আছে কিনা তার উপর ভিত্তি করে। এর মানে হল যে নোটটি এখনও প্লেয়িং স্টেটে থাকা অবস্থায় যদি অন্য টেম্পো ইভেন্ট ঘটে তবে এটিকে উপেক্ষা করা হবে।
এটি সম্ভাব্যভাবে একটি ত্রুটি প্রবর্তন করতে পারে, বিশেষ করে যদি একটি নোট খুব দীর্ঘ হয় এবং এটির খেলার সময় একটি নাটকীয় গতি পরিবর্তন ঘটে। এটা একটা বাণিজ্য বন্ধ. আমি এই ছোটখাট ত্রুটিটি গ্রহণ করেছি কারণ আমি দ্রুত উন্নয়নের দিকে মনোনিবেশ করছি।
এমন উদাহরণ রয়েছে যেখানে গতি অগ্রাধিকার নেয় এবং প্রতিটি বিবরণে না জড়ানো আরও বাস্তবসম্মত।
সুতরাং, আমরা নিম্নলিখিত তথ্য দিয়ে সজ্জিত:
এই বিবরণগুলি হাতে রেখে, আমি প্রতিটি নোটের জন্য ক্যানভাসে সঠিক স্থানাঙ্কগুলি চিহ্নিত করতে পারি। কী নম্বরটি X-অক্ষ নির্ধারণ করে, যখন কী প্রেসের আরম্ভ হল Y-অক্ষ। প্রেসের দৈর্ঘ্য আয়তক্ষেত্রের উচ্চতা নির্দেশ করে।
একটি স্ট্যান্ডার্ড ডিভ উপাদান ব্যবহার করে এবং এর অবস্থান 'পরম'-এ সেট করে, আমি সফলভাবে কাঙ্ক্ষিত প্রভাব অর্জন করেছি।
আমি পিয়ানোর জন্য একটি সিন্থেসাইজার তৈরি করতে চাইনি, কারণ এতে অনেক সময় লাগত। পরিবর্তে, আমি একটি বিদ্যমান OGG ফাইল ব্যবহার করেছি যা ইতিমধ্যে "রেন্ডার" করা হয়েছে এবং সাউন্ড লাইব্রেরির জন্য নেটিভ ইনস্ট্রুমেন্টস ' ' নির্বাচন করেছি।
ব্যক্তিগতভাবে, আমি বিশ্বাস করি এটি সেরা পিয়ানো VST যন্ত্র উপলব্ধ।
আমি ফলস্বরূপ OGG ফাইলটিকে একটি আদর্শ অডিও উপাদানে এম্বেড করেছি। তখন আমার প্রধান কাজ ছিল আমার নোট ক্যানভাসের scrollTop
অ্যানিমেশনের সাথে অডিও সিঙ্ক্রোনাইজ করা।
আমি সিঙ্ক্রোনাইজেশন মোকাবেলা করার আগে, অ্যানিমেশনটি প্রথমে স্থাপন করতে হয়েছিল। ক্যানভাস অ্যানিমেশনটি মোটামুটি সহজবোধ্য — আমি রৈখিক ইন্টারপোলেশন ব্যবহার করে scrollTop
অসীম মান থেকে শূন্য পর্যন্ত অ্যানিমেট করি। এই অ্যানিমেশনের সময়কাল অ্যালবামের দৈর্ঘ্যের সাথে মেলে।
যখন একটি নোট একটি চাবির উপর নেমে আসে, তখন সেই চাবিটি আলোকিত হয়। এর মানে হল যে প্রতিটি নোটের অবতারণের জন্য, আমাকে সংশ্লিষ্ট কীটি "সক্রিয়" করতে হবে এবং নোটটি তার কোর্স শেষ হলে, এটি নিষ্ক্রিয় করুন।
মোট 6215 নোটের সাথে, এটি 12,430টি নোট সক্রিয়করণ এবং নিষ্ক্রিয়করণ অ্যানিমেশনের সমান।
অতিরিক্তভাবে, আমি ব্যবহারকারীদের অডিও রিওয়াইন্ড করার ক্ষমতা প্রদান করার লক্ষ্য রেখেছিলাম, তাদের অ্যালবামের মধ্যে যেকোনো জায়গায় নেভিগেট করতে সক্ষম করে। এই ধরনের একটি বৈশিষ্ট্য বাস্তবায়ন করার জন্য, একটি শক্তিশালী সমাধান অপরিহার্য।
এবং যখন "শুধু কাজ করে" এমন একটি নির্ভরযোগ্য সমাধানের প্রয়োজনের সম্মুখীন হয়, তখন আমার কাছে যেতে হয় সর্বদা ।
প্রতিটি কীগুলির জন্য সমস্ত অ্যানিমেশন তৈরি করতে কতটা কোড লাগে তা দেখুন। উপাদান অ্যানিমেট করতে id
ব্যবহার করা একক পৃষ্ঠা অ্যাপ্লিকেশনগুলির জন্য সেরা অনুশীলন নয়৷ যাইহোক, এই পদ্ধতি একটি বাস্তব সময় সাশ্রয়কারী. প্রতিটি কীর জন্য আমি id
উল্লেখ করেছি তা মনে রাখবেন? এখানেই তারা খেলায় আসে।
const keysTl = gsap.timeline() notes.value.forEach((note) => { const keySelector = `#note_${note.noteNumber}` keysTl .set(keySelector, KEY_ACTIVE_STATE, note.positionSeconds) .set(keySelector, KEY_INACTIVE_STATE, note.positionSeconds + note.durationSeconds - 0.02) })
সিঙ্ক্রোনাইজেশন কোড মূলত অডিও এবং GSAP গ্লোবাল টাইমলাইনের মধ্যে ইভেন্টগুলির মাধ্যমে একটি সংযোগ স্থাপন করে।
audioRef.value?.addEventListener('timeupdate', () => { const time = audioRef.value?.currentTime ?? 0 globalTl.time(time) }) audioRef.value?.addEventListener('play', () => { globalTl.play() }) audioRef.value?.addEventListener('playing', () => { globalTl.play() }) audioRef.value?.addEventListener('waiting', () => { globalTl.pause() }) audioRef.value?.addEventListener('pause', () => { globalTl.pause() })
ঠিক যখন আমি গুটিয়ে নেওয়ার মতো অনুভব করলাম, তখন একটি কৌতূহলী ধারণা মাথায় এলো। যদি আমি অ্যালবামে একটি অনন্য মোচড় যোগ করি? এটি মূলত আমার করণীয় তালিকায় ছিল না, তবে আমি অনুভব করেছি যে এই বৈশিষ্ট্যটি ছাড়া প্রকল্পটি সত্যই উজ্জ্বল হবে না। সুতরাং, আমি এটিকেও অন্তর্ভুক্ত করতে বেছে নিয়েছি।
প্রতিবার আমি নিজেকে একটি ট্র্যাকে নিমজ্জিত করি, আমি নিজেকে এর গভীর অর্থের প্রতিফলন দেখতে পাই। রচয়িতা কি বার্তা জানাতে চেষ্টা করছিল? উদাহরণস্বরূপ, লুডোভিকো ইনাউডির "নাইটবুক" এর বিবেচনা করুন। পিয়ানো বাম কানে অনুরণিত হয়, যখন স্ট্রিংগুলি ডানদিকে প্রতিধ্বনিত হয়।
এটি উভয়ের মধ্যে উদ্ঘাটিত একটি সংলাপের পরিবেশ তৈরি করে। মনে হচ্ছে যেন পিয়ানো চাবিগুলি অনুসন্ধান করছে: "আপনি কি একমত?" স্ট্রিং ইতিবাচকভাবে প্রতিক্রিয়া. "এটাই কি প্রশ্ন?" স্ট্রিং তাদের নিশ্চিতকরণ প্রতিধ্বনিত. ক্রমটি উভয় যন্ত্রের একত্রিত হওয়ার সাথে শেষ হয়, যা ঐক্য এবং সম্প্রীতির উপলব্ধির প্রতীক। যে একটি মন্ত্রমুগ্ধ অভিজ্ঞতা না?
এটা উল্লেখ করা আবশ্যক যে এটি সম্পূর্ণরূপে আমার ব্যক্তিগত ব্যাখ্যা। একবার, আমি মিলানে একটি লুডোভিকো কনসার্টে যোগ দেওয়ার সুযোগ পেয়েছি। পারফরম্যান্সের পরে, আমি তার কাছে গিয়ে জিজ্ঞাসা করলাম যে তিনি সত্যিই সেই নির্দিষ্ট বিভাগে একটি সংলাপের ধারণাটি এম্বেড করতে চেয়েছিলেন কিনা।
তার প্রতিক্রিয়া জ্ঞানদায়ক ছিল: "আমি কখনই সেভাবে চিন্তা করিনি, তবে আপনি অবশ্যই একটি প্রাণবন্ত কল্পনার অধিকারী।"
সেই অভিজ্ঞতা থেকে অঙ্কন করে, আমি ভাবলাম: যদি আমি শিট মিউজিকের সাথে সাবটাইটেল একত্রিত করি? নির্দিষ্ট অংশগুলি খেলার সাথে সাথে, ভাষ্যটি পর্দায় বাস্তবায়িত হতে পারে, যা সুরকারের অভিপ্রায়ের অন্তর্দৃষ্টি বা ব্যাখ্যা প্রদান করে।
এই বৈশিষ্ট্যটি শ্রোতাদের "লেখক আসলে কী বোঝাতে চেয়েছিলেন?" সম্পর্কে একটি গভীর উপলব্ধি বা একটি নতুন দৃষ্টিভঙ্গি দিতে পারে।
এটা ভাগ্যবান যে আমি আমার অ্যানিমেশন টুল হিসেবে GSAP বেছে নিয়েছি। এটি আমাকে অনায়াসে অন্য একটি টাইমলাইন সংহত করার অনুমতি দেয়, বিশেষভাবে ভাষ্যটি অ্যানিমেট করার দায়িত্ব দেওয়া হয়েছিল। এই সংযোজন প্রক্রিয়াটিকে সুগম করেছে এবং আমার ধারণার বাস্তবায়নকে আরও মসৃণ করেছে।
এইচটিএমএল মার্কআপের মাধ্যমে মন্তব্যগুলি উপস্থাপন করার প্রবণতা আমার ছিল। এটি অর্জনের জন্য, আমি একটি উপাদান তৈরি করেছি যা onMounted
ইভেন্টের সময় অ্যানিমেশনের সাথে পরিচয় করিয়ে দেয়।
<template> <div :class="$style.comment" ref="commentRef"> <slot></slot> </div> </template> <script setup lang="ts"> /* ... */ onMounted(() => { if (!commentRef.value) return props.timeline .fromTo( commentRef.value, { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.5 }, props.time ? parseTime(props.time) : props.delay ? `+=${props.delay}` : '+=1' ) .to( commentRef.value, { autoAlpha: 0, duration: 0.5 }, props.time ? parseTime(props.time) + props.duration : `+=${props.duration}` ) }) </script>
এই উপাদান ব্যবহার নিম্নরূপ হবে.
<template> <div> <Comment time="0:01" :duration="5" :timeline="commentsTl"> <h1>A title for a track</h1> </Comment> <Comment :delay="1" :duration="13" :timeline="commentsTl"> I would like to say... </Comment>
সমস্ত উপাদানের জায়গায়, পরবর্তী পদক্ষেপটি ছিল সাইটটি হোস্ট করা। আমি Netlify বেছে নিয়েছি। এখন, আমি আপনাকে অ্যালবামটি উপভোগ করার এবং চূড়ান্ত উপস্থাপনাটি দেখার জন্য আমন্ত্রণ জানাচ্ছি।
আমি সত্যিই আশা করি সেখানে অন্য পিয়ানো-প্রেমী বিকাশকারীরা আছেন, যারা তাদের অ্যালবামগুলিকে এমন একটি অনন্য পদ্ধতিতে প্রদর্শন করতে আগ্রহী। আপনি যদি তাদের মধ্যে একজন হন, তাহলে প্রকল্পটি শুরু করতে দ্বিধা করবেন না।