- Smart contracts documentation
- Ride smart contracts
/contracts
/scripts
/test_ride
- JS utilities
/src_js
/test_js
- Samples:
/static/samples
SDK source code located in ./src_js/sdk.js
. You can use this SDK for integration with Clef.
- Tone.js for audio rendering.
const {
COLORS,
get_song_name_by_asset_id,
get_song_colors_by_asset_id,
set_volume,
play_song_by_asset_id,
stop
} = require('sdk.js');
COLORS
- array of color values which can be returned byget_song_colors_by_asset_id
.COLORS.major
,COLORS.minor
,COLORS.neutral
,COLORS.weird
get_song_name_by_asset_id(asset_id, options)
async - returns string, song name.asset_id
argument - string, NFT asset ID.options
optional argument - network settings.
const asset_id = 'CSB5QjKAYeY5BCK4EyLC66fKn5QWX73HAXhn4pNb9HD'; const name = await get_song_name_by_asset_id(asset_id); console.log(`Song ${asset_id} name: ${name}`);
get_song_colors_by_asset_id(asset_id, options)
async - returns array of song colors.asset_id
argument - string, NFT asset ID.options
optional argument - network settings.
set_volume(volume)
async - set audio playback volume.volume
argument - number, volume value from0
to1
.
await set_volume(0.7);
play_song_by_asset_id(Tone, asset_id, ready, options)
async - play song.Tone
argument - Tone.js interface.asset_id
argument - string, NFT asset ID.ready
argument - async function, called when song is ready to play.options
optional argument - network settings.
const Tone = require('tone'); const asset_id = 'CSB5QjKAYeY5BCK4EyLC66fKn5QWX73HAXhn4pNb9HD'; console.log('Preparing song audio...'); await play_song_by_asset_id(Tone, asset_id, async () => { console.log('Playing...'); }); console.log('Playback stopped.');
stop()
async - stop audio playback.await stop();
Default network settings:
const options = {
fetch: window.fetch, // fetch function
clef_url: 'https://clef.one/', // for audio samples
clef_cache_url: 'https://cache.clef.one/', // for cached data
node_url: 'https://nodes.wavesnodes.com/', // waves blockchain node
library: '3P4m4beJ6p1pMPHqCQMAXEdquUuXJz72CMe' // clef library contract address
};
Back-end implementation for testing located in ./src_js/back_fake.js
Implementation for blockchain interaction located in ./src_js/back_node.js
const { env,
types,
authenticate,
get_resource_by_id,
get_resource_by_asset_id } = require('back_fake.js');
-
env
- list of environment names.env.keeper
- use a Keeper Wallet account.env.cloud
- use an Email-based Waves.Exchange account.env.web
- use a private key- or seed phrase-based Waves.Exchange account.
-
types
- list of resource type names.- Available for purchase
types.chord
types.rhythm
types.beat
- Other
types.song
types.hybrid
types.arpeggio
- Available for purchase
-
get_resource_by_id(id)
async - get a resource by id. Returns resource object, or null, if not found.const song_id = '1111111A'; const song = await get_resource_by_id(song_id); if (song != null) { console.log(JSON.stringify(song, null, ' ')); }
-
get_resource_by_asset_id(id)
async - get a resource by asset id. Returns resource object, or null, if not found.const asset_id = '3foobarfoobar'; const song = await get_resource_by_asset_id(asset_id); if (song != null) { console.log(JSON.stringify(song, null, ' ')); }
-
authenticate(options)
async - Authenticate a user. Returns clearance object with following methods.let user = await authenticate({ env: env.keeper });
-
Testing-only methods
empty_stock()
async - remove all stock resources.add_balance(balance)
async - increase user balance.
-
Admin-only methods
mint_resources(resources)
async - add resources to stock.resources
argument should be an array of resources.- Each resource should have a
quantity
field.
await user.mint_resources([ { quantity: 8, type: types.chord, label: 'Am', notes: [ 0, 0, 0, 3, 7 ] } ]);
-
logout()
async - sign out. -
get_balance()
async - returns a number, user balance. -
get_free_mix_balance()
async - returns an integer number, user free mix token balance. -
get_resources(options)
async - returns an array of user resources.- Each resource contains at least those fields:
id
- string, unique resource id.type
- string, resource type name. One oftypes
.label
- string, resource label.
options
argument may contain following fields.filter
- resource type string or an array of resource type strings.size
- page size, maximum number of returned resources.after
- id of the resource to paginate after.
/* Get all user resources. */ let resources = await user.get_resources(); /* Get songs only. */ let songs = await user.get_resources({ filter: types.song }); /* Get resources by pages. */ let page0 = await user.get_resources({ filter: types.song, size: 10 }); let page1 = await user.get_resources({ filter: types.song, size: 10, after: page0[page0.length - 1].id });
- Each resource contains at least those fields:
-
buy(type)
async - buy a resource.type
is one oftypes
.await user.buy(types.chord);
-
mint_song(resources)
async - mint a new song from resources.resources
argument is an array of resources. Each resource should have a fieldid
.
let chords = await user.get_resources({ filter: types.chord }); await user.mint_song([ chords[0], chords[1] ]);
-
mint_hybrid(songs)
async - mint a new song from two other songs.songs
argument is an array of 2 songs. Each song should have a fieldid
.
let songs = await user.get_resources({ filter: types.song }); await user.mint_hybrid([ songs[0], songs[1] ]);
-
mint_hybrid_with_free_mix_token(songs)
async - mint a new song from two other songs. Pay with free mix token.songs
argument is an array of 2 songs. Each song should have a fieldid
.
let songs = await user.get_resources({ filter: types.song }); await user.mint_hybrid_with_free_mix_token([ songs[0], songs[1] ]);
-
mint_hybrid_and_purge(songs)
async - mint a new song from two other songs and purge those songs.songs
argument is an array of 2 songs. Each song should have a fieldid
.
let songs = await user.get_resources({ filter: types.song }); await user.mint_hybrid_and_purge([ songs[0], songs[1] ]);
-
get_wallet_address()
async - get user wallet address. Returns a string. To render an avatar, you can useidentity-img
like this:const address = await user.get_wallet_address(); const avatar_size = 64; const identity_img = require('identity-img'); const avatar_url = identity_img.create( address, { rows: 8, cells: 8, size: avatar_size });
-
get_explorer_url()
async - get explorer URL for user wallet. Returns a URL string. -
can_mint_hybrid()
async - returnstrue
if the user has enough balance to mint a hybrid; orfalse
otherwise. Will check for both transactionfee
andpayment
. -
get_airdrop_info(name)
async - returns an object with fieldsairdrop_exists
,user_in_whitelist
,allowed_claims
andsongs_total
.name
argument is a string, airdrop name.airdrop_exists
istrue
if specified airdrop exists;false
otherwise.user_in_whitelist
istrue
if user whitelisted for specified airdrop;false
otherwise.allowed_claims
is a number of songs user can claim from specified airdrop.songs_total
is a total number of songs left in specified airdrop.
let airdrop = 'test'; let info = await user.get_airdrop_info(airdrop); console.log(`Allowed claims: ${info.allowed_claims}; songs total: ${info.songs_total}`);
-
airdrop_claim(name)
async - claim songs from an airdrop. Returns an array with claimed songs ids.name
argument is a string, airdrop name.
let airdrop = 'test'; let ids = await user.airdrop_claim(airdrop);
-
Example
const { env, types, authenticate } = require('back_fake.js');
authenticate({ env: env.keeper }).then(user => {
/* Print user balance. */
user.get_balance().then(balance => {
console.log(`Balance: ${balance}`);
});
/* Buy a chord, then print all bought chords. */
user.buy(types.chord).then(() => {
user.get_resources({ filter: types.chord }).then(chords => {
console.log('Bought chords:');
console.log(JSON.stringify(chords, null, ' '));
});
});
});
NOTE: Don't access resource internal elements directly, they may change in future. Use functions from music.js utility instead.
For use cases see ./test_js/back_fake.test.js
Music utility is located in ./src_js/music.js
.
const { diatonic_minor,
pitch_to_midi,
beat_to_sec,
render_sheet
get_song_label,
get_song_parents,
get_song_bpm,
get_song_meter,
get_song_tonality,
colors,
get_song_colors,
get_song_chords,
get_chord_name,
get_song_chord_names,
get_song_generation,
get_song_asset_id,
get_song_asset_url,
can_mint_hybrid } = require('music.js');
-
diatonic_minor(key_note)
- returns an array of integer numbers representing diatonic minor scale. -
pitch_to_midi(tonality, pitch)
- returns an integer representing a MIDI note from tonality and pitch.tonality
argument is an array of MIDI notes to define a tonality.pitch
argument is a number of steps from key note in specified tonality.
let tonality = diatonic_minor(0); let midi_key_note = pitch_to_midi(tonality, 0); let midi_note = pitch_to_midi(tonality, 15);
-
beat_to_sec(bpm, beats)
- convertsbeats
to seconds according to specifiedbpm
./* duration = 0.5 */ let duration = beat_to_sec(120, 4);
-
render_sheet(song)
- convertsong
to music sheet.song
argument should be a song resource returned from back-end.
Music sheet is an object with following fields.
duration
- total duration in seconds.instruments
- object with names for each instrument.kick
,snare
,hihat
,bass
,back
,lead
- arrays of notes for each instrument.
Each array's element is an object with following fields.
time
- note attack time in seconds.note
- MIDI note pitch.duration
- note duration.velocity
- note velocity. If there is a pause before the first note, the array will have a first element with fieldduration
value equal to zero.
let songs = await user.get_resources({ filter: types.song }); let sheet = render_sheet(songs[0]);
-
get_song_id(song)
- returns song id: string. -
get_song_label(song)
- returns song label. -
get_song_parents(song)
- returns array of song parents' ids. -
get_song_bpm(song)
- returns song BPM. -
get_song_meter(song)
- returns song meter: array of two integers. -
get_song_tonality(song)
- returns song tonality name: string. -
get_song_colors(song)
- returns song colors: array of integer values. Each value is one of:colors.major
,colors.minor
,colors.neutral
,colors.weird
,
-
get_song_chords(song)
- returns song chords: array of chords. Each chord is an array of notes. Each note is an integer number of semitone steps from C. -
get_chord_name(chord)
- returns chord name.chord
is an array of notes. -
get_song_chord_names(song)
- returns song chord names: array of strings. -
get_song_generation(song)
- returns song generation: integer number. -
get_song_asset_id(song)
- returns song NFT asset id: string. -
get_song_asset_url(song)
- returns song NFT asset url: string. -
can_mint_hybrid(song)
- returns true if a hybrid can be minted from specified song; false otherwise.
NOTE: Resource id and resource asset id are different. Asset id is a hash-identifier of NFT in blockchain.
Example
const { get_resource_by_id } = require('back_fake.js');
const { render_sheet,
get_song_label,
get_song_parents,
get_song_bpm,
get_song_meter,
get_song_tonality,
colors,
get_song_colors,
get_song_chords,
get_song_chord_names,
get_song_generation,
get_song_asset_id,
get_song_asset_url } = require('music.js');
const song_id = '1111111A';
get_resource_by_id(song_id).then(song => {
const sheet = render_sheet(song);
const asset_id = get_song_asset_id(song);
const asset_url = get_song_asset_url(song);
const label = get_song_label(song);
const parents = get_song_parents(song);
const bpm = get_song_bpm(song);
const meter = get_song_meter(song);
const tonality = get_song_tonality(song);
const colors = get_song_colors(song);
const chords = get_song_chords(song);
const generation = get_song_generation(song);
const chord_names = get_song_chord_names(song);
});
For use cases see ./test_js/music.test.js
Audio utility is located in ./src_js/audio.js
.
const { render_audio,
render_song,
play_song,
stop,
set_volume } = require('audio.js');
const Tone = require('tone');
render_audio(Tone, sheet, log)
async - render sheet to a buffer.Tone
argument is aTone.js
library interface.song
argument should be a sheet data returned byrender_sheet(song)
.log
argument is logging interface.
let buffer = await render_audio(Tone, sheet, (s) => { console.log(s); });
render_song(Tone, song, log)
async - render song to a buffer.Tone
argument is aTone.js
library interface.song
argument should be a song resource returned from back-end.log
argument is logging interface.
let buffer = await render_song(Tone, song, (s) => { console.log(s); });
play_song(Tone, song, ready, log)
async - render song and play. Promise returns when playback is done.Tone
argument is aTone.js
library interface.song
argument should be a song resource returned from back-end.ready
argument is a function called when audio buffer is ready.log
argument is logging interface.
console.log('Play'); play_song(Tone, song, () => { console.log('Ready') }, (s) => { console.log(s); }) .then(() => { console.log('Done'); });
stop(Tone)
async - stop playing.Tone
argument is aTone.js
library interface.
stop(Tone);
set_volume(value)
async - set playback volume.value
- volume value from0
to1
, where0
is silence,1
is maximum volume.
set_volume(0.5);
NOTE: This reference may change.
Chord is defined as a list of notes. Each note is a number of semitone steps from tonality root. First note is bass, second note is lead.
chord {
label: string;
notes: integer[];
}
Rhythm is defined as a list of note durations. Every other duration defines a pause, so total number of durations is a multiple of 2.
NOTE: In blockchain, rhythm notes are stored as an integer value for scale and a list of integer values for notes. Each note duartion is defined as note / scale.
rhythm {
label: string;
notes: float[];
}
song {
label: string;
parents: token_id[];
bpm: integer;
bar_size: integer;
beat_size: integer;
tonality: integer;
instruments {
kick: string;
snare: string;
hihat: string;
bass: string;
back: string;
lead: string;
}
chords: chord[];
arpeggio: integer[];
rhythm {
kick: rhythm[];
snare: rhythm[];
hihat: rhythm[];
bass: rhythm[];
back: rhythm[];
lead: rhythm[];
}
}