使用SoundPool开发Harmonium App:如何实现按键按住持续发声?
Hey there! I totally get where you're coming from—building a Harmonium app with SoundPool requires a different approach than your piano app, since we need sustained sound on key holds and that "cutoff-when-pressing-another-key" behavior. Let's walk through exactly how to make this work.
Core Concepts to Adjust for Harmonium Behavior
SoundPool is great for short, one-off sounds (like piano keys), but for sustained notes, we need to leverage its looping capability and track active playback streams to manage note cutoffs. Here's the breakdown:
- Loop the sound indefinitely when a key is held
- Track the active stream ID so we can stop it immediately when another key is pressed or the current key is released
- Limit concurrent streams since we only need one note playing at a time
Step-by-Step Implementation
1. Initialize SoundPool with Proper Configuration
First, set up SoundPool to handle looping and single-stream playback (perfect for harmonium's single-note-at-a-time behavior):
private SoundPool soundPool; private HashMap<Integer, Integer> soundIdMap = new HashMap<>(); private int activeStreamId = -1; // Tracks the currently playing note private boolean isSoundsLoaded = false; // Ensures we don't play unloaded sounds @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initialize SoundPool (compatible with old and new Android versions) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { AudioAttributes audioAttrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); soundPool = new SoundPool.Builder() .setMaxStreams(1) // Only allow 1 active stream at a time .setAudioAttributes(audioAttrs) .build(); } else { soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0); } // Load your harmonium sound files (map each key view ID to its sound) soundIdMap.put(R.id.key_do, soundPool.load(this, R.raw.harmonium_do, 1)); soundIdMap.put(R.id.key_re, soundPool.load(this, R.raw.harmonium_re, 1)); soundIdMap.put(R.id.key_mi, soundPool.load(this, R.raw.harmonium_mi, 1)); // Add all your keys here // Wait for sounds to finish loading before allowing input soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> { isSoundsLoaded = true; }); // Attach touch listeners to all your keys setupKeyListeners(); }
2. Implement Key Touch Logic for Sustained Playback
Create a touch listener that handles ACTION_DOWN (start looping the note) and ACTION_UP (stop the note), plus cuts off the active note when a new key is pressed:
private void setupKeyListeners() { // Example for one key—repeat this for all your harmonium keys Button keyDo = findViewById(R.id.key_do); keyDo.setOnTouchListener(this::handleKeyTouch); Button keyRe = findViewById(R.id.key_re); keyRe.setOnTouchListener(this::handleKeyTouch); // Add the rest of your keys here } private boolean handleKeyTouch(View view, MotionEvent motionEvent) { if (!isSoundsLoaded) return false; // Don't do anything until sounds are ready int soundId = soundIdMap.get(view.getId()); switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: // Stop the currently playing note (if any) before starting a new one if (activeStreamId != -1) { soundPool.stop(activeStreamId); } // Play the sound in infinite loop (-1 = loop forever) activeStreamId = soundPool.play( soundId, 1.0f, // Left volume 1.0f, // Right volume 1, // Priority (doesn't matter here since max streams is 1) -1, // Loop count (-1 = infinite) 1.0f // Playback speed (1.0 = normal) ); return true; case MotionEvent.ACTION_UP: // Stop the note when the key is released if (activeStreamId != -1) { soundPool.stop(activeStreamId); activeStreamId = -1; // Reset the active stream tracker } return true; default: return false; } }
3. Clean Up Resources
Don't forget to release SoundPool when your activity is destroyed to avoid memory leaks:
@Override protected void onDestroy() { super.onDestroy(); if (soundPool != null) { soundPool.release(); soundPool = null; } }
Important Notes for Smooth Harmonium Playback
- Use loop-friendly audio files: Make sure your harmonium sound samples have smooth start/end points—this prevents annoying pops or gaps when looping.
- Stream limit safety: Setting
setMaxStreams(1)ensures SoundPool will automatically stop the old stream if you accidentally forget to callstop(), but manual tracking is more reliable for consistent behavior. - Load state check: The
isSoundsLoadedflag prevents crashes or silent presses if the app tries to play sounds before they finish loading.
内容的提问来源于stack exchange,提问作者just don't be user123

