How to check if an specific sound in SamplePlayer is playing?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Joel Gomes da Silva
:warning: Old Version Published before Godot 3 was released.

Hello! I wanna know if there’s a way to check if an specific sound in a sample library is currently playing through the SamplePlayer, like checking by name.

In my project (a musical instrument game), my goal is to play a note if it’s not already playing, and checking if a voice is active (SamplePlayer.is_voice_active) is not a solution, since the index can change because of the polyphonic nature of the instrument.

The instrument have samples named according to its midi number (A1 = 045, E7 = 112, etc), just for you to know what will be inside the notes_playing array.

Here’s my current code:

extends Node

var notes_playing = [] 

func _ready():
	set_process(true)

func _process(delta):
	
	var sample_player = self.get_node("sample_player")
	
	# If there's notes playing
	if notes_playing != []:
		
		# Process all notes to play
		for note in notes_playing:
			
			var current_note = str(note)
			
			# Avoid issues with two digit sample names (ex: 53 must be 053)
			if int(current_note) < 100:
				current_note = str("0" + current_note)
			
			# Play note
			if "current_note IN sample_player IS NOT PLAYING":
				sample_player.play(current_note)
			
	# If there's not notes playing
	if notes_playing == [] and sample_player.is_active()::
		sample_player.stop_all()

How do you fill the notes_playing array? Is it a list of all notes that should be playing at a given point in time? is_voice_active is what you need, but you have to use it a bit differently.

I think this should help:

# List of notes that should be playing (from your code)
var notes_playing = []

# Get the sample player once and for all
onready var _sample_player = get_node("sample_player")
# Dictionary of active voices, indexed by the note MIDI number
var _active_notes = {}


func _process(delta):
	# The reason why I made two steps is because voices could
	# get re-used in the second step.
	process_notes_to_stop()
	process_notes_to_start()


func process_notes_to_stop():
	# Before playing anything, find which voices are still active
	var ended_notes = []
	for note_id in _active_notes:
		var voice = _active_notes[note_id]
		# If the voice is inactive
		if _sample_player.is_voice_active(voice) == false:
			# We'll remove it from the active notes
			ended_notes.push_back(note_id)
		else:
			# The voice is active, but if it should not be playing, stop it
			if notes_playing.find(note_id) == -1:
				_sample_player.stop(voice)
	
	# Remove ended notes from the active notes
	for i in ended_notes:
		_active_notes.erase(i)


func process_notes_to_start():
	# For each note that should be playing
	for note_id in notes_playing:
		# If the note is not already playing
		if _active_notes.has(note_id) == false:
			# Play it
			# Note: you only need the name here, otherwise numbers are more lightweight
			var note_name = get_note_name(note_id)
			# play() returns the voice ID, store it in active notes
			_active_notes[note_id] = _sample_player.play(note_name)


# Helper to convert a MIDI integer note to its name
static func get_note_name(note_id):
	var name = str(note_id)
	if note_id < 10:
		return "00" + name
	if note_id < 100:
		return "0" + name
	return name

Zylann | 2016-11-06 01:05