### Load and Play Sounds with pyfmodex Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python code demonstrates how to load and play multiple sound files using the pyfmodex library. It initializes the FMOD system, creates sound objects, and then plays them in response to user input within a curses-based TUI. The example also shows how to check playback status and time. Dependencies include 'curses', 'sys', 'time', 'pathlib', 'pyfmodex', and its submodules. ```python """Example code to show how to simply load and play multiple sounds, the simplest usage of FMOD. """ import curses import sys import time from pathlib import Path import pyfmodex from pyfmodex.enums import RESULT, TIMEUNIT from pyfmodex.exceptions import FmodError from pyfmodex.flags import MODE MIN_FMOD_VERSION = 0x00020108 mediadir = Path("media") soundnames = ( mediadir / "drumloop.wav", mediadir / "jaguar.wav", mediadir / "swish.wav", ) # Create a System object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init() sounds = [] for filename in soundnames: # drumloop.wav has embedded loop points which automatically turns on # looping so we turn it off (for all) here. sounds.append(system.create_sound(str(filename), mode=MODE.LOOP_OFF)) # Main loop def main(stdscr): """Draw a simple TUI, grab keypresses and let the user play the sounds.""" stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "===================\n" "Play Sound Example.\n" "===================\n" "\n" f"Press 1 to play a mono sound ({soundnames[0].stem})\n" f"Press 2 to play a mono sound ({soundnames[1].stem})\n" f"Press 3 to play a stereo sound ({soundnames[2].stem})\n" "Press q to quit" ) channel = None currentsound = None while True: is_playing = False position = 0 length = 0 if channel: try: is_playing = channel.is_playing position = channel.get_position(TIMEUNIT.MS) currentsound = channel.current_sound if currentsound: length = currentsound.get_length(TIMEUNIT.MS) except FmodError as fmoderror: if fmoderror.result not in ( RESULT.INVALID_HANDLE, RESULT.CHANNEL_STOLEN, ): raise fmoderror stdscr.move(9, 0) stdscr.clrtoeol() stdscr.addstr( "Time %02d:%02d:%02d/%02d:%02d:%02d : %s" % ( position / 1000 / 60, position / 1000 % 60, position / 10 % 100, length / 1000 / 60, length / 1000 % 60, length / 10 % 100, "Playing" if is_playing else "Stopped", ), ) stdscr.addstr(10, 0, f"Channel Playing {system.channels_playing.channels:-2d}") # Listen to the user try: keypress = stdscr.getkey() if keypress in "123": channel = system.play_sound(sounds[int(keypress) - 1]) elif keypress == "q": break except curses.error as cerr: if cerr.args[0] != "no input": raise cerr system.update() time.sleep(50 / 1000) curses.wrapper(main) # Shut down for sound in sounds: sound.release() system.release() ``` -------------------------------- ### Python FMOD Convolution Reverb Setup and Control Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python script demonstrates setting up a convolution reverb DSP using pyfmodex. It initializes FMOD, creates channel groups, loads an impulse response WAV file, configures the reverb parameters (IR, dry mix), and sets up a send connection for the wet signal. The script includes error handling for FMOD version and audio format. ```python """Sample code to demonstrate how to set up a convolution reverb DSP and work with it. """ import curses import sys import time from ctypes import c_short, sizeof import pyfmodex from pyfmodex.enums import ( CHANNELCONTROL_DSP_INDEX, DSP_CONVOLUTION_REVERB, DSP_TYPE, DSPCONNECTION_TYPE, SOUND_FORMAT, TIMEUNIT, ) from pyfmodex.flags import MODE MIN_FMOD_VERSION = 0x00020108 # Create a System object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init(maxchannels=1) # Create a new channel group to hold the convolution DSP unit reverbgroup = system.create_channel_group("reverb") # Create a new channel group to hold all the channels and process the dry path maingroup = system.create_channel_group("main") # Create the convolution DSP unit and set it as the tail of the channel group reverbunit = system.create_dsp_by_type(DSP_TYPE.CONVOLUTIONREVERB) reverbgroup.add_dsp(CHANNELCONTROL_DSP_INDEX.TAIL, reverbunit) # Open the impulse response wav file, but use FMOD_OPENONLY as we want to read # the data into a seperate buffer irsound = system.create_sound("media/standrews.wav", mode=MODE.DEFAULT | MODE.OPENONLY) # For simplicity of the example, if the impulse response is the wrong format # just display an error if irsound.format.format != SOUND_FORMAT.PCM16: print( "Impulse Response file is the wrong audio format. It should be 16bit" " integer PCM data." ) sys.exit(1) # The reverb unit expects a block of data containing a single 16 bit int # containing the number of channels in the impulse response, followed by PCM 16 # data short_size = sizeof(c_short) irsound_channels = irsound.format.channels irsound_data_length = irsound.get_length(TIMEUNIT.PCMBYTES) irdata = (c_short * (1 + irsound_data_length))() irsound_data = irsound.read_data(irsound_data_length)[0] irdata[0] = irsound_channels irdata[1:] = list(irsound_data) reverbunit.set_parameter_data(DSP_CONVOLUTION_REVERB.PARAM_IR, irdata) # Don't pass any dry signal from the reverb unit, instead take the dry part of # the mix from the main signal path reverbunit.set_parameter_float(DSP_CONVOLUTION_REVERB.PARAM_DRY, -80) # We can now release the sound object as the reverb unit has created its # internal data irsound.release() # Load up and play a sample clip recorded in an anechoic chamber sound = system.create_sound("media/singing.wav", mode=MODE.THREED | MODE.LOOP_NORMAL) channel = system.play_sound(sound, channel_group=maingroup, paused=True) # Create a send connection between the channel head and the reverb unit channel_head = channel.get_dsp(CHANNELCONTROL_DSP_INDEX.HEAD) reverb_connection = reverbunit.add_input(channel_head, DSPCONNECTION_TYPE.SEND) channel.paused = False # Main loop def main(stdscr): """Draw a simple TUI, grab keypresses and let the user manipulate the reverb connection. """ wet_volume = 1 dry_volume = 1 stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "====================\n" "Convolution Example.\n" "====================\n" "\n" "Press k and j to change dry mix\n" "Press h and l to change wet mix\n" "Press q to quit" ) while True: stdscr.addstr(8, 0, f"wet mix [{wet_volume:.2f}] | dry mix [{dry_volume:.2f}]") # Listen to the user try: keypress = stdscr.getkey() if keypress == "h": wet_volume = max(wet_volume - 0.05, 0) elif keypress == "l": wet_volume = min(wet_volume + 0.05, 1) elif keypress == "j": dry_volume = max(dry_volume - 0.05, 0) elif keypress == "k": dry_volume = min(dry_volume + 0.05, 1) elif keypress == "q": break except curses.error as cerr: if cerr.args[0] != "no input": raise cerr reverb_connection.mix = wet_volume maingroup.volume = dry_volume system.update() time.sleep(50 / 1000) curses.wrapper(main) # Shut down sound.release() maingroup.release() ``` -------------------------------- ### Initialize FMOD, Load Sounds, and Create Channel Groups with pyfmodex Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code Initializes the FMOD system, loads audio files, creates distinct channel groups, and assigns these groups as children of the master group. It checks for FMOD version compatibility and handles sound and group resource management. ```python """Sample code to show how to put channels into channel groups.""" import curses import sys import time import pyfmodex from pyfmodex.flags import MODE MIN_FMOD_VERSION = 0x00020108 # Create a System object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init(maxchannels=6) # Load some sounds sounds = [] sounds.append(system.create_sound("media/drumloop.wav", mode=MODE.LOOP_NORMAL)) sounds.append(system.create_sound("media/jaguar.wav", mode=MODE.LOOP_NORMAL)) sounds.append(system.create_sound("media/swish.wav", mode=MODE.LOOP_NORMAL)) sounds.append(system.create_sound("media/c.ogg", mode=MODE.LOOP_NORMAL)) sounds.append(system.create_sound("media/d.ogg", mode=MODE.LOOP_NORMAL)) sounds.append(system.create_sound("media/e.ogg", mode=MODE.LOOP_NORMAL)) group_a = system.create_channel_group("Group A") group_b = system.create_channel_group("Group B") group_master = system.master_channel_group # Instead of being independent, set the group A and B to be children of the # master group group_master.add_group(group_a) group_master.add_group(group_b) # Start all the sounds for idx, sound in enumerate(sounds): system.play_sound(sound, channel_group=group_a if idx < 3 else group_b) # Change the volume of each group, just because we can! (reduce overall noise) group_a.volume = 0.5 group_b.volume = 0.5 ``` -------------------------------- ### Enumerate Record Drivers with pyfmodex Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python script enumerates available recording drivers using pyfmodex. It initializes the FMOD system, sets up a callback for changes in the recording device list, and displays driver information including name, sample rate, channel count, GUID, connection status, and recording/playback state. It requires the pyfmodex library and curses for the TUI. ```Python """Example code to show how to enumerate the available recording drivers on this device and work with them. """ import curses import sys import time from collections import defaultdict from ctypes import c_int, c_short, sizeof import pyfmodex from pyfmodex.enums import RESULT, SOUND_FORMAT from pyfmodex.exceptions import FmodError from pyfmodex.flags import DRIVER_STATE, MODE, SYSTEM_CALLBACK_TYPE from pyfmodex.structures import CREATESOUNDEXINFO MIN_FMOD_VERSION = 0x00020108 MAX_DRIVERS_IN_VIEW = 3 MAX_DRIVERS = 16 # Create a System object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init() # Setup a callback so we can be notified if the record list has changed def record_list_changed_callback( # pylint: disable=unused-argument mysystem, callback_type, commanddata1, comanddata2, userdata ): """Increase a counter referenced by userdata.""" _record_list_changed_count = c_int.from_address(userdata) _record_list_changed_count.value += 1 return RESULT.OK.value record_list_changed_count = c_int(0) system.user_data = record_list_changed_count system.set_callback( record_list_changed_callback, SYSTEM_CALLBACK_TYPE.RECORDLISTCHANGED ) recordings = [defaultdict(bool) for _ in range(MAX_DRIVERS)] def show_record_drivers(stdscr, selected_driver_idx, num_drivers): """Show an overview of detected record drivers.""" row, _ = stdscr.getyx() for i in range(min(MAX_DRIVERS_IN_VIEW, num_drivers)): idx = (selected_driver_idx - MAX_DRIVERS_IN_VIEW // 2 + i) % num_drivers row += 2 if idx == selected_driver_idx: stdscr.addstr(row, 0, "> ") driver_info = system.get_record_driver_info(idx) statechar = "(*) " if DRIVER_STATE(driver_info.state) & DRIVER_STATE.DEFAULT else "" stdscr.addstr(row, 2, f"{idx}. {statechar}{driver_info.name.decode():41s}") row += 1 stdscr.addstr(row, 2, f"{driver_info.system_rate/1000:2.1f}KHz") stdscr.addstr(row, 10, f"{driver_info.speaker_mode_channels}ch") data4 = driver_info.guid.data4.zfill(8).decode() stdscr.addstr( row, 13, "{%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X}" % ( driver_info.guid.data1, driver_info.guid.data2, driver_info.guid.data3, int(data4[0]) << 8 | int(data4[1]), int(data4[2]), int(data4[3]), int(data4[4]), int(data4[5]), int(data4[6]), int(data4[7]), ), ) row += 1 stdscr.addstr( row, 2, "(%s) (%s) (%s)" % ( "Connected" if (DRIVER_STATE(driver_info.state) & DRIVER_STATE.CONNECTED) else "Unplugged", "Recording" if system.is_recording(idx) else "Not recoding", "Playing" if recordings[idx]["channel"] and recordings[idx]["channel"].is_playing else "Not playing", ), ) # Main loop def main(stdscr): """Draw a simple TUI, grab keypresses and let the user control recording and playback. """ stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "===========================\n" "Record Enumeration Example.\n" "===========================" ) selected_driver_idx = 0 cur_y, _ = stdscr.getyx() while True: stdscr.move(cur_y + 2, 0) stdscr.clrtobot() stdscr.addstr( f"Record list has updated {record_list_changed_count.value} time(s)\n" f"Currently, {system.record_num_drivers.connected} recording device(s) are plugged in\n" "\n" ``` -------------------------------- ### Verify pyfmodex Installation Source: https://pyfmodex.readthedocs.io/en/latest/usage/installation This snippet demonstrates how to verify the pyfmodex installation by importing the library in a Python REPL. No specific inputs or outputs are associated with this verification, as its success is indicated by the absence of import errors. It assumes pyfmodex has been installed. ```python import pyfmodex ``` -------------------------------- ### Play Sample Sound with pyfmodex Source: https://pyfmodex.readthedocs.io/en/latest/usage/quickstart This Python script demonstrates the basic usage of the pyfmodex library to play an audio file. It initializes the FMOD system, creates a sound object from a specified file, plays the sound, and waits for it to finish before releasing the resources. Ensure that 'somefile.mp3' is replaced with a valid audio file path. ```python import pyfmodex system = pyfmodex.System() system.init() sound = system.create_sound("somefile.mp3") channel = sound.play() while channel.is_playing: pass sound.release() system.release() ``` -------------------------------- ### Configure DSP Channel Mixer and Effects Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code Sets up the DSP channel mixer configuration, including channel mask, number of channels, and source speaker mode. It then configures low-pass and high-pass DSP connections by setting their mix matrices and toggling their bypass and active states. This prepares the DSP system for audio processing. ```python 117dspchannelmixer.channel_format = Structobject( channel_mask=0, num_channels=0, source_speaker_mode=SPEAKERMODE.STEREO ) # Now set the above matrices ds lowpassconnection.set_mix_matrix(lowpassmatrix, 2, 2) dsphighpassconnection.set_mix_matrix(highpassmatrix, 2, 2) ds lowpass.bypass = True dsphighpass.bypass = True ds lowpass.active = True dsphighpass.active = True ``` -------------------------------- ### Real-time Audio Playback and Looping with Pyfmodex Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python script demonstrates real-time audio playback using pyfmodex. It initializes FMOD, loads sounds, queues them for playback, and manages channels to create a seamless looping experience. User input is handled to pause/resume playback and quit the application. It utilizes the curses library for terminal interaction. ```python import curses import time from pyfmodex import * # noqa # Global variables system = None sounds = [] def queue_next_sound(channel_to_replace=None): """Queues the next sound in the `sounds` list. If `channel_to_replace` is provided, this new sound will be scheduled to play immediately after `channel_to_replace` finishes. Otherwise, it starts immediately. """ global sounds global system if not sounds: return None # Get the next sound in a round-robin fashion sound_index = queue_next_sound.sound_index % len(sounds) sound_to_play = sounds[sound_index] queue_next_sound.sound_index += 1 # Play the sound channel = system.play_sound(sound_to_play) # If a channel was specified, set the frequency for seamless stitching if channel_to_replace: channel_to_replace.set_position(channel_to_replace.length) channel.set_frequency(channel_to_replace.frequency) return channel queue_next_sound.sound_index = 0 def main(stdscr): """Main function to initialize FMOD and handle audio playback loop.""" global system global sounds # Initialize FMOD System system = System() # noqa system.init() # Load sounds (replace with your actual sound file paths) # Example: Load two short sounds for seamless looping sounds.append(system.create_sound("short_sound_1.ogg", MODE.LOOP_NORMAL)) sounds.append(system.create_sound("short_sound_2.ogg", MODE.LOOP_NORMAL)) # Get the master channel group master_channel_group = system.master_channel_group stdscr.nodelay(True) # Make getkey non-blocking stdscr.timeout(100) # Set a timeout for getkey stdscr.addstr("Press SPACE to pause/resume, q to quit\n") # Kick off the first two sounds. First one is immediate, second one will be # triggered to start after the first one. channels = [] channels.append(queue_next_sound()) channels.append(queue_next_sound(channels[0])) slot = 0 while True: paused_state = "paused" if master_channel_group.paused else "playing" stdscr.move(7, 0) stdscr.clrtoeol() stdscr.addstr(f"Channels are {paused_state}") # Replace the sound that just finished with a new sound, to create # endless seamless stitching! try: is_playing = channels[slot].is_playing except FmodError as fmoderror: if fmoderror.result != RESULT.INVALID_HANDLE: raise fmoderror if not is_playing and not master_channel_group.paused: # Replace sound that just ended with a new sound, queued up to # trigger exactly after the other sound ends channels[slot] = queue_next_sound(channels[1 - slot]) slot = 1 - slot # flip # Listen to the user try: keypress = stdscr.getkey() if keypress == " ": master_channel_group.paused = not master_channel_group.paused elif keypress == "q": break except curses.error as cerr: if cerr.args[0] != "no input": raise cerr system.update() # If you wait too long (longer than the length of the shortest sound), # you will get gaps. time.sleep(10 / 1000) curses.wrapper(main) # Shut down for sound in sounds: sound.release() system.release() ``` -------------------------------- ### Display Audio State and Handle Input in Python Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This snippet demonstrates how to display audio playback information such as time, state, and buffer percentage using the curses library. It also handles user input for controlling playback (space to pause/resume) and exiting the application (q). Dependencies include the 'curses' module and pyfmodex library. ```python import curses import time # Assuming 'stdscr', 'sound', 'channel', 'system', 'open_state', 'OPENSTATE' are defined elsewhere # ... (previous code) stdscr.addstr( f"Time = %02d:%02d:%02d\n" % ( position / 1000 / 60, position / 1000 % 60, position / 10 % 100, ) ) stdscr.addstr( f"State = {state}\n" f"Buffer Percentage = {open_state.percent_buffered}%" ) show_tags(stdscr, sound, channel) # Listen to the user try: keypress = stdscr.getkey() if keypress == " ": if channel: channel.paused = not channel.paused elif keypress == "q": break except curses.error as cerr: if cerr.args[0] != "no input": raise cerr system.update() time.sleep(50 / 1000) if channel: channel.stop() stdscr.clear() stdscr.addstr("Waiting for sound to finish opening before trying to release it...") stdscr.refresh() while True: if sound.open_state.state == OPENSTATE.READY: break system.update() time.sleep(50 / 1000) sound.release() curses.wrapper(main) # Shut down system.release() ``` -------------------------------- ### Pyfmodex Sound Playback and Control Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python code snippet demonstrates how to initialize FMOD, create a sound, play it, and manage playback control using user input within a curses interface. It displays the current playback time and status, allowing the user to pause/resume playback or quit. Dependencies include 'curses' and 'time'. ```python # Data format of sound format=SOUND_FORMAT.PCM16.value, # User callback to reading pcmreadcallback=SOUND_PCMREADCALLBACK(pcmread_callback), # User callback to seeking pcmsetposcallback=SOUND_PCMSETPOSCALLBACK(pcmsetpos_callback), ) sound = system.create_sound(0, mode=mode, exinfo=exinfo) channel = sound.play() subwin.clear() subwin.addstr("Press SPACE to toggle pause\n" "Press q to quit") row, _ = subwin.getyx() while True: is_playing = False paused = False position = 0 length = 0 if channel: try: is_playing = channel.is_playing paused = channel.paused position = channel.get_position(TIMEUNIT.MS) length = sound.get_length(TIMEUNIT.MS) except FmodError as fmoderror: if not fmoderror.result is RESULT.INVALID_HANDLE: raise fmoderror subwin.move(row + 2, 0) subwin.clrtoeol() subwin.addstr( "Time %02d:%02d:%02d/%02d:%02d:%02d : %s" % ( position / 1000 / 60, position / 1000 % 60, position / 10 % 100, length / 1000 / 60, length / 1000 % 60, length / 10 % 100, "Paused" if paused else "Playing" if is_playing else "Stopped", ), ) # Listen to the user try: keypress = subwin.getkey() if keypress == " ": channel.paused = not channel.paused elif keypress == "q": break except curses.error as cerr: if cerr.args[0] != "no input": raise cerr system.update() time.sleep(50 / 1000) sound.release() curses.wrapper(main) # Shut down system.release() ``` -------------------------------- ### Python 3D Sound Positioning with pyfmodex Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This script demonstrates the fundamental aspects of 3D sound positioning in FMOD using the pyfmodex Python library. It initializes the FMOD system, loads 3D sounds, sets their positions, and creates an interactive terminal user interface (TUI) to control playback and listener movement. The script requires the pyfmodex library and FMOD sound files (e.g., 'media/drumloop.wav', 'media/jaguar.wav', 'media/swish.wav'). ```python import curses import sys import time from math import sin import pyfmodex from pyfmodex.flags import MODE INTERFACE_UPDATETIME = 50 DISTANCEFACTOR = 1 MIN_FMOD_VERSION = 0x00020108 # Create system object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init(maxchannels=3) THREED_SETTINGS = system.threed_settings THREED_SETTINGS.distance_factor = DISTANCEFACTOR # Load some sounds sound1 = system.create_sound("media/drumloop.wav", mode=MODE.THREED) sound1.min_distance = 0.5 * DISTANCEFACTOR sound1.max_distance = 5000 * DISTANCEFACTOR sound1.mode = MODE.LOOP_NORMAL sound2 = system.create_sound("media/jaguar.wav", mode=MODE.THREED) sound2.min_distance = 0.5 * DISTANCEFACTOR sound2.max_distance = 5000 * DISTANCEFACTOR sound2.mode = MODE.LOOP_NORMAL sound3 = system.create_sound("media/swish.wav") # Play sounds at certain positions channel1 = system.play_sound(sound1, paused=True) channel1.position = (-10 * DISTANCEFACTOR, 0, 0) channel1.paused = False channel2 = system.play_sound(sound2, paused=True) channel2.position = (15 * DISTANCEFACTOR, 0, 0) channel2.paused = False # Main loop def main(stdscr): """Draw a simple TUI, grab keypresses and let the user manipulate a simple environment with a listener and some sounds. """ listener = system.listener(0) pos_ch1 = int((channel1.position[0]) / DISTANCEFACTOR) + 25 pos_ch2 = int((channel2.position[0]) / DISTANCEFACTOR) + 25 stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "===========\n" "3D Example.\n" "===========\n" "\n" "Press 1 to toggle sound 1 (16bit Mono 3D)\n" "Press 2 to toggle sound 2 (8bit Mono 3D)\n" "Press 3 to play a sound (16bit Stereo 2D)\n" "Press h or l to move listener (when in still mode)\n" "Press space to toggle listener still mode\n" "Press q to quit" ) listener_automove = True listener_prevposx = 0 listener_velx = 0 clock = 0 while True: tic = time.time() listener_posx = listener.position[0] environment = list("|" + 48 * "." + "|") environment[pos_ch1 - 1 : pos_ch1 + 2] = list("<1>") environment[pos_ch2 - 1 : pos_ch2 + 2] = list("<2>") environment[int(listener_posx / DISTANCEFACTOR) + 25] = "L" stdscr.addstr(11, 0, "".join(environment)) stdscr.addstr("\n") # Listen to the user try: keypress = stdscr.getkey() if keypress == "1": channel1.paused = not channel1.paused elif keypress == "2": channel2.paused = not channel2.paused elif keypress == "3": system.play_sound(sound3) elif keypress == " ": listener_automove = not listener_automove elif keypress == "q": break if not listener_automove: if keypress == "h": listener_posx = max( -24 * DISTANCEFACTOR, listener_posx - DISTANCEFACTOR ) elif keypress == "l": listener_posx = min( 23 * DISTANCEFACTOR, listener_posx + DISTANCEFACTOR ) except curses.error as cerr: if cerr.args[0] != "no input": raise cerr # Update the listener if listener_automove: listener_posx = sin(clock * 0.05) * 24 * DISTANCEFACTOR listener_velx = (listener_posx - listener_prevposx) * ( 1000 / INTERFACE_UPDATETIME ) listener.position = (listener_posx, 0, 0) listener.velocity = (listener_velx, 0, 0) listener_prevposx = listener_posx clock += 30 * (1 / INTERFACE_UPDATETIME) system.update() toc = time.time() time.sleep(max(0, INTERFACE_UPDATETIME / 1000 - (toc - tic))) curses.wrapper(main) # Shut down sound1.release() sound2.release() sound3.release() system.release() ``` -------------------------------- ### Release FMOD Resources (Python) Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code Demonstrates the process of releasing FMOD resources, including a reverb unit, its associated reverb group, and the main FMOD system. This is crucial for preventing memory leaks and ensuring proper application shutdown. ```python reverbgroup.remove_dsp(reverbunit) reverbunit.disconnect_all(inputs=True, outputs=True) reverbunit.release() reverbgroup.release() system.release() ``` -------------------------------- ### Create User-Generated Sound with Callbacks (Python) Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python code snippet shows how to create a sound with data filled by the user using pyfmodex. It defines `pcmread_callback` to generate audio data (sine waves for left and right channels) and `pcmsetpos_callback` to handle position setting. The sound can be played as a stream or a static sample, with options for looping and creating the stream. It initializes FMOD, sets up sound creation information, and integrates with `curses` for user interaction. ```python """Example code to show how to create a sound with data filled by the user.""" import curses import sys import time from ctypes import c_float, c_short, sizeof from math import sin import pyfmodex from pyfmodex.callback_prototypes import ( SOUND_PCMREADCALLBACK, SOUND_PCMSETPOSCALLBACK, ) from pyfmodex.enums import RESULT, SOUND_FORMAT, TIMEUNIT from pyfmodex.exceptions import FmodError from pyfmodex.flags import MODE from pyfmodex.structures import CREATESOUNDEXINFO MIN_FMOD_VERSION = 0x00020108 # Create a System object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init() # pylint: disable=invalid-name # Using names common in mathematics t1, t2 = c_float(0), c_float(0) # time v1, v2 = c_float(0), c_float(0) # velocity def pcmread_callback(sound_p, data_p, datalen_i): # pylint: disable=unused-argument """Read callback used for user created sounds. Generates smooth noise. """ # >>2 = 16bit stereo (4 bytes per sample) for _ in range(datalen_i >> 2): # left channel stereo16bitbuffer_left = int(sin(t1.value) * 32767) c_short.from_address(data_p).value = stereo16bitbuffer_left data_p += sizeof(c_short) # right channel stereo16bitbuffer_right = int(sin(t2.value) * 32767) c_short.from_address(data_p).value = stereo16bitbuffer_right data_p += sizeof(c_short) t1.value += 0.01 + v1.value t2.value += 0.0142 + v2.value v1.value += sin(t1.value) * 0.002 v2.value += sin(t2.value) * 0.002 return RESULT.OK.value def pcmsetpos_callback( sound, subsound, position, timeunit ): # pylint: disable=unused-argument """Set position callback for user created sounds or to intercept FMOD's decoder during an API setPositon call. This is useful if the user calls set_position on a channel and you want to seek your data accordingly. """ return RESULT.OK.value # Main loop def main(stdscr): """Draw a simple TUI, grab keypresses and let the user select a sound generation method. """ stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "===========================\n" "User Created Sound Example.\n" "===========================" ) stdscr.refresh() subwin = stdscr.derwin(4, 0) subwin.nodelay(True) subwin.addstr( "Sound played here is generated in realtime. It will either play as a " "stream which means it is continually filled as it is playing, or it " "will play as a static sample, which means it is filled once as the " "sound is created, then, when played, it will just play that short " "loop of data.\n" "\n" "Press 1 to play an generated infinite stream\n" "Press 2 to play a static looping sample\n" "Press q to quit" ) mode = MODE.OPENUSER | MODE.LOOP_NORMAL while True: # Listen to the user try: keypress = subwin.getkey() if keypress == "1": mode |= MODE.CREATESTREAM break if keypress == "2": break if keypress == "q": return except curses.error as cerr: if cerr.args[0] != "no input": raise cerr time.sleep(50 / 1000) # Create and play the sound numchannels = 2 defaultfrequency = 44100 exinfo = CREATESOUNDEXINFO( # Number of channels in the sound numchannels=numchannels, # Default playback rate of the sound defaultfrequency=defaultfrequency, # Chunk size of stream update in samples. This will be the amount of # data passed to the user callback. decodebuffersize=44100, # Length of PCM data in bytes of whole sound (for sound.get_length) length=defaultfrequency * numchannels * sizeof(c_short) * 5, ) # User created sounds require an FMOD_SOFTWARE24BIT or FMOD_HARDWARE24BIT flag # and an FMOD_UNSIGNED flag, which is handled by the MODE.OPENUSER flag. if mode & MODE.CREATESTREAM: exinfo.pcmsetposcallback = pcmsetpos_callback exinfo.pcmreadcallback = pcmread_callback else: exinfo.length = defaultfrequency * numchannels * sizeof(c_short) * 5 exinfo.pcmreadcallback = pcmread_callback exinfo.pcmsetposcallback = pcmsetpos_callback try: sound = pyfmodex.Sound( name_or_data="", # No name or data, it's user created mode=mode, exinfo=exinfo, system=system, ) except FmodError as e: print(f"Error creating sound: {e}") return channel = system.play_sound(sound) # TUI loop while True: stdscr.addstr(0, 0, "Playing sound... Press q to quit.") stdscr.clrtoeol() stdscr.refresh() try: keypress = stdscr.getkey() if keypress == "q": break except curses.error as cerr: if cerr.args[0] != "no input": raise cerr # Check if sound is still playing if channel.is_playing(): time.sleep(10 / 1000) else: break # Clean up sound.release() system.close() system.release() if __name__ == "__main__": try: curses.wrapper(main) except KeyboardInterrupt: print("Exiting.") ``` -------------------------------- ### Play Internet Stream Audio - Python Source: https://pyfmodex.readthedocs.io/en/latest/usage/sample_code This Python script uses pyfmodex to play an MP3 stream from a URL. It initializes the FMOD system, sets stream buffer sizes, and handles playback, buffering states, and user input for control. It also processes and displays metadata tags from the stream. ```python import ctypes import curses import sys import time import pyfmodex from pyfmodex.enums import OPENSTATE, RESULT, TAGDATATYPE, TAGTYPE, TIMEUNIT from pyfmodex.exceptions import FmodError from pyfmodex.flags import MODE from pyfmodex.structobject import Structobject from pyfmodex.structures import CREATESOUNDEXINFO URL = "https://focus.stream.publicradio.org/focus.mp3" MIN_FMOD_VERSION = 0x00020108 # Create a System object and initialize system = pyfmodex.System() VERSION = system.version if VERSION < MIN_FMOD_VERSION: print( f"FMOD lib version {VERSION:#08x} doesn't meet " f"minimum requirement of version {MIN_FMOD_VERSION:#08x}" ) sys.exit(1) system.init(maxchannels=1) # Increase the file buffer size a little bit to account for Internet lag system.stream_buffer_size = Structobject(size=64 * 1024, unit=TIMEUNIT.RAWBYTES) # Increase the default file chunk size to handle seeking inside large playlist # files that may be over 2kb. exinfo = CREATESOUNDEXINFO(filebuffersize=1024 * 16) tags = {} def show_tags(stdscr, sound, channel): """Read and print any tags that have arrived. This could, for example, happen if a radio station switches to a new song. """ stdscr.move(11, 0) stdscr.addstr("Tags:\n") while True: try: tag = sound.get_tag(-1) except FmodError: break if tag.datatype == TAGDATATYPE.STRING.value: tag_data = ctypes.string_at(tag.data).decode() tags[tag.name.decode()] = (tag_data, tag.datalen) if tag.type == TAGTYPE.PLAYLIST.value and not tag.name == "FILE": # data point to sound owned memory, copy it before the # sound is released sound.release() sound = system.create_sound( tag.data, mode=MODE.CREATESTREAM | MODE.NONBLOCKING, exinfo=exinfo, ) elif tag.type == TAGTYPE.FMOD.value: # When a song changes, the sample rate may also change, so # compensate here if tag.name.decode() == "Sample Rate Change" and channel: channel.frequency = float(ctypes.string_at(tag.data).decode()) stdscr.move(12, 0) stdscr.clrtobot() for name, value in tags.items(): stdscr.addstr(f"{name} = '{value[0]}' ({value[1]} bytes)\n") # Main loop def main(stdscr): """Draw a simple TUI, grab keypresses and let the user control playback.""" stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "===================\n" "Net Stream Example.\n" "===================\n" "\n" "Press SPACE to toggle pause\n" "Press q to quit\n" ) sound = system.create_sound( URL, mode=MODE.CREATESTREAM | MODE.NONBLOCKING, exinfo=exinfo ) channel = None while True: open_state = sound.open_state is_playing = False position = 0 paused = False if channel: try: is_playing = channel.is_playing position = channel.get_position(TIMEUNIT.MS) paused = channel.paused # Silence the stream until we have sufficient data for smooth # playback channel.mute = open_state.starving except FmodError as fmoderror: if fmoderror.result not in ( RESULT.INVALID_HANDLE, RESULT.CHANNEL_STOLEN, ): raise fmoderror else: try: channel = system.play_sound(sound) except FmodError: # This may fail if the stream isn't ready yet, so don't check # for errors pass state = "" if open_state.state == OPENSTATE.BUFFERING: state = "Buffering..." elif open_state.state == OPENSTATE.CONNECTING: state = "Connecting..." elif paused: state = "Paused" elif is_playing: state = "Playing" if open_state.starving: state += " (STARVING)" stdscr.move(7, 0) stdscr.clrtoeol() stdscr.addstr( ```