Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • flow3r/flow3r-firmware
  • Vespasian/flow3r-firmware
  • alxndr42/flow3r-firmware
  • pl/flow3r-firmware
  • Kari/flow3r-firmware
  • raimue/flow3r-firmware
  • grandchild/flow3r-firmware
  • mu5tach3/flow3r-firmware
  • Nervengift/flow3r-firmware
  • arachnist/flow3r-firmware
  • TheNewCivilian/flow3r-firmware
  • alibi/flow3r-firmware
  • manuel_v/flow3r-firmware
  • xeniter/flow3r-firmware
  • maxbachmann/flow3r-firmware
  • yGifoom/flow3r-firmware
  • istobic/flow3r-firmware
  • EiNSTeiN_/flow3r-firmware
  • gnudalf/flow3r-firmware
  • 999eagle/flow3r-firmware
  • toerb/flow3r-firmware
  • pandark/flow3r-firmware
  • teal/flow3r-firmware
  • x42/flow3r-firmware
  • alufers/flow3r-firmware
  • dos/flow3r-firmware
  • yrlf/flow3r-firmware
  • LuKaRo/flow3r-firmware
  • ThomasElRubio/flow3r-firmware
  • ai/flow3r-firmware
  • T_X/flow3r-firmware
  • highTower/flow3r-firmware
  • beanieboi/flow3r-firmware
  • Woazboat/flow3r-firmware
  • gooniesbro/flow3r-firmware
  • marvino/flow3r-firmware
  • kressnerd/flow3r-firmware
  • quazgar/flow3r-firmware
  • aoid/flow3r-firmware
  • jkj/flow3r-firmware
  • naomi/flow3r-firmware
41 results
Show changes
Showing
with 416 additions and 60 deletions
......@@ -4,8 +4,13 @@
typedef struct {
uint32_t read_head_position;
uint32_t write_head_position;
uint32_t sample_start;
uint32_t sample_len;
int16_t trigger_prev;
int16_t rec_trigger_prev;
int16_t volume;
bool rec_active;
} sampler_data_t;
extern radspa_descriptor_t sampler_desc;
......
......@@ -98,17 +98,12 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
}
for(uint8_t j = 0; j < data->num_tracks; j++){
if(track_sigs[j]->buffer != NULL) (track_sigs[j]->buffer)[i] = data->tracks[j].track_fill;
track_sigs[j]->set_value(track_sigs[j], i, data->tracks[j].track_fill, num_samples, render_pass_id);
}
if(sync_out_sig->buffer != NULL) (sync_out_sig->buffer)[i] = data->sync_out;
if(step_sig->buffer != NULL) (step_sig->buffer)[i] = data->step;
sync_out_sig->set_value(sync_out_sig, i, data->sync_out, num_samples, render_pass_id);
step_sig->set_value(step_sig, i, data->step, num_samples, render_pass_id);
}
}
for(uint8_t j = 0; j < data->num_tracks; j++){
track_sigs[j]->value = data->tracks[j].track_fill;
}
sync_out_sig->value = data->sync_out;
step_sig->value = data->step;
}
radspa_t * sequencer_create(uint32_t init_var){
......
......@@ -44,8 +44,7 @@ void slew_rate_limiter_run(radspa_t * slew_rate_limiter, uint16_t num_samples, u
} else {
ret = input;
}
(output_sig->buffer)[i] = ret;
output_sig->set_value(output_sig, i, ret, num_samples, render_pass_id);
}
output_sig->value = ret;
}
......@@ -4,7 +4,7 @@
// this file, kindly append "-modified" to the version string below so it is not mistaken
// for an official release.
// Version 0.1.0+
// Version 0.2.0
/* Realtime Audio Developer's Simple Plugin Api
*
......@@ -43,6 +43,10 @@
#define RADSPA_SIGNAL_HINT_TRIGGER (1<<2)
#define RADSPA_SIGNAL_HINT_GAIN (1<<3)
#define RADSPA_SIGNAL_HINT_SCT (1<<5)
#define RADSPSA_SIGNAL_HINT_REDUCED_RANGE (1<<6)
#define RADSPSA_SIGNAL_HINT_POS_SHIFT (1<<7)
// 6 bit number, 0 for non-stepped, else number of steps
#define RADSPSA_SIGNAL_HINT_STEPPED_LSB (1<<8)
#define RADSPA_SIGNAL_VAL_SCT_A440 (INT16_MAX - 6*2400)
#define RADSPA_SIGNAL_VAL_UNITY_GAIN (1<<12)
......@@ -60,21 +64,30 @@ typedef struct _radspa_descriptor_t{
} radspa_descriptor_t;
typedef struct _radspa_signal_t{
// this bitfield determines the type of the signal, see RADSPA_SIGNAL_HINTS_*
uint32_t hints;
// this is the name of the signal as shown to the user.
// allowed characters: lowercase, numbers, underscore, may not start with number
// if name_multplex >= 0: may not end with number
char * name;
// arbitrary formatted string to describe what the signal is for
char * description;
// unit that corresponds to value, may be empty. note: some RADSPA_SIGNAL_HINTS_*
// imply units. field may be empty.
char * unit;
// -1 to disable signal multiplexing
int8_t name_multiplex;
int16_t * buffer; // full buffer of num_samples. may be NULL.
// used for input channels only
int16_t value; //static value, should be used if buffer is NULL.
// buffer full of samples, may be NULL
int16_t * buffer;
// static value to be used when buffer is NULL for input signals only
int16_t value;
uint32_t render_pass_id;
// function to retrieve value. radspa_helpers provides an example.
// function for input signals to retrieve their value at a buffer index. radspa_helpers provides an example. this function should be set by the host.
int16_t (* get_value)(struct _radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id);
struct _radspa_signal_t * next; //signals are in a linked list
// function for output signals to set a value at a buffer index. radspa_helpers provides an example. this function should be set by the host.
void (* set_value)(struct _radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id);
// linked list pointer
struct _radspa_signal_t * next;
} radspa_signal_t;
typedef struct _radspa_t{
......
......@@ -77,6 +77,7 @@ int16_t radspa_signal_add(radspa_t * plugin, char * name, uint32_t hints, int16_
sig->value = value;
sig->name_multiplex = -1;
sig->get_value = radspa_signal_get_value;
sig->set_value = radspa_signal_set_value;
//find end of linked list
uint16_t list_index = 0;
......@@ -97,16 +98,41 @@ int16_t radspa_signal_add(radspa_t * plugin, char * name, uint32_t hints, int16_
}
int16_t radspa_signal_get_value(radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id){
if(sig->buffer != NULL){
if(sig->render_pass_id != render_pass_id){
radspa_host_request_buffer_render(sig->buffer, num_samples); //, render_pass_id);
sig->render_pass_id = render_pass_id;
}
return sig->buffer[index];
if(sig->buffer == NULL){
return radspa_signal_get_value_disconnected(sig, index, num_samples, render_pass_id);
} else {
return radspa_signal_get_value_connected(sig, index, num_samples, render_pass_id);
}
}
inline int16_t radspa_signal_get_value_connected(radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id){
if(sig->render_pass_id != render_pass_id){
radspa_host_request_buffer_render(sig->buffer, num_samples); //, render_pass_id);
sig->render_pass_id = render_pass_id;
}
return sig->buffer[index];
}
inline int16_t radspa_signal_get_value_disconnected(radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id){
return sig->value;
}
void radspa_signal_set_value(radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id){
if(sig->buffer == NULL){
radspa_signal_set_value_disconnected(sig, index, value, num_samples, render_pass_id);
} else {
radspa_signal_set_value_connected(sig, index, value, num_samples, render_pass_id);
}
}
inline void radspa_signal_set_value_connected(radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id){
sig->buffer[index] = value;
}
inline void radspa_signal_set_value_disconnected(radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id){
sig->value = value;
}
radspa_t * radspa_standard_plugin_create(radspa_descriptor_t * desc, uint8_t num_signals, size_t plugin_data_size, uint32_t plugin_table_size){
radspa_t * ret = calloc(1, sizeof(radspa_t));
if(ret == NULL) return NULL;
......
......@@ -28,3 +28,10 @@ void radspa_signals_free(radspa_t * plugin);
* of radspa_host_request_buffer_render.
*/
int16_t radspa_signal_get_value(radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id);
int16_t radspa_signal_get_value_connected(radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id);
int16_t radspa_signal_get_value_disconnected(radspa_signal_t * sig, int16_t index, uint16_t num_samples, uint32_t render_pass_id);
void radspa_signal_set_value(radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id);
void radspa_signal_set_value_connected(radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id);
void radspa_signal_set_value_disconnected(radspa_signal_t * sig, int16_t index, int16_t value, uint16_t num_samples, uint32_t render_pass_id);
......@@ -145,16 +145,6 @@ void st3m_gfx_splash(const char *c);
// Called by st3m_usb_cdc when it has the opportunity to send some data to the
// host.
size_t st3m_console_cdc_on_txpoll(uint8_t *buffer, size_t bufsize) {
// I have no idea why this is needed, but it is. Otherwise a large backlog
// of data cuases the IN endpoint to get stuck.
//
// I've spend three days debugging this.
//
// No, I'm not fine. Thanks for asking, though. I appreciate it.
if (bufsize > 0) {
bufsize -= 1;
}
int64_t now = esp_timer_get_time();
xSemaphoreTake(_state.mu, portMAX_DELAY);
......@@ -400,4 +390,4 @@ static esp_vfs_t _vfs = {
.open = &_console_open,
.read = &_console_read,
.write = &_console_write,
};
\ No newline at end of file
};
......@@ -46,6 +46,7 @@ void st3m_usb_cdc_txpoll(void) {
for (;;) {
uint32_t space = tud_cdc_n_write_available(st3m_usb_interface_app_cdc);
if (space == 0) {
tud_cdc_n_write_flush(st3m_usb_interface_app_cdc);
return;
}
......
docs/badge/assets/0.png

25.6 KiB

docs/badge/assets/1.png

25.2 KiB

docs/badge/assets/2.png

26.4 KiB

docs/badge/assets/3.png

27.3 KiB

docs/badge/assets/4.png

27.7 KiB

docs/badge/assets/5.png

20.7 KiB

docs/badge/assets/6.png

25.2 KiB

docs/badge/assets/8.png

27.9 KiB

......@@ -8,29 +8,38 @@ The current selection of fonts is baked into the firmware for use with :ref:`Con
Available Fonts
---------------
Following fonts are currently available:
+-------------+----------------------+
| Font Number | Font Name |
+=============+======================+
| 0 | Arimo Regular |
+-------------+----------------------+
| 1 | Arimo Bold |
+-------------+----------------------+
| 2 | Arimo Italic |
+-------------+----------------------+
| 3 | Arimo Bold Italic |
+-------------+----------------------+
| 4 | Camp Font 1 |
+-------------+----------------------+
| 5 | Camp Font 2 |
+-------------+----------------------+
| 6 | Camp Font 3 |
+-------------+----------------------+
| 7 | Material Icons |
+-------------+----------------------+
| 8 | Comic Mono |
+-------------+----------------------+
Following fonts are currently available (previews in size 20):
.. |font0| image:: assets/0.png
.. |font1| image:: assets/1.png
.. |font2| image:: assets/2.png
.. |font3| image:: assets/3.png
.. |font4| image:: assets/4.png
.. |font5| image:: assets/5.png
.. |font6| image:: assets/6.png
.. |font8| image:: assets/8.png
+-------------+----------------------+---------+
| Font Number | Font Name | Preview |
+=============+======================+=========+
| 0 | Arimo Regular | |font0| |
+-------------+----------------------+---------+
| 1 | Arimo Bold | |font1| |
+-------------+----------------------+---------+
| 2 | Arimo Italic | |font2| |
+-------------+----------------------+---------+
| 3 | Arimo Bold Italic | |font3| |
+-------------+----------------------+---------+
| 4 | Camp Font 1 | |font4| |
+-------------+----------------------+---------+
| 5 | Camp Font 2 | |font5| |
+-------------+----------------------+---------+
| 6 | Camp Font 3 | |font6| |
+-------------+----------------------+---------+
| 7 | Material Icons | |
+-------------+----------------------+---------+
| 8 | Comic Mono | |font8| |
+-------------+----------------------+---------+
The Camp Fonts are based on Beon Regular, Saira Stencil One and Questrial Regular.
......
......@@ -60,7 +60,22 @@ After connecting your badge and making sure it runs:
Use Ctrl-] or Ctrl-x to exit this shell
[... logs here... ]
The badge will continue to run. Now, if you press Ctrl-C, you will interrupt the
The badge will continue to run.
.. warning::
**Your flow3r is not showing up using Linux?**
To let ``mpremote`` to work properly your user needs to have access rights to ttyACM.
Quick fix: ``sudo chmod a+rw /dev/ttyACM[Your Device Id here]```
More sustainable fix: Setup an udev rule to automatically allow the logged in user to access ttyUSB
1. To use this, add the following to /etc/udev/rules.d/60-extra-acl.rules: ``KERNEL=="ttyACM[0-9]*", TAG+="udev-acl", TAG+="uaccess"``
2. Reload ``udevadm control --reload-rules && udevadm trigger``
Now, if you press Ctrl-C, you will interrupt the
firmware and break into a Python REPL (read-eval-print-loop) prompt:
::
......
from st3m.goose import Enum
from st3m.application import Application, ApplicationContext
from st3m.input import InputController, InputState
from st3m.ui.interactions import ScrollController
from st3m.ui import colours
from st3m.ui.view import ViewManager
from ctx import Context
import network
from .applist import AppList
from .background import Flow3rView
from .record import RecordView
from .manual import ManualInputView
class ViewState(Enum):
CONTENT = 1
NO_INTERNET = 2
class Gr33nhouseApp(Application):
items = ["Browse apps", "Record flow3r seed", "Enter flow3r seed"]
input: InputController
background: Flow3rView
state: ViewState
def __init__(self, app_ctx: ApplicationContext) -> None:
super().__init__(app_ctx=app_ctx)
self.input = InputController()
self.background = Flow3rView()
self._sc = ScrollController()
self._sc.set_item_count(3)
self.state = ViewState.CONTENT
def on_enter(self, vm: ViewManager | None) -> None:
super().on_enter(vm)
if self.vm is None:
raise RuntimeError("vm is None")
def draw(self, ctx: Context) -> None:
if self.state == ViewState.NO_INTERNET:
ctx.move_to(0, 0)
ctx.rgb(*colours.BLACK)
ctx.rectangle(
-120.0,
-120.0,
240.0,
240.0,
).fill()
ctx.save()
ctx.rgb(*colours.WHITE)
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.move_to(0, -15)
ctx.text("No internet")
ctx.move_to(0, 15)
ctx.text("Check settings")
ctx.restore()
return
self.background.draw(ctx)
ctx.save()
ctx.gray(1.0)
ctx.rectangle(
-120.0,
-15.0,
240.0,
30.0,
).fill()
ctx.translate(0, -30 * self._sc.current_position())
offset = 0
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
for idx, item in enumerate(self.items):
if idx == self._sc.target_position():
ctx.gray(0.0)
else:
ctx.gray(1.0)
ctx.move_to(0, offset)
ctx.text(item)
offset += 30
ctx.restore()
def think(self, ins: InputState, delta_ms: int) -> None:
self.input.think(ins, delta_ms)
self._sc.think(ins, delta_ms)
if self.vm is None:
raise RuntimeError("vm is None")
if not network.WLAN(network.STA_IF).isconnected():
self.state = ViewState.NO_INTERNET
return
else:
self.state = ViewState.CONTENT
self.background.think(ins, delta_ms)
if self.input.buttons.app.left.pressed:
self._sc.scroll_left()
elif self.input.buttons.app.right.pressed:
self._sc.scroll_right()
elif self.input.buttons.app.middle.pressed:
pos = self._sc.target_position()
if pos == 0:
self.vm.push(AppList())
elif pos == 1:
self.vm.push(RecordView())
elif pos == 2:
self.vm.push(ManualInputView())
from st3m.goose import Optional, Enum, Any
from st3m.input import InputController, InputState
from st3m.ui import colours
from st3m.ui.view import BaseView, ViewManager
from st3m.ui.interactions import ScrollController
from ctx import Context
import urequests
import time
from .background import Flow3rView
from .confirmation import ConfirmationView
class ViewState(Enum):
INITIAL = 1
LOADING = 2
ERROR = 3
LOADED = 4
class AppList(BaseView):
initial_ticks: int = 0
_state: ViewState = ViewState.INITIAL
apps: list[Any] = []
input: InputController
background: Flow3rView
def __init__(self) -> None:
self.input = InputController()
self.vm = None
self.background = Flow3rView()
self._sc = ScrollController()
def on_enter(self, vm: Optional[ViewManager]) -> None:
self.vm = vm
self.initial_ticks = time.ticks_ms()
def draw(self, ctx: Context) -> None:
ctx.move_to(0, 0)
if self._state == ViewState.INITIAL or self._state == ViewState.LOADING:
ctx.rgb(*colours.BLACK)
ctx.rectangle(
-120.0,
-120.0,
240.0,
240.0,
).fill()
ctx.save()
ctx.rgb(*colours.WHITE)
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.text("Collecting seeds...")
ctx.restore()
return
elif self._state == ViewState.ERROR:
ctx.rgb(*colours.BLACK)
ctx.rectangle(
-120.0,
-120.0,
240.0,
240.0,
).fill()
ctx.save()
ctx.rgb(*colours.WHITE)
ctx.gray(1.0)
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.text("Something went wrong")
ctx.restore()
return
elif self._state == ViewState.LOADED:
self.background.draw(ctx)
ctx.save()
ctx.gray(1.0)
ctx.rectangle(
-120.0,
-15.0,
240.0,
30.0,
).fill()
ctx.translate(0, -30 * self._sc.current_position())
offset = 0
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.move_to(0, 0)
for idx, app in enumerate(self.apps):
if idx == self._sc.target_position():
ctx.gray(0.0)
else:
ctx.gray(1.0)
ctx.move_to(0, offset)
ctx.text(app["name"])
offset += 30
ctx.restore()
else:
raise RuntimeError(f"Invalid view state {self._state}")
def think(self, ins: InputState, delta_ms: int) -> None:
self._sc.think(ins, delta_ms)
if self.initial_ticks == 0 or time.ticks_ms() < self.initial_ticks + 300:
return
self.input.think(ins, delta_ms)
if self._state == ViewState.INITIAL:
try:
self._state = ViewState.LOADING
print("Loading app list...")
res = urequests.get("https://flow3r.garden/api/apps.json")
self.apps = res.json()["apps"]
if self.apps == None:
print(f"Invalid JSON or no apps: {res.json()}")
self._state = ViewState.ERROR
return
self._state = ViewState.LOADED
self._sc.set_item_count(len(self.apps))
print("App list loaded")
except Exception as e:
print(f"Load failed: {e}")
self._state = ViewState.ERROR
return
elif self._state == ViewState.LOADING:
raise RuntimeError(f"Invalid view state {self._state}")
elif self._state == ViewState.ERROR:
return
self.background.think(ins, delta_ms)
if self.input.buttons.app.left.pressed:
self._sc.scroll_left()
elif self.input.buttons.app.right.pressed:
self._sc.scroll_right()
elif self.input.buttons.app.middle.pressed:
if self.vm is None:
raise RuntimeError("vm is None")
app = self.apps[self._sc.target_position()]
url = app["tarDownloadUrl"]
name = app["name"]
author = app["author"]
self.vm.push(
ConfirmationView(
url=url,
name=name,
author=author,
)
)