GithubHelp home page GithubHelp logo

monome / crow Goto Github PK

View Code? Open in Web Editor NEW
161.0 25.0 34.0 2.01 MB

Crow speaks and listens and remembers bits of text. A scriptable USB-CV-II machine

License: GNU General Public License v3.0

Makefile 1.48% C 61.85% Lua 32.35% Shell 0.03% Dockerfile 0.11% Batchfile 0.16% Python 4.02%

crow's People

Contributors

beels avatar carlcolglazier avatar csboling avatar delta-6400 avatar dewb avatar discohead avatar dndrks avatar isaacpearl avatar jonpliske avatar jroo avatar ngwese avatar nordseele avatar pq avatar rbrt-fm avatar ryland avatar simonvanderveldt avatar szvsw avatar tehn avatar trentgill avatar tyleretters avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

crow's Issues

usb device lua REPL

fully functioning usb device REPL:

  • connect via screen/minicom

  • type directly at crow, receive evaluations

  • two types of messages received from crow: DEBUG and COMMAND. one or the other could simply have a preceding character, ie, debug starts with > for example. a command would be something that could be directly executed by the host interpreter, ie adc(1,2)

investigate end character (newline?) to evaluate
note screen/minicom will need to turning local key echo on?

improve 'l2h' lua to c-header converter script

ed note: This whole process could likely be deprecated if some smart person figures out how we can upload lua bytecode to crow directly, rather than manually convert everything into C strings & load them at runtime. The key benefit here is that lua syntax errors will be caught by that process (using luac i guess), and perhaps smaller RAM footprint (because the C strings aren't loaded into memory at all).

I'm unclear on the memory implications of the current approach - Some help profiling the memory usage would likely be a good use of time.

/////////////////////////////////////////////////////

nb: l2h is written using 'fennel', which is a lisp syntax for the lua language. if you want to rewrite it in plain lua, that's ok, but you'll need to update the Makefile to stop it from overwriting your work!

no attempt is made to validate the crow standard libs before 'make' creates binaries. while it's not the role of l2h to do full testing of these libraries, it is useful to have l2h validate the code as at least able to be parsed. all that should be necessary is to use loadfile() on the .lua file and return an error if it fails to parse.

  • return with error if lua code has syntax errors

currently the lua files are simply copied, line-by-line, into a c-header file. in particular this means that comments & whitespace are taking up space in flash, and must be parsed out by the lua interpreter at runtime. importantly, once the lua code is flashed onto the chip there is no way to retrieve it, so it doesn't need to stay human readable.

we can reduce flash usage, and RAM overhead at load time the following ways. top of the list is easiest & biggest impact, while end of list is either difficult or has only a small benefit:

  • remove comment lines
  • remove empty lines
  • strip in-line comments
  • strip leading whitespace per line
  • don't add a newline character at end of each line unless necessary (is it ever?)
  • remove all other non-functional whitespace
  • rename local arguments with known scope to single-character names
  • precalculate known constants
    note! most of the above is done by the lua interpreter / compiler (ie luac), so perhaps a more lucrative use of time is to find a way to upload precompiled lua bytecode, rather than raw lua strings. if taking this approach, we should make sure to compare binary sizes as bytecode can apparently be larger than textual representation.

currently the crow standard library enforces using the single-quote ' character for indicating strings or chars, or the double-brak [[ and ]] form (though this is rarely used). when these libraries are wrapped into c-headers with l2h each line is wrapped in a " so the c-compiler can parse them as strings. this means that if a user writes lua code that uses the " to denote a string, the resulting c-header file will not be correctly formed. a big step in the right direction would simply be to parse for the " character in the lua source, and add a leading \ to escape it in C.

  • escape doublequote " characters in lua source

asl support for 'held' is unimplemented

since switching to coroutines approach, asl no longer supports the ability for a asl:bang(false) (ie low going trigger) to jump directly to the release phase.

at present the held construct just checks if it should skip the held brace when starting it.

print current flash to REPL

c command to print contents of flash.

can be used by "maiden" to "download" file from crow to local file system

teletype support for crow

crow is able to be queried by another i2c leader device. teletype is the clear companion device who would lead crow, so the TT project must be updated with hooks for the actions.

This should be implemented for a single crow device first.

Afterwards we can consider how to handle multiple crow devices. This could be sequential input/output numbers (ie CROW.OUT 5 would refer to the first output of the second device, vs CROW.2.OUT 1 or CROW.OUT 2 1 etc).

Proposed initial functions are listed in lua form: https://github.com/trentgill/crow/blob/aa45d96a322f6c818e84a98bc2d69b58a189ed45/lua/ii.lua#L38
and TT form: https://github.com/trentgill/crow/blob/aa45d96a322f6c818e84a98bc2d69b58a189ed45/lua/ii.lua#L67

All the generic functions should send /receive int16 values.

RAM usage analysis after loading lua libs

due to concerns about using too much RAM with loaded lua code, there should be a functionality to check (in debug builds?) the amount of RAM currently free.

this could be called in the C component of dofile()

int s;
int tos = (int)&s;
int* h = malloc(sizeof(int));
int boh = (int)h;
U_Print("ram left "); U_PrintU32(tos - boh);
free(h);

Synthesis

Fixed architecture dsp functionality.

Likely needs to be a global setting to switch the module into acting as a full polysynth.

(Triangle (ramp/waveshape) -> Filter | Noise -> Amplifier). 4-voice goal. Making a 2op FM option would be maximum suggested spec.

Smoothing on the control-rate inputs (from ADCs, or MIDI, or via USB).

LFO and Envelopes should be provided, potentially by the ASL language if that's possible. Might require something far weirder under the hood, so just start with the basics here.

Use as much from JF as will fit on the flash / in ram / in cpu time.

last feature on the sales pitch hah.

refine calibration engine

  • add lua hook to print the current calibration values
  • add lua hook to recalibrate the io
  • print a warning over TTY saying to make sure no cables attached. also upon success
  • add zero-check for second input channel (currently just copied from adc0 )
  • test accuracy of v8 once we have real CV examples running
  • add bounds checks to confirm the data is correct. return error to lua if not (could be a cable attached)
  • as it only happens in factory, increase timing, add delays for settling time, make it more robust

serial stream abort character

considering using a character which signifies that the preceding received bytes should be trashed.

ie, on connection say some other service tries to query the device. there will be garbage bytes in the buffer for example "23409j3sfd"

when a crow client connects, it'll try to id the crow by sending "_c.summon()" or whatever, but the REPL will try to execute "23409j3sfd_c.summon()" and throw an error. of course the client can retry. but perhaps it makes sense that whenever it seems ESC (code 27) it trashes the input buffer (resets position to 0?) whereupon the next command will get executed when a newline comes in.

i haven't looked at the crow code yet, just thinking out loud while working on the crow-command-line-client

ii follower commands for crow

https://github.com/trentgill/crow/blob/b2d5f22be6f6639a3cf1eaf6bebc0865c440a421/lua/ii.lua#L36 proposes a set of crow ii commands (as follower). it provides the basics of input / output, in as similar a way as possible to teletype (so it can be used as an expander with almost zero mental overhead).

then also the generic .CMD functions create a generic interface to get data into/out of crow. the implementation is entirely up to the user. all that is predetermined is the number of args (with options up to 4 args), which the user can then deal with in their crow script. eg. CW.CMD2 could be used to execute set_lfo( channel, speed )

CW.CMD2 could alternatively be used in dispatch_ii( action, argument ), meaning that the first arg actually chooses between a (potentially giant) list of actions.

obviously this allows the user to create utterly undecipherable TT scripts. this seems like a downside, but it also seems like a massive boon for the creative possibilities. the idea that TT can trigger 'some event' with an ii call to crow seems essential, and i'd prefer not to have to codify all the possibilities of what that event should be.

//

an aside: moving to a multi-leader i2c setup suggests a set of new TT decision questions not yet engaged with. specifically, "what functionality should TT provide as an i2c follower?". the immediate parallel to the above proposition is to allow calls to IN, OUT but also SCRIPT.

food for thought!

Use on-board RNG to redefine math.random()

stm32f7 has onboard random number generator.
create a LL driver with a single external function to get random number (float?)
export getter into lua env
redefine math.random to query that getter, rather than the pseudo-random generator requiring a seed.

//

note the RNG can't deliver values over DMA, only interrupt (i think), so keep a queue of 16(?) values & set a flag that requests a new value whenever one is used. this means the getter can be instant, and a script can call math.random() repeatedly without worrying about the same value being returned, or needing a pseudo-random alternative.

add versioning with reporting

call something short,
print(crow.version)
returns the version number led by 'crow'
crow 0.0.0

useful for autodetection for when you are querying uarts and seeing if they come back with the right response (ie now i know this is a crow)

^^append command

add functionality to append to an existing user-script, rather than requiring the whole thing to be uploaded at once. this way a user can extend their script on the fly without having to worry about the full script.

maybe this is not a good idea?

input stream causes crash if called in init script

currently the input 'stream' only works if sent as a lua chunk after boot. including it in default.lua causes the system not to boot.

hypotheses:

  • sending commands to usb host before connecting overflows buffer?
  • adc timer is being activated before appropriate interrupts are set?

Calibration engine

hardware works.
needs to have sequential testing steps.
should be called during testing, and optionally called from lua at runtime (though should be unnecessary).

nested ASL constructs don't work

run make tests to see the errors that occur in the ASL library.

basically the coroutine handling doesn't work correctly and is causing nested structures to only execute one step per resume. they should be exhausted before proceeding.

i2c tables

  • functions/tables to abstract i2c functionality: list of devices (ansible, ww, etc), lists of functions per device (set position, set cv value, etc)
  • functions to print table of devices, table of functions per device (self-documenting)

ii autoloads module's get/set functions as needed

the initial version requires the user to 'require' the table for their module to be remote-controlled:
require 'JF'

the design would mean a call to eg: ii.jf.transpose() would fail if the above hadn't been required.

we fundamentally require this now because their is a hard RAM limit and loading all of the helper for all of the potential i2c clients would use far too much.

instead, we can hide this limitation from the user, making it appear as though all of the tables are always loaded. whenever there is a call to ii.jf.___ and the function is not found, we lookup if the jf table is nil (if it's not, then it suggests a typo), but if it is, we first require 'JF' before executing the code.

this means that the RAM is only used after the user first uses a function from that subset even though it appears as though all the functions are always available.

//

note: this requires ii..listcommands() to be implemented in C, printing a c-string (from a header file) as the contents. or perhaps this string/array is passed back to lua for printing/matching.

Slope generation should be timebased (not convergence)

Currently slopes last until they converge with their destination. This means 'wait for time x' is not possible.

Instead use an explicit timing mechanism which counts samples before a callback. Will make callback detection much simpler (and possible at control-rate).

Could also add sub-sample callback detection with an 'overflow' amount to be applied on the new callback.

I2C network query protocol

Leaders can ping the network requesting all devices to signal their presence. The response should include:

  • I2C address of the device
  • Whether the device is a leader/follower/both

Main use-case is for multiple self-organizing crows (and W/s) to communicate without requiring any explicit setup in a user-script. See issue #5.

What are the other use-cases where this functionality is valuable?

This feature clearly suggests a large amount of additional work beyond crow as other modules must support the query on the global broadcast channel. That is a large number of devices now (>15?) so I hope the response-to-ping behaviour can be relatively simple, thus simple to implement.

queryable help functions

> midi.showevents() -- midi.help() -- m.help()
crow.m.note_on = function( note, velocity, channel )
  — handle a note_on
end

…

> input.showevents() -- input.help()
crow.input.change = function( channel, direction )
  — handle a detected rising/falling event
end
crow.input.stream = function( channel, value )
  — handle a stream of values read from the CV input jack
end
...

the above could be called with m.help() or input.help() and also give a line or two of how to use it. this kind of locally stored documentation seems like a great way to minimize the need for being connected to the internet when you’re already somewhat familiar with how it works. i can implement those things in C so they don’t have RAM implications, just flash. obviously they need to be as short as possible so they don’t flood the repl and take ages to send over the pipe.

of course we could add a help() global function which prints out how to use the via-repl-help functions and gives a list of the different modules that are available. this stuff seems like it could happen quickly once we have the readline app working, as i’m sure this ideas will need to be massaged into working in that context, vs working in maiden (is that how a user will talk to crow for norns?).

> help()
--- CAW! this is crow help.
-- crow has the following modules loaded:
_c -- crow standard library
input -- CV input
out -- CV output
m -- MIDI input
metro -- Timers & metronomes
asl -- 'a slope language'
asllib -- standard slopes

-- you can find out available functions by typing <module_name>.help(), like this:
input.help()

Readline-based lua script for more sophisticated terminal UX

minicom / screen is current solution which is sufficient for proof-of-concept, but is a pretty bad interface for real usage (plus requires setup knowledge of those programs).

making a small lua script (or if not possible, c application), would enable:

  • Readline functionality (command history, auto-complete(?))
  • automatic com port setup to match the known stm32 config
  • can auto-discover the appropriate /dev/tty* by inspecting device IDs

then optimizations / ease of stm32 development

  • commands are sent as packets, not individual chars (makes the parser on stm32 much simpler / faster)
  • 'enter_bootloader' and other sys commands can be sent as a single reserved char for easy marshaling
  • (future) possibility for optimizing data-transfer using tokens rather than raw strings
  • (future) potential for host-provided documentation lookup, linting etc

lua COM port interfaces (both very old): https://github.com/edartuz/lua-serial, http://lua-users.org/wiki/SerialCommunication

lua FLASH script

normal EVAL mode is just REPL. takes a string and then evaluates it upon completion.

functions to enable flash writing:

  • erase flash
  • start flash chunk
  • (receive a lot of strings that are not eval'd)
  • stop flash chunk
  • (chunk is then evaluated for errors(loadstring()), then written to flash, then executed if it checks out(pcall())

allow transmission of long-lua-chunks for immediate execution

currently there is a hard limit on the size of an immediate-execution lua chunk sent over TTY.

the limit is set by the reader[] buffer size in caw.c, which is itself set by the USB_RX_BUFFER define. as of this writing it's 1024 bytes.

#define USB_RX_BUFFER 1024

in order to extend this capability we either need to increase the buffer size, or dynamically allocate memory as the buffer reaches the limit.

please discuss.

metro

  • 8 hardware timers
  • interfaced to metro.lua (identical to norns)

Lua globals protection

Need a solution to make sure the user can't create a script (saved in flash as the default) that makes the lua environment unresponsive, thus being unable to change the script.

Ideas:

  • make certain functions in a protected table
  • make a copy of said functions and test for equality to the runtime versions.

These would include a small number of functions that would break script-updating (dostring(), usb2repl() etc).

ASL as a language for describing patterns

ASL stands for 'a slope language', but our entire focus on slopes is their close correlation with the representation of musical expressions. Yes 'LFO' and 'ADSR' are of course covered, but ASL can also describe 'crescendo' or 'melodic contour'.

A number of extensions to ASL are already proposed to move toward these ideas:

  • 'absolute shapers' allowing outputs to quantized to a user defined musical sequence
  • separation (and later composition) of 'actions', 'pulses' and 'offsets'

A primary concern is thinking about how the syntax or lexical structure of ASL could be refined to speak more directly to melody and rhythm.

It would require some changes to the implementation (and perhaps introduce some limitations), but running ASL at audio-rates could be conceptually interesting. Perhaps this requires further changes to the syntax as well? The idea is that of 'algorithmic waveforms' where modulations are built-in to the waveform descriptor.

Consider a waveform that is a simple triangle, where the 'top' of the triangle is moved about within the period of the waveform. this results in sawtooth through ramp sounds. typically such a oscillator is custom built with this behaviour (see Just Friends etc), then that point can be modulated by control-voltage or some other algorithm. I propose it would be interesting to control the location of that point with an algorithm. Using ASL, that algorithm is effortlessly wrapped in a closure that calculates a new location upon each repetition.

Of course this above idea can then be generalized such that arbitrary waveforms can be created with an arbitrary number of modulation points. The real key is that the modulation is as much a part of the description as the frequency and amplitudes of points. Thus we can say that the modulation is a component of the waveform itself.

This is the key to 'algorithmic waveforms'. That is, waveforms where their behaviour changes over time according to some context. The logical extension of algo-waves is that of 'behavioural waveforms' or the category of 'behavioural synthesis'. In this case the aforementioned 'context' would be based on an 'environment' shared across the synthesis platform.

global reset

command to RESET crow

  • clear/reset global state _G to startup condition (pre flash execution = blank slate)
  • stop metros
  • reset ADC and DAC activity, midi state, etc

DAC spi driver takes too long to Tx

after some pin flipping, appears that the time is spent enabling the interrupt (actually masking the bit in the SPI register!).

takes ~23uS to call HAL_SPI_Transmit_IT() or HAL_SPI_Transmit_DMA() (dma actually takes slightly longer)

SCLK is running at 7MHz. this seems to be a limitation in the HAL driver, as it can only operate at RCC/8 while using HAL for some reason.

//

there's no reason that the IRQ needs to be enable/disabled between each transmission, as the callback only occurs when data is being sent (or fails to send).

in order to enable the IRQ we would have to use the LL driver instead of HAL because the enable/disable happens inside the HAL handling which we don't want to change.

//

instead of figuring out the LL driver, it would likely be much faster to just call the SPI transfer in blocking mode using HAL_SPI_Transmit() and just wait for the transfer to complete.

this will block processing longer than a good interrupt solution, but because it's such a short packet, even at 7MHz the packet time is only 3.2uS! that's almost a x10 improvement over the standard HAL_IT() implementation.

worth a shot!

should get beyond the 40kHz rate for 4 channels which is all we're after to do decent audio. then a variable-samplerate implementation would allow this to dip if it needs to.

Bootloader process with soft-reboot

Lua should be able to call into the bootloader directly.

the main program must set a flash register to tell the bootloader to stay in DFU mode until it successfully receives a program.

//
There is an edge case where the DFU fails upload, but tries to launch the applicaation anyway (unsetting the bootloader bit), even though the application fails. this means the user could not re-enter bootloader mode to fix the program, effectively bricking the module.

A solution would be that the flash bit is not unset, until after the bootloader finishes, and yields to the main application, after some sequence of successful startup, then unsetting the bootloader bit (having accepted that the program is sufficiently correct).

i2c chaining

  • command to set offset (chain position, ie cv in/out count) which sets i2c address
  • query index
  • LATER cross-crow commands ie send lua from one crow to another
  • i2c command issuing global reset to another crow

ie:

  • hook up usb host to crow#2, set position to 2
  • switch usb to crow#1
  • send cv(5, value) and cv command is passed via i2c to crow#2 (which has cv position 5)

asl: add thread{} operator

breaks the operator precedence basic rules, which were discovered through the currently broken precedence behaviour.

take the following asl:

loop{
  loop{ toward(0)
    , toward(1)
    }
  , toward(3,3)
  }

the expected behaviour here is that the inner loop of toward(0), toward(1) would never end. the toward(3,3) would not be reached because the inner loop wouldn't exit.

conversely, if we change the outer loop{} to a thread{} the control flow is switched such that the inner loop is not able to take over the control flow. instead the sequence will be:

toward(0)
toward(3,3)
toward(1)
toward(3,3)
toward(0) --etc

this allows the creation of algorithmic cycles by threading multiple loops with different stage counts. eg:

thread{
  loop{ toward(1,1), toward(2,2), toward(3,3 },
  loop{ toward(4,4), toward(5,5), toward(6,6), toward(7,7), toward(8,8) } }

this program will create a sequence 35 stages long, but only requires describing 8 events. consider the possibility of threading a loop{}'d sequence of delay times, with a loop{}'d sequence of instant jumps, where the sequences are different lengths. this would have the effect of the cv sequence being cyclical, but the rhythm of the sequence cycling over a number of repetitions.

I2C descriptor improvements

  • Remove requirement that lua_name match filename
  • Fractional types for args/retvals
  • Mapping functionality so fract13 can be mapped to [-5.0,5.0]
  • Multiple return value support (eg. to return both num/denom jf's 'retune' parameter)

Would help to rework the util/ii_gen.lua script to load all the files first & generate copies of 'cmd' into 'getters' section, rather than special case on them constantly. then it would be easier to make custom iterators for each segment.

MIDI hardware

Requires hardware extension with an optocoupler. Breadboard this with the generic optocouplers, then deadbug onto the module (transistor as buffer).

MIDI input

Requires the user to call a global function activating midi mode. this could / should deactivate the ADC on channel one, and definitley deactivate any callback set for that channel.
Might be able to auto-detect midistate by leaving the UART activated, and asking the user to 'press any key' on their device, then detect this as uart data.

UART should be on it's own channel to be independent of the uart-debugger.

probably just want to have a lua callback for any midi message, and likely just copy the midi-input library directly from norns (or include it directly).

expose some helper functions to the user that simplify getting notes vs. ccs vs. sync.

ADC interface methods

  • finish design
  • implement

design notes

  • each input is independent, can be set up with different attributes
  • basic access via QUERY ie get_adc(1) returns adc(1,value)
  • POLL. set an interval. sends data at this interval. start/stop. (hard timer for each of these)
  • HYSTERESIS/window/comparator. send an event based on a range/threshold setting, positive/negative/both edges. aka onCHANGE
  • quantization? could/should the hysteresis be abstracted outside of just the onCHANGE reporting?
  • filter? slew/lag the input ie, become an envelope follower?

How to configure Makefile pre-requisites

Currently make can fail for a number of reasons, and can need to be run multiple times before success, or the user may need to run the lua util/ii_gen.lua helper by hand. I've spent some time trying to configure pre-requisites but run into issues because of the way $(OBJS) is a variable not a recipe.

The current build process in make-ese is something like:

layer1: util/l2h.fnl
  fennel --compile util/l2h.fnl -> util/l2h.lua
  mkdir build

layer2: $(layer1) util/ii_gen.lua 
  lua util/ii_gen.lua # builds all the generated i2c files from $(II_SRC)

layer3: $(layer2) util/l2h.lua
  lua util/l2h.lua $(LUA_SRC) # wraps all the lua files in c headers

$(EXECUTABLE): $(layer3) $(OBJS)
  # everything works from here on

I'm guessing i have something subtly wrong with my understanding of how make handles prerequisites. Hoping someone more knowledgeable than me has an answer!

flocking algo

not even a standard library idea, but just a general idea for a crow script:

IN1 takes a v8 pitch
IN2 takes a 'speed' setting

OUT1-4 provide a 'flock' of cvs that follow & move around the input signal. could be a nice way to make more interesting 'detune' sounds than with a simple static or random spread. also has an interesting time-constant effect.

furthermore, if this were then processed by a quantizer it could be an alternate approach to the ASR algorithm.

Confirm I2C addresses to use

I arbitrarily chose the i2c addresses as 0x78 through 0x7B.
Couldn't find an exhaustive list of the occupied addresses (though i do remember seeing one). Thinking the choice of i2c address could double as the 'which crow' number when multiple units share a single usb connection.

Perhaps @scanner-darkly has ideas here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.