Skip to content

Latest commit

 

History

History
248 lines (178 loc) · 7.39 KB

File metadata and controls

248 lines (178 loc) · 7.39 KB

Sono Extensions: Developer Reference

What is an extension?

Extensions let you add new visual experiences and interactions to Sono. They run in a sandboxed Lua environment with access to APIs that expose player state, real-time audio data, the local library, persistent storage, and a 2D drawing canvas.


The .sopk format

An extension is a .sopk file; a ZIP archive containing at minimum:

  • manifest.json: describes the extension and declares its requirements
  • An entry Lua file (default: main.lua)

manifest.json

{
  "id": "com.example.my_extension",
  "name": "My Extension",
  "version": "1.0.0",
  "sono_sdk": ">=1.0.0",
  "author": "Your Name",
  "description": "Short description of what this does",
  "entry": "main.lua",
  "type": "tool",
  "permissions": ["player.read", "ui.screen"],
  "hooks": ["onTrackChanged"],
  "ui_mode": "canvas"
}

Fields

Field Required Description
id yes Unique reverse-domain identifier, e.g. com.example.ext
name yes Display name shown in the UI
version yes SemVer string, e.g. 1.0.0
sono_sdk yes Version constraint for the Sono SDK, e.g. >=1.0.0
author yes Author name
description yes One-line description
entry yes Path to the Lua entry point, relative to the archive root
type yes One of: feature lyrics tracker widget tool
permissions no Array of permission strings the extension needs
hooks no Array of lifecycle hook names to subscribe to
ui_mode no canvas for custom drawing, rfw for Remote Flutter Widgets
ui_file no Path to the RFW definition file (rfw mode only)
ui_overlay no true to render the canvas as a full-screen overlay

Permissions

Extensions must declare every API they intend to use. APIs for undeclared permissions are not registered and will cause a nil error if called.

Permission APIs unlocked
player.read sono.player.* (read-only)
player.control sono.player.play(), pause(), seek(), next(), previous(), setSpeed()
audio.fft sono.audio.getSpectrum()
library.read sono.library.*
storage sono.storage.*
ui.screen sono.canvas.* and sono.ui.*

Lifecycle

Functions you can define

All functions are optional. The runtime only calls them if they exist.

Function When called
sono_init() Once after the extension activates
sono_onDraw(w, h) Every frame (~60 fps) when the canvas UI is visible
sono_onTrackChanged(track) When the current track changes
sono_onPlaybackStateChanged(playing) When playback starts or pauses
sono_dispose() When the extension is deactivated or uninstalled

Hooks must also be listed in the manifest's hooks array to receive calls for onTrackChanged and onPlaybackStateChanged.


API Reference

sono.player

Requires player.read.

Function Returns Description
sono.player.getCurrentTrack() track or nil The currently loaded track
sono.player.isPlaying() bool Whether playback is active
sono.player.getPosition() number Current position in milliseconds
sono.player.getDuration() number Track duration in milliseconds

Track object fields: id, title, artist, album, durationMs, path


sono.player: control

Requires player.control (in addition to player.read).

Function Description
sono.player.play() Start playback
sono.player.pause() Pause playback
sono.player.playPause() Toggle play/pause
sono.player.seek(ms) Seek to a position in milliseconds
sono.player.next() Skip to next track
sono.player.previous() Skip to previous track
sono.player.setSpeed(speed) Set playback speed (1.0 = normal)

sono.audio

Requires audio.fft. Android only; returns an empty table on other platforms.

Function Returns Description
sono.audio.getSpectrum() table 1-indexed table of FFT magnitudes (0..1), ~64 bins

The spectrum is updated at ~30 fps. Index 1 is the lowest frequency; higher indices correspond to higher frequencies. Safe to call every draw frame.


sono.library

Requires library.read.

Function Returns Description
sono.library.getAllSongs() table Array of all song objects in the library
sono.library.getAlbums() table Array of album objects
sono.library.getFavoriteSongIds() table Array of favourite song IDs

Song fields: id, title, artist, album, durationMs, path Album fields: id, album, artist, numOfSongs


sono.storage

Requires storage. Per-extension key-value store, persisted to disk between sessions.

Function Description
sono.storage.set(key, value) Store a value (string, number, or boolean)
sono.storage.get(key) Read a stored value (nil if absent)
sono.storage.delete(key) Remove a single key
sono.storage.clear() Remove all stored data for this extension

sono.canvas

Requires ui.screen and ui_mode: "canvas". Only valid inside sono_onDraw(w, h).

All color arguments are RGBA integers in the range 0–255. Coordinates are in logical pixels with (0, 0) at the top-left.

Function Description
sono.canvas.clear(r, g, b, a) Fill the entire canvas with a color
sono.canvas.drawRect(x, y, w, h, r, g, b, a) Draw a filled rectangle
sono.canvas.drawCircle(cx, cy, radius, r, g, b, a) Draw a filled circle
sono.canvas.drawLine(x1, y1, x2, y2, r, g, b, a, strokeWidth) Draw a line
sono.canvas.drawText(text, x, y, r, g, b, a, fontSize) Draw text

The w and h parameters passed to sono_onDraw give the canvas dimensions for the current frame.


sono.ui

Requires ui.screen and ui_mode: "rfw". Used to push data from Lua into a Remote Flutter Widget tree.

Function Description
sono.ui.setData(key, value) Push a value into the Flutter widget tree
sono.ui.getData(key) Read a value previously set

Example: minimal beat-reactive canvas extension

manifest.json

{
  "id": "com.example.pulse",
  "name": "Pulse",
  "version": "1.0.0",
  "sono_sdk": ">=1.0.0",
  "author": "You",
  "description": "A circle that reacts to the bass",
  "entry": "main.lua",
  "type": "tool",
  "permissions": ["player.read", "audio.fft", "ui.screen"],
  "hooks": ["onPlaybackStateChanged"],
  "ui_mode": "canvas"
}

main.lua

local radius  = 40
local playing = false

function sono_init()
  playing = sono.player.isPlaying()
end

function sono_onPlaybackStateChanged(p)
  playing = p
end

function sono_onDraw(w, h)
  sono.canvas.clear(10, 10, 10, 255)

  if playing then
    local spec = sono.audio.getSpectrum()
    local bass = (spec[1] or 0) + (spec[2] or 0) + (spec[3] or 0)
    radius = radius * 0.8 + (40 + bass * 80) * 0.2
  end

  sono.canvas.drawCircle(w * 0.5, h * 0.5, radius, 255, 72, 147, 200)
end

Packaging

Put your manifest.json and Lua files into a ZIP archive and rename it to .sopk:

zip -j my_extension.sopk manifest.json main.lua

Install it via Settings > Extensions > Install .sopk.

REMEMBER: This is an early version of Sono Extensions. Things will probably change.