diff -urN visualboyadvance-m-2.1.4-orig/src/common/ConfigManager.cpp visualboyadvance-m-2.1.4/src/common/ConfigManager.cpp
--- visualboyadvance-m-2.1.4-orig/src/common/ConfigManager.cpp	2019-10-02 23:17:30 +0900
+++ visualboyadvance-m-2.1.4/src/common/ConfigManager.cpp	2019-12-27 00:41:06 +0900
@@ -595,6 +595,10 @@
 		gb_effects_config.enabled = true;
 	}
 
+	int soundBufferMax = ReadPref("soundBufferMax", 300);
+	int soundBufferMin = ReadPref("soundBufferMin", 300);
+	soundSetBufferSize(soundBufferMax / 1000.0f, soundBufferMin / 1000.0f);
+
 	if (optFlashSize == 0)
 		flashSetSize(0x10000);
 	else
diff -urN visualboyadvance-m-2.1.4-orig/src/common/SoundDriver.h visualboyadvance-m-2.1.4/src/common/SoundDriver.h
--- visualboyadvance-m-2.1.4-orig/src/common/SoundDriver.h	2019-10-02 23:17:30 +0900
+++ visualboyadvance-m-2.1.4/src/common/SoundDriver.h	2019-12-27 00:37:19 +0900
@@ -35,8 +35,10 @@
         /**
          * Initialize the sound driver.
          * @param sampleRate In Hertz
+         * @param bufferMax Output buffer size in seconds
+         * @param bufferMin Output flush threshold in seconds
          */
-        virtual bool init(long sampleRate) = 0;
+        virtual bool init(long sampleRate, float bufferMax, float bufferMin) = 0;
 
         /**
          * Tell the driver that the sound stream has paused
diff -urN visualboyadvance-m-2.1.4-orig/src/common/SoundSDL.cpp visualboyadvance-m-2.1.4/src/common/SoundSDL.cpp
--- visualboyadvance-m-2.1.4-orig/src/common/SoundSDL.cpp	2019-10-02 23:17:30 +0900
+++ visualboyadvance-m-2.1.4/src/common/SoundSDL.cpp	2019-12-27 00:50:07 +0900
@@ -24,9 +24,6 @@
 
 extern int emulating;
 
-// Hold up to 300 ms of data in the ring buffer
-const double SoundSDL::buftime = 0.300;
-
 SoundSDL::SoundSDL():
     samples_buf(0),
     sound_device(-1),
@@ -110,10 +107,16 @@
     samples_buf.write(finalWave, samples * 2);
 
     SDL_UnlockMutex(mutex);
+
+    if (samples_buf.used() >= writeThreshold) {
+	SDL_SemPost(data_available);
+	if (should_wait())
+	    SDL_SemWait(data_read);
+    }
 }
 
 
-bool SoundSDL::init(long sampleRate) {
+bool SoundSDL::init(long sampleRate, float bufferMax, float bufferMin) {
     if (initialized) deinit();
 
     // no sound on windows unless we do this
@@ -142,7 +145,11 @@
         return false;
     }
 
-    samples_buf.reset(std::ceil(buftime * sampleRate * 2));
+    this->bufferMax = bufferMax;
+    this->bufferMin = bufferMin;
+    bufferSize = std::ceil(bufferMax * sampleRate * 2);
+    writeThreshold = std::ceil(bufferMin * sampleRate * 2);
+    samples_buf.reset(bufferSize);
 
     mutex          = SDL_CreateMutex();
     data_available = SDL_CreateSemaphore(0);
@@ -187,7 +194,7 @@
 void SoundSDL::resume() {}
 
 void SoundSDL::reset() {
-    init(soundGetSampleRate());
+    init(soundGetSampleRate(), bufferMin, bufferMax);
 }
 
 void SoundSDL::setThrottle(unsigned short throttle_) {
diff -urN visualboyadvance-m-2.1.4-orig/src/common/SoundSDL.h visualboyadvance-m-2.1.4/src/common/SoundSDL.h
--- visualboyadvance-m-2.1.4-orig/src/common/SoundSDL.h	2019-10-02 23:17:30 +0900
+++ visualboyadvance-m-2.1.4/src/common/SoundSDL.h	2019-12-27 00:48:41 +0900
@@ -28,7 +28,7 @@
         SoundSDL();
         virtual ~SoundSDL();
 
-        virtual bool init(long sampleRate);
+        virtual bool init(long sampleRate, float bufferMax, float bufferMin);
         virtual void pause();
         virtual void reset();
         virtual void resume();
@@ -53,6 +53,10 @@
         SDL_AudioSpec audio_spec;
 
         unsigned short current_rate;
+	float bufferMax;
+	float bufferMin;
+	int bufferSize;
+	int writeThreshold;;
 
         bool initialized = false;
 
diff -urN visualboyadvance-m-2.1.4-orig/src/gba/Sound.cpp visualboyadvance-m-2.1.4/src/gba/Sound.cpp
--- visualboyadvance-m-2.1.4-orig/src/gba/Sound.cpp	2019-10-02 23:17:30 +0900
+++ visualboyadvance-m-2.1.4/src/gba/Sound.cpp	2019-12-27 00:49:46 +0900
@@ -42,6 +42,8 @@
 
 static uint16_t soundFinalWave[1600];
 long soundSampleRate = 44100;
+float soundBufferMax = 0.3;
+float soundBufferMin = 0.3;
 bool soundInterpolation = true;
 bool soundPaused = true;
 float soundFiltering = 0.5f;
@@ -504,6 +506,12 @@
     return soundVolume;
 }
 
+void soundSetBufferSize(float bufferMax, float bufferMin)
+{
+    soundBufferMax = bufferMax;
+    soundBufferMin = bufferMin;
+}
+
 void soundSetEnable(int channels)
 {
     soundEnableFlag = channels;
@@ -534,7 +542,7 @@
     if (!soundDriver)
         return false;
 
-    if (!soundDriver->init(soundSampleRate))
+    if (!soundDriver->init(soundSampleRate, soundBufferMax, soundBufferMin))
         return false;
 
     soundPaused = true;
diff -urN visualboyadvance-m-2.1.4-orig/src/gba/Sound.h visualboyadvance-m-2.1.4/src/gba/Sound.h
--- visualboyadvance-m-2.1.4-orig/src/gba/Sound.h	2019-10-02 23:17:30 +0900
+++ visualboyadvance-m-2.1.4/src/gba/Sound.h	2019-12-27 00:35:34 +0900
@@ -18,6 +18,11 @@
 void soundSetVolume(float);
 float soundGetVolume();
 
+// Manages sound output buffer size
+// Up to bufferMax seconds will be buffered; buffered data will be sent
+// to the output device until less than bufferMin seconds remain
+void soundSetBufferSize(float bufferMax, float bufferMin);
+
 // Manages muting bitmask. The bits control the following channels:
 // 0x001 Pulse 1
 // 0x002 Pulse 2
