Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Rewrite] What is the ideal form of a Sender? #13

Open
Bubobubobubobubo opened this issue Jul 23, 2024 · 0 comments
Open

[Rewrite] What is the ideal form of a Sender? #13

Bubobubobubobubo opened this issue Jul 23, 2024 · 0 comments

Comments

@Bubobubobubobubo
Copy link
Owner

Introduction

Since I started working on Sardine, I have relied quite a lot on fixed form Senders like note, cc and other types. They excel at writing one specific type of message but it would be nicer to be able to compose "chains" or "groups" of operations like this:

p1 >> note(Pnote(0, 2, 4)) && cc(ctrl=Pchoose(5, 10), value=Phuman(50), chan=2) && pc(Prand(1, 10))

We could also think of a way to 'nest' chains like this in order to track many of them with only one player:

p1 >> (
  note(Pnote(0, 2, 4)) && cc(ctrl=Pchoose(5, 10), value=Phuman(50), chan=2) && pc(Prand(1, 10)),
  cc(chan=2, control=20, value=Psaw(1, 20, 40)) & note(60, channel=4)
)

The most problematic thing is to reproduce a 'succession' of patterns, something that would work akin to a track. Maybe we could probably make use of a special Pattern type, that eats other patterns, and manage their iteration cycle:

p1 >> Ptrack(
    note(Pnote(0, 2, 4), p=P(1, 2, 4)),
    note(Pnote(0, 3, 1, 5, 8)),
    note(Pnote(1, 2, 4, 5)
) && cc(chan=2, control=50, value=Psine(1/2, 2, 20)

Each pattern in the inner layer will only loop n times and signal its iteration end, triggering the upper Ptrack that it needs to move forward to the next children. The limit can be the number of specified durations or a fixed number.

The main question for everything is: where should durations be specified? Should each pattern in the chain have its own rhythm? This would be super convenient and flexible but also slightly harder to implement correctly. This syntax comes with some advantages:

  • independant durations: more interesting and intricate rhythms, to the expanse of a p kwarg everywhere (in each pattern).
  • greater composability of I/O operations. A complex instrument is just a complex pattern.
  • iterators are internal properties of the pattern itself, and not of the player!

Implementation

Obviously, everything has to be implemented (again!) by scratching what currently exists. The Player implementation currently assumes that there is a central sender_method and only one recursive function in charge of everything. This has proven to be a gigantic mess for more complex patterns (polyphony, etc).

  1. Write a Stream class, which is what is returned when you compose a chain of operations: note(50) & note(70) or just feed a single chain to a Player: p1 >> note(50).
  2. Write a Pattern class, a complex type of generator that handles infinite bi-directional indexing, also capable of returning a representation of each pattern ready for conversion as a recursive function to run on the clock.
  3. Rewrite the Player class to work with Streams only. Each Player will act more like a manager and central reference system, marking that a Stream and individual patterns belong to a specific identifier so that the player can alter and/or stop each stream.
@Bubobubobubobubo Bubobubobubobubo changed the title [Rewrite] What form a Sender should have? [Rewrite] What is the ideal form of a Sender? Jul 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant