- 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_idargument - string, NFT asset ID.optionsoptional 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_idargument - string, NFT asset ID.optionsoptional argument - network settings.
set_volume(volume)async - set audio playback volume.volumeargument - number, volume value from0to1.
await set_volume(0.7);
play_song_by_asset_id(Tone, asset_id, ready, options)async - play song.Toneargument - Tone.js interface.asset_idargument - string, NFT asset ID.readyargument - async function, called when song is ready to play.optionsoptional 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.chordtypes.rhythmtypes.beat
- Other
types.songtypes.hybridtypes.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.resourcesargument should be an array of resources.- Each resource should have a
quantityfield.
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.
optionsargument 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.typeis one oftypes.await user.buy(types.chord);
-
mint_song(resources)async - mint a new song from resources.resourcesargument 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.songsargument 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.songsargument 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.songsargument 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-imglike 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 - returnstrueif the user has enough balance to mint a hybrid; orfalseotherwise. Will check for both transactionfeeandpayment. -
get_airdrop_info(name)async - returns an object with fieldsairdrop_exists,user_in_whitelist,allowed_claimsandsongs_total.nameargument is a string, airdrop name.airdrop_existsistrueif specified airdrop exists;falseotherwise.user_in_whitelististrueif user whitelisted for specified airdrop;falseotherwise.allowed_claimsis a number of songs user can claim from specified airdrop.songs_totalis 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.nameargument 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.tonalityargument is an array of MIDI notes to define a tonality.pitchargument 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)- convertsbeatsto seconds according to specifiedbpm./* duration = 0.5 */ let duration = beat_to_sec(120, 4);
-
render_sheet(song)- convertsongto music sheet.songargument 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 fielddurationvalue 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.chordis 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.Toneargument is aTone.jslibrary interface.songargument should be a sheet data returned byrender_sheet(song).logargument 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.Toneargument is aTone.jslibrary interface.songargument should be a song resource returned from back-end.logargument 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.Toneargument is aTone.jslibrary interface.songargument should be a song resource returned from back-end.readyargument is a function called when audio buffer is ready.logargument 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.Toneargument is aTone.jslibrary interface.
stop(Tone);
set_volume(value)async - set playback volume.value- volume value from0to1, where0is silence,1is 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[];
}
}