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

Question about your code #1

Closed
goofy2k opened this issue Nov 19, 2021 · 17 comments
Closed

Question about your code #1

goofy2k opened this issue Nov 19, 2021 · 17 comments

Comments

@goofy2k
Copy link

goofy2k commented Nov 19, 2021

Hi @ncassetta , I have been working on using jdksmidi in a soundboard project. I have difficulties in implementing the original jdksmidi code, so I have been studying it.

Maybe you can help me a bit.....

In the jdksmidi code, incoming messages are stored in a circular in_queue (MIDIQueue). I expect that, e.g. when recording, these messages must be transferred to the actual track that can be played. In jdksmidi I did not discover where this is done. I would expect that the messages received must be put in order of time that they should be played. In jdksmidi track.cpp/.h I see routines for sorting a track. There is NO routine for inserting (transfering messages from the in_queue to a track). I would think that this repetitive task should be executed in a MIDITick routine.

What are your thoughts about this? How is this implemented in your code.

Thanks

@goofy2k
Copy link
Author

goofy2k commented Nov 19, 2021

I now see that in recorder.cpp you made a remark: // TODO: perhaps it is possible to write a Sequencer::InsertEvent() method

This probably relates to my question.

The point is that while e.g. playing a loop and at the same time feeding MIDI events to the system you need to have a way to safely enter them into a track while playing goes on. I think jdksmidi solves this by adding received events in in_queue. The only two time-critical thing that you have to do fast are: providing a timestamp of the time of receipt and sending to an output (if MIDI thru is enabled). Then, you have the complete length of the recording loop to insert the event into the right place in to a track. I did not find this action in the jdksmidi code.

@ncassetta
Copy link
Owner

ncassetta commented Nov 20, 2021

Hi @goofy2k, jdksmidi is very old code, I ported it to NiCMidi. However, as you have seen, it did not allow the user to record: the MIDIDriver class could only receive messages and put them in its internal queue, not in a MIDITrack.
In NiCMidi I organized things like this:

  • the MIDIInDriver class manages a queue of MIDIRawMessage objects coming from the hardware port: these are structs composed of
    • a MIDIMessage object
    • a timestamp (in msecs)
    • the number of the MIDI in port.
  • the MIDIRecorder class gets these messages, transforms them into MIDITimedMessage objects (assigning them a time in MIDI ticks) and puts them into MIDITrack objects.

It must be associated with a MIDISequencer (given in the constructor). As I wrote in a comment, I initially tried to add messages directly to the sequencer tracks, but this gave trouble because it changed the tracks while they were playing. So I had to copy the sequencer tracks into the recorder (in the Start method), write to them (in the TickProc method executed every tick of the timer) and then copy them back into the sequencer (in the Stop method).
The MIDIRecorder object is still rather basic and does not allow, for example, to record in loops.
You can examine the test_recorder.cpp sample file in the examples folder to have an idea of the MIDIRecorder usage.

@goofy2k
Copy link
Author

goofy2k commented Nov 20, 2021 via email

@goofy2k
Copy link
Author

goofy2k commented Nov 20, 2021

At first I tried to add recording / looping capabilities to the board that runs the digital sound processing, but two of those time-critical tasks is too much for a single ESP32. So I decided to divide the tasks over two boards and connect them via Bluetooth.

@goofy2k
Copy link
Author

goofy2k commented Nov 20, 2021

I made the repo for my project public! You can have a look at: https://github.com/goofy2k/MIDI-sequencer

@goofy2k
Copy link
Author

goofy2k commented Nov 20, 2021

The repo contains both apps:

  • the sound board firmware: faust_ble_midi
  • the "sequencer": fckx_sequencer

@ncassetta
Copy link
Owner

@goofy2k
jdksmidi only had hardware support for Windows, not even Linux. So in NiCMidi I used RtMidi for communications with hardware, focusing on sequencing/recording and loading/editing/saving of MIDIFiles.
In NiCMidi the communications with the hardware are managed by the MIDIOutDriver and MIDIInDriver classes: the first has an OutputMessage method (which in turn calls HardwareMsgOut) to send a message to an Out port, while the second has a HardwareMsgIn callback activated when a Midi message comes from an In port. More advanced classes automatically call these methods every tick of the main timer to get playing or recording.
To interface with non-standard ports you probably have to study RtMidi, I don't have much experience with hardware.

@goofy2k
Copy link
Author

goofy2k commented Nov 21, 2021 via email

@ncassetta
Copy link
Owner

ncassetta commented Nov 21, 2021

@goofy2k
What compiler are you using? Are you using the c++ 0x11 flag?

@goofy2k
Copy link
Author

goofy2k commented Nov 21, 2021 via email

@goofy2k
Copy link
Author

goofy2k commented Nov 21, 2021

For the MIDI input I see 2 options: 1) adaptation on the RtMidi side, using openVirtualPort 2) adaptation in your MIDIInput class in driver.h/.cpp

I'll do one of these options first.

After that I will investigate creation of a timer class based on freeRTOS.

Fred

@goofy2k
Copy link
Author

goofy2k commented Nov 22, 2021

In your docs the hierarchy of classes is shown. It is useful to know that a number of classes depend on a single MIDITickComponent. I miss information about how MIDITimer fits into this all. Is MIDITimer a base class for MIDITickComponent? And only for that?

A quick inspection of your code let's me think that only driver, manager and tick depend directly on timer. If it is I can try to design a freeRTOS based MIDITimer class without having to adapt the dependent classes.

@goofy2k
Copy link
Author

goofy2k commented Nov 22, 2021

Can run the example test_component example now (with MIDITickComponent, MIDIManager, MIDITimer). It still has a run-time error at stopping the component. I had to comment out MIDIIn, MIDIout reference. I'll probably wrap my MQTT (in) and BLE (out) code in these classes. Must see if I keep RtMidi. I guess that this causes overhead that is not required. ESP32 memory is limited :-)
Everything available in my repo.....

@ncassetta
Copy link
Owner

No, the MIDITimer is a static class which only (when it is open) starts a background thread which executes its ThreadProc every 10 milliseconds.
The MIDITick class has a callback which is called by the timer procedure, It doesn't interacts wit the MIDITimer and includes the header "timer.h" only for the definition of the type tMsecs.
The MIDIInDriver uses the MIDITimer only in the static HardwareMsgIn method, for timestamp of the incoming messages

@goofy2k
Copy link
Author

goofy2k commented Nov 24, 2021 via email

@goofy2k
Copy link
Author

goofy2k commented Nov 25, 2021

Making some progress :-) Trying to implement the test_recorder example . Discovering some potential bugs. See Issues.

@goofy2k
Copy link
Author

goofy2k commented Dec 9, 2021

just closing, to keep focus on the open issue, which is more a discussion thread :-)

@goofy2k goofy2k closed this as completed Dec 9, 2021
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

2 participants