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

Feature Request: USB Buffer Management/Flush Functionality for MIDI Component #135

Open
517002650 opened this issue Feb 25, 2025 · 2 comments

Comments

@517002650
Copy link

I am currently using your excellent USBComposite_stm32f1 library for a MIDI project on STM32F1. I've encountered an issue where continuous sending of MIDI messages without the host reading them causes the STM32 to freeze after approximately 40 seconds, likely due to buffer overflow.

I've implemented a connection monitoring system that uses the transmission time of Active Sensing messages to detect when the buffer might be full:
.
#include <USBComposite.h>

/**

  • STM32F1 MIDI USB Composite Device with Buffer Management Test
  • This sketch demonstrates a potential issue with the USBComposite library
  • where continuous transmission of MIDI messages without host reading can
  • cause the device to freeze after approximately 40 seconds.
  • The code monitors connection status by checking the time it takes to send
  • Active Sensing messages, which can indicate if the buffer is full.
    */

#define SPEAKER_PIN PA1 // Pin for audio output
#define STATUS_LED_PIN PA0 // LED to indicate connection status

/**

  • Extended MIDI class that implements connection monitoring
  • by measuring the time it takes to send Active Sensing messages
    */
    class myMidi : public USBMIDI {
    private:
    uint32_t lastActiveSensingTime = 0; // Last time we sent an Active Sensing message
    uint32_t lastSuccessfulSend = 0; // Last time we successfully sent a message quickly
    bool connectionActive = false; // Current connection status

public:
/**

  • Handle MIDI Note Off messages - turns off the speaker
    */
    virtual void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {
    noTone(SPEAKER_PIN);
    }

/**

  • Handle MIDI Note On messages - plays the corresponding frequency on the speaker
    */
    virtual void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {
    tone(SPEAKER_PIN, (midiNoteFrequency_10ths[note]+5)/10);
    }

/**

  • Check if the MIDI connection is active
  • This method sends an Active Sensing message (0xFE) every 5 seconds and
  • measures how long it takes to send. If the send operation is quick (<1ms),
  • it assumes the connection is active. If it takes too long (>5ms), it likely
  • means the buffer is full because the host isn't reading data.
  • @return true if connection is active, false otherwise
    */
    bool checkConnection() {
    uint32_t currentTime = millis();
// Send an Active Sensing message every 5 seconds
if (currentTime - lastActiveSensingTime > 5000) {
    lastActiveSensingTime = currentTime;
    
    // Record the time before sending
    uint32_t sendStartTime = micros();
    
    // Send Active Sensing message (0xFE)
    // Active Sensing is a System Realtime message with MIDI code 0xFE
    sendRealTime(0xFE);
    
    // Check how long it took to send
    uint32_t sendDuration = micros() - sendStartTime;
    
    // If sending completed quickly (<1ms), consider connection active
    if (sendDuration < 1000) {
        connectionActive = true;
        lastSuccessfulSend = currentTime;
    } 
    // If sending took too long (>5ms), buffer is likely full
    else if (sendDuration > 5000) {
        connectionActive = false;
    }
}

// If no successful send in the last 10 seconds, consider connection inactive
if (currentTime - lastSuccessfulSend > 10000) {
    connectionActive = false;
}

return connectionActive;

}

/**

  • Get the current connection status
  • @return true if connection is active, false otherwise
    */
    bool isConnected() {
    return connectionActive;
    }

/**

  • Send a MIDI System Realtime message
  • Uses the writePacket method directly from USBMIDI to send
  • the message with minimal overhead
  • @param rt The realtime message code to send (e.g., 0xFE for Active Sensing)
    */
    void sendRealTime(uint8_t rt) {
    // Directly use the writePacket method provided by USBMIDI class
    writePacket(rt);
    }
    };

// Create instances of our MIDI and serial components
myMidi midi;
USBCompositeSerial CompositeSerial;

void setup() {
USBComposite.setProductId(0x0030);
pinMode(SPEAKER_PIN, OUTPUT);
pinMode(STATUS_LED_PIN, OUTPUT);

// Register both MIDI and Serial components to create a composite device
midi.registerComponent();
CompositeSerial.registerComponent();
USBComposite.begin();

}

void loop() {
// Process incoming MIDI messages
midi.poll();

// Send a Note Off message - this continuous sending without host reading
// will eventually fill the buffer and cause the MCU to freeze after ~40 seconds
midi.sendNoteOff(0, 0, 127);

// Check MIDI connection status
bool connected = midi.checkConnection();

// Update LED based on connection status
if (connected) {
digitalWrite(STATUS_LED_PIN, HIGH); // Turn LED on when connected
} else {
digitalWrite(STATUS_LED_PIN, LOW); // Turn LED off when disconnected

// OPTIONAL: Reset USB connection when buffer appears to be full
// Uncomment these lines to test automatic reconnection
/*
USBComposite.end();
delay(1000);
USBComposite.begin();
*/

}
}

@517002650
Copy link
Author

Additional Information About the Issue:

I've discovered an interesting pattern with this buffer overflow problem:

  1. When using a laptop running on battery power (not connected to AC adapter), the device freezes after approximately 40 seconds of continuous MIDI transmission.

  2. However, when any of these conditions are met, the problem disappears:

    • The laptop is connected to AC power
    • A MIDI application is actively reading the incoming MIDI data
    • When using a desktop PC (regardless of whether MIDI data is being read)

This suggests that the issue might be related to USB power management or polling rate changes when laptops enter battery-saving modes. Without active reading from the host, the USB buffer fills up, but this happens much faster on battery-powered laptops, possibly due to reduced USB polling frequency or power-saving features that affect USB endpoints.

I believe a buffer management solution in the library would help address this issue, as it appears to be a specific interaction between power-saving modes on laptops and the USB MIDI buffer management.

@517002650
Copy link
Author

Finally, it was found that it cannot be registered together with the virtual USB serial port. If the virtual serial port is registered together, this problem will occur.

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