Kirk 10389821 wrote: Finally, your problem reminds me of a Computer Engineering Class, where we built circuits that were run through a simulator. The simulator used a queue design, where "events" would trigger through the queue, and the simulator was able to be fast, because it ignored the timing signals, allowing it to "not wait" any time before processing an item. (I got in trouble in the class, because I wrote obscenely inefficient but SIMPLE code, reducing the homework to a TRIVIAL problem, avoiding the timing issues others were busy coding around).
anyways, I could envision a queue that is managing N queues of inputs, and only when you take off an item, do you go to the stream to pull in another item.
You're describing the problem pretty well, which I actually solved last night. I won't paste the implementation here, but here is using it with a queue q
Forgive the grotty code. It's just test stuff I've been banging on
#ifndef ARDUINO
#include <sfx_midi_file.hpp>
#include <sfx_midi_clock.hpp>
#include <sfx_midi_utility.hpp>
#include <queue>
#include <stdio.h>
#include <math.h>
using namespace sfx;
void dump_midi(stream* stm, const midi_file& file) {
printf("Type: %d\nTimebase: %d\n",(int)file.type,(int)file.timebase);
printf("Tracks: %d\n",(int)file.tracks_size);
for(int i = 0;i<(int)file.tracks_size;++i) {
printf("\tOffset: %d, Size: %d, Preview: ",(int)file.tracks[i].offset,(int)file.tracks[i].size);
stm->seek(file.tracks[i].offset);
uint8_t buf[16];
size_t tsz = file.tracks[i].size;
size_t sz=stm->read(buf,tsz<16?tsz:16);
for(int j = 0;j<sz;++j) {
printf("%02x",(int)buf[j]);
}
printf("\n");
}
}
void dump_midi(const midi_message& msg) {
switch(msg.type()) {
case midi_message_type::note_off:
printf("Note Off: %d, %d\n",(int)msg.lsb(),(int)msg.msb());
break;
case midi_message_type::note_on:
printf("Note On: %d, %d\n",(int)msg.lsb(),(int)msg.msb());
break;
case midi_message_type::polyphonic_pressure:
printf("Poly pressure: %d, %d\n",(int)msg.lsb(),(int)msg.msb());
break;
case midi_message_type::control_change:
printf("Control change: %d, %d\n",(int)msg.lsb(),(int)msg.msb());
break;
case midi_message_type::pitch_wheel_change:
printf("Pitch wheel change: %d, %d\n",(int)msg.lsb(),(int)msg.msb());
break;
case midi_message_type::song_position:
printf("Song position: %d, %d\n",(int)msg.lsb(),(int)msg.msb());
break;
case midi_message_type::program_change:
printf("Program change: %d\n",(int)msg.value8);
break;
case midi_message_type::channel_pressure:
printf("Channel pressure: %d\n",(int)msg.value8);
break;
case midi_message_type::song_select:
printf("Song select: %d\n",(int)msg.value8);
break;
case midi_message_type::system_exclusive:
printf("Systex data: Size of %d\n",(int)msg.sysex.size);
break;
case midi_message_type::reset:
if(msg.meta.data==nullptr) {
printf("Reset\n");
} else {
int32_t result;
const uint8_t* p=midi_utility::decode_varlen(msg.meta.encoded_length,&result);
if(p!=nullptr) {
printf("Meta message: Type of %02x, Size of %d\n",(int)msg.meta.type, (int)result);
} else {
printf("Error reading message\n");
}
}
break;
case midi_message_type::end_system_exclusive:
printf("End sysex\n");
break;
case midi_message_type::active_sensing:
printf("Active sensing\n");
break;
case midi_message_type::start_playback:
printf("Start playback\n");
break;
case midi_message_type::stop_playback:
printf("Stop playback\n");
break;
case midi_message_type::tune_request:
printf("Tune request\n");
break;
case midi_message_type::timing_clock:
printf("Timing clock\n");
break;
default:
printf("Illegal message: %02x\n",(int)msg.status);
while(true);
}
}
using midi_queue = std::queue<midi_stream_event>;
int main(int argc, char** argv) {
midi_clock mclock;
static const char* def = "data\\sonata.mid";
const char* sz;
if(argc<2) {
sz = def;
} else {
sz=argv[1];
}
file_stream fstm(sz);
midi_file f;
midi_queue q;
sfx_result r=midi_file::read(&fstm,&f);
if(sfx_result::success!=r) {
printf("Error opening file: %d\n",(int)r);
return (int)r;
}
dump_midi(&fstm,f);
fstm.seek(0);
midi_file_source msrc;
struct mstate {
midi_clock* clock;
midi_queue* queue;
midi_file_source* source;
};
midi_stream_event* queue = (midi_stream_event*)calloc(4,sizeof(midi_stream_event));
if(queue==nullptr) {
printf("out of memory");
return (int)sfx_result::out_of_memory;
}
mstate st;
st.clock = &mclock;
st.queue = &q;
mclock.tick_callback([](uint32_t pending,unsigned long long elapsed,void* state){
mstate st=*(mstate*)state;
while(true) {
if(st.queue->size()) {
const midi_stream_event& event=st.queue->front();
bool first = true;
if(event.absolute<=elapsed) {
if(event.message.type()==midi_message_type::meta_event && event.message.meta.type==0x51) {
int32_t mt = (event.message.meta.data[0] << 16) | (event.message.meta.data[1] << 8) | event.message.meta.data[2];
printf("Set tempo to %f\n",midi_utility::microtempo_to_tempo(mt));
st.clock->microtempo(mt);
}
printf("delta: %lli - ",(long long)event.delta);
dump_midi(event.message);
event.message.~midi_message();
st.queue->pop();
} else {
break;
}
} else {
break;
}
}
},&st);
r = midi_file_source::open(&fstm,&msrc);
if(sfx_result::success!=r) {
printf("Error opening file: %d\n",(int)r);
return (int)r;
}
mclock.timebase(msrc.file().timebase);
mclock.start();
while(true) {
midi_event e;
if(q.size()>=16) {
mclock.update();
continue;
}
sfx_result r=msrc.receive(&e);
if(r!=sfx_result::success) {
if(r==sfx_result::end_of_stream) {
mclock.update();
if(!q.size()) {
printf("Exiting\n");
break;
}
continue;
} else {
printf("Error receiving message: %d\n",(int)r);
}
printf("Exiting\n");
break;
} else {
q.push({(unsigned long long)msrc.elapsed(),e.delta,e.message});
printf("queue size: %d\n",(int)q.size());
mclock.update();
}
}
}
#endif
To err is human. Fortune favors the monsters.
|