Skip to main content

Audio Processing Platform

Project description

Welcome to the Sudio 🎵

sudio is an Open-source, easy-to-use digital audio processing library featuring both a real-time, non-real-time mix/edit platform.

Abstract

Audio signal processing is a highly active research field where digital signal processing theory meets human sound perception and real-time programming requirements. It has a wide range of applications in computers, gaming, and music technology, to name a few of the largest areas. Successful applications include for example perceptual audio coding, digital music synthesizers, and music recognition software.

Real-time audio processing

For live digital audio systems with high-resolution multichannel functionalities, it is desirable to have accurate latency control and estimation over all the stages of digital audio processing chain.

The sudio is written flexible can used for high level audio processing algorithm and other system factors, which might cause the latency effects. It also can estimate the synchronization and delay of multiple channels.

Non-Realtime processing:

Sudio is a comprehensive library for mixing, editing, and recording audio content.

Audio data maintaining process:

The sudio used additional cached files to reduce dynamic memory usage and improve performance, meaning that audio data storage methods could have different execution times based on the stored files. Thanks to that, Sudo can manage various audio streams from audio files or operating systems without any size limitation.





🎵 Sudio is like a budding musician finding its rhythm. Your support can help it compose a symphony. 🎶




Installation

Latest PyPI stable release (previous version)
pip install sudio

Quick start

Audio playback

import sudio

su = sudio.Master()
su.add('baroon.mp3')
su.echo('baroon')

the record with the name of baroon will be played on the stdout audio stream.

Audio Manipulation

Time Domain Slicing

You can easily slice audio files to play specific segments:

su = sudio.Master()
song = su.add('baroon.mp3')
su.echo(song[12: 27.66])

# Play from 30 seconds to the end
su.echo(song[30:])

# Play the first 15 seconds
su.echo(song[:15])
Combining Audio Segments

You can join multiple segments of audio:

su = sudio.Master()
rec = su.add('baroon.mp3')

# method 1
su.echo(rec[12: 27.66, 65: 90])

# method 2
result = rec[12: 27.66].join(rec[65: 90])

# Combine multiple segments
medley = song[10:20].join(song[40:50], song[70:80])
su.echo(medley)

The audio record is split into two parts, the first one 12-27.66 seconds, and the last one 65-90 seconds, then the sliced records are merged and played in the stream.

Audio Basic Effects
Volume Adjustment

Adjust the volume of an audio segment:

su = sudio.Master()
song = su.add('song.mp3')

# Double the volume
loud_segment = song[10:20] * 2

# Halve the volume
quiet_segment = song[30:40] / 2

su.echo(loud_segment.join(quiet_segment))
Applying Filters

Apply frequency filters to audio:

su = sudio.Master()
song = su.add('song.mp3')

# Apply a low-pass filter (keep frequencies below 1000 Hz)
low_pass = song[:'1000']

# Apply a high-pass filter (keep frequencies above 500 Hz)
high_pass = song['500':]

# Apply a band-pass filter (keep frequencies between 500 Hz and 2000 Hz)
band_pass = song['500':'2000']

su.echo(low_pass.join(high_pass, band_pass))
Complex Slicing
su = sudio.Master()
baroon = su.add('baroon.mp3')
su.echo(baroon[5:10, :'1000', 10: 20, '1000': '5000'])

In the example above, a low-pass filter with a cutoff frequency of 1 kHz is applied to the record from 5 to 10 seconds, then a band-pass filter is applied from 10 to 20 seconds, and finally they are merged.

Custom Fade-In and Mixing
import sudio
from sudio.types import SampleFormat
import numpy as np

su = sudio.Master()
song = su.add('example.mp3')

fade_length = int(song.get_sample_rate() * 5)  # 5-second fade
fade_in = np.linspace(0, 1, fade_length)

with song.unpack(astype=SampleFormat.formatFloat32) as data:
    data[:, :fade_length] *= fade_in
    song.set_data(data)

modified = song[:30] + song[:15, :'100'] * 3
su.echo(modified)
su.export(modified, 'modified_song.wav')

This example demonstrates advanced audio manipulation techniques:

  1. Fade-In Effect: We create a 5-second linear fade-in effect.

  2. Data Type Conversion: The audio data is converted to float32 for precise calculations.

  3. Unpacking and Repacking: We use unpack() to access raw audio data, modify it, and then set_data() to apply changes.

  4. Audio Slicing and Mixing:

    • song[:30]: Takes the first 30 seconds of the audio.
    • song[:15, :'100']: Takes the first 15 seconds and applies a low-pass filter at 100 Hz.
    • The * 3 multiplies the amplitude of the filtered segment.
    • These segments are then added together.
  5. Playback and Export: The modified audio is played and exported to a new file.

This example showcases the library's capability to perform complex audio manipulations, combining time-domain operations (fade-in, slicing) with frequency-domain filtering and amplitude adjustments.

Audio Analysis

Perform simple analysis on audio files:

su = sudio.Master()
song = su.add('song.mp3')

# Get audio duration
duration = song.get_duration()
print(f"Song duration: {duration} seconds")

# Get sample rate
sample_rate = song.get_sample_rate()
print(f"Sample rate: {sample_rate} Hz")

# Get number of channels
channels = song.get_nchannels()
print(f"Number of channels: {channels}")
Exporting Modified Audio

After manipulating audio, you can save the results:

su = sudio.Master()
song = su.add('song.mp3')

# Create a modified version
modified = song[30:60] * 1.5  # 30 seconds from 30s mark, amplified

# Export to a new file
su.export(modified, 'modified_song.wav')
Mixing and Shifting Tracks

When adding two Wrap objects, the combined audio will be as long as the longer one, mixing overlapping parts. Adding a constant shifts the waveform up while keeping the original duration. This allows for flexible audio mixing and simple DC offset adjustments.

import sudio
import numpy as np

su = sudio.Master()

# Add two audio files
song1 = su.add('song1.mp3')  # Assuming this is a 30-second audio
song2 = su.add('song2.mp3')  # Assuming this is a 40-second audio

# Add the two songs
combined = song1 + song2

# Play the combined audio
su.echo(combined)

# Add a constant value to shift the audio
shifted = song1 + 0.1

# Play the shifted audio
su.echo(shifted)

# Print durations
print(f"Song1 duration: {song1.get_duration()} seconds")
print(f"Song2 duration: {song2.get_duration()} seconds")
print(f"Combined duration: {combined.get_duration()} seconds")
print(f"Shifted duration: {shifted.get_duration()} seconds")

Audio Streaming

Basic Streaming with Pause and Resume

This example demonstrates how to control audio playback using the sudio library, including starting, pausing, resuming, and stopping a stream.

import sudio
import time

# Initialize the audio master
su = sudio.Master()
su.start()

# Add an audio file to the master
record = su.add('example.mp3')
stream = su.stream(record)

# Enable stdout echo
su.echo()

# Start the audio stream
stream.start()
print(f"Current playback time: {stream.time} seconds")

# Pause the playback after 5 seconds
time.sleep(5)
stream.pause()
print("Paused playback")

# Resume playback after 2 seconds
time.sleep(2)
stream.resume()
print("Resumed playback")

# Stop playback after 5 more seconds
time.sleep(5)
stream.stop()
print("Stopped playback")

This script showcases basic audio control operations, allowing you to manage playback with precise timing.

Basic Streaming with Jumping to Specific Times in the Audio

This example illustrates how to start playback and jump to a specific time in an audio file.

import sudio
import time

# Initialize the audio master
su = sudio.Master()
su.start()

# Add a long audio file to the master
record = su.add('long_audio.mp3')
stream = su.stream(record)

# Enable stdout echo
su.echo()

# Start the audio stream
stream.start()
print(f"Starting playback at: {stream.time} seconds")

# Jump to 30 seconds into the audio after 5 seconds of playback
time.sleep(5)
stream.time = 30
print(f"Jumped to: {stream.time} seconds")

# Continue playback for 10 more seconds
time.sleep(10)
print(f"Current playback time: {stream.time} seconds")

# Stop the audio stream
stream.stop()

This script demonstrates how to navigate within an audio file, which is useful for long audio content or when specific sections need to be accessed quickly.

Streaming with Volume Control

This example shows how to dynamically control the volume of an audio stream using a custom pipeline.

import sudio
import time
import sudio.types

# Initialize the audio master with a specific input device
su = sudio.Master(std_input_dev_id=2)
su.start()

# Add an audio file to the master
record = su.add('example.mp3')
stream = su.stream(record)

# Define a function to adjust the volume
def adjust_volume(data, args):
    return data * args['volume']

# Create a pipeline and append the volume adjustment function
pip = sudio.Pipeline()
pip.append(adjust_volume, args={'volume': 1.0})

# Start the pipeline
pip.start()

# Add the pipeline to the master
pipeline_id = su.add_pipeline(pip, process_type=sudio.types.PipelineProcessType.MAIN)
su.set_pipeline(pipeline_id)

# Enable stdout echo
su.echo()

# Start the audio stream
stream.start()
print("Playing at normal volume")
time.sleep(10)

# Adjust the volume to 50%
pip.update_args(adjust_volume, {'volume': 0.5})
print("Reduced volume to 50%")
time.sleep(10)

# Restore the volume to normal
pip.update_args(adjust_volume, {'volume': 1.0})
print("Restored normal volume")
time.sleep(10)

# Stop the audio stream
stream.stop()

This example introduces a more complex setup using a custom pipeline to dynamically adjust the audio volume during playback. It's particularly useful for applications requiring real-time audio processing or user-controlled volume adjustments.

Table of contents:

Examples and Advanced Usage


Short-time Fourier transform

The Short-time Fourier transform (STFT), is a Fourier-related transform used to determine the sinusoidal frequency and phase content of local sections of a signal as it changes over time. In practice, the procedure for computing STFTs is to divide a longer time signal into shorter segments of equal length and then compute the Fourier transform separately on each shorter segment. This reveals the Fourier spectrum on each shorter segment. One then usually plots the changing spectra as a function of time, known as a spectrogram or waterfall plot, such as commonly used in Software Defined Radio (SDR) based spectrum displays.

prerequisites
pip install sudio
pip install kivy

Graphical STFT image


API Documentation

Master

sudio.Master

The Master class is the core component of the audio processing system. It manages audio input/output, signal processing, and provides a framework for advanced audio manipulation.

Key Features

  1. Flexible Device Configuration: Supports both default and custom audio input/output devices.
  2. Multiple Audio Formats: Handles various sample formats including 32-bit float, 32/24/16/8-bit integer.
  3. Windowing and Overlap: Implements customizable windowing functions with overlap-add method.
  4. Multithreaded Processing: Utilizes threading for efficient audio stream management.
  5. Pipeline System: Allows creation and management of audio processing pipelines.
  6. File I/O: Supports adding, loading, and exporting audio files.
  7. Real-time Processing: Enables real-time audio manipulation through callback functions.

Initialization

The Master class is initialized with various parameters to configure the audio processing environment:

master = Master(std_input_dev_id=None, std_output_dev_id=None, data_format=SampleFormat.formatInt16, ...)
Key Parameters
  • std_input_dev_id / std_output_dev_id: Specify input/output device IDs. If None, uses system defaults.
  • data_format: Sets the audio sample format (e.g., 16-bit integer, 32-bit float).
  • nperseg: Number of samples per frame in each channel.
  • window: Specifies the windowing function (e.g., 'hann', 'hamming').
  • input_dev_callback / output_dev_callback: Custom callback functions for audio I/O.

Advanced Features

  1. Windowing: Supports various window types with automatic NOLA (Nonzero Overlap Add) constraint checking.
  2. Stream Control: Methods for muting, unmuting, and echoing audio streams.
  3. Pipeline Management: Add, set, and disable audio processing pipelines.
  4. File Management: Add, load, export, and delete audio records.
  5. Device Information: Static methods to query available audio devices and their properties.

Usage Examples

  1. Basic Initialization:

    master = Master()
    master.start()
    
  2. Custom Input Device:

    master = Master(std_input_dev_id=1, data_format=SampleFormat.formatFloat32)
    
  3. Adding a Pipeline:

    pipeline = Pipeline()
    master.add_pipeline(pipeline, name="my_pipeline", process_type=PipelineProcessType.MAIN)
    master.set_pipeline("my_pipeline")
    
  4. Recording Audio:

    recorded_audio = master.recorder(record_duration=10, name="my_recording")
    
  5. Exporting Audio:

    master.export("my_recording", "path/to/output.wav")
    

The Master class provides a comprehensive suite of tools for audio processing, making it suitable for a wide range of applications from simple audio playback to complex real-time audio manipulation systems.

Methods


add_file
sudio.Master.add_file(  self, filename: str, sample_format: SampleFormat = SampleFormat.formatUnknown, 
                        nchannels: int = None, sample_rate: int = None ,safe_load=True)

The add_file method used to Add an audio file to the local database. None value for the parameters means that they are automatically selected based on the properties of the audio file.

  • parameters:

    • filename: path/name of the audio file
    • sample_format: optional; sample format (refer to the SampleFormat data field).
    • nchannels: optional; number of audio channels
    • sample_rate: optional; sample rate
    • safe_load: optional; load an audio file and modify it according to the 'Master' attributes. (sample rate, sample format, number of channels, etc).
  • returns WrapGenerator object

:memo: Note: supported files format: WAV, FLAC, VORBIS, MP3

:memo: Note: The audio data maintaining process has additional cached files to reduce dynamic memory usage and improve performance, meaning that, The audio data storage methods can have different execution times based on the cached files.


add
sudio.Master.add(self, record, safe_load=True)

Adds a new record to the local database of the Master object.

Parameters
  • record: The record to be added. Can be one of the following:

    • A Wrap or WrapGenerator object
    • A string path to an audio file (supported formats: mp3, WAV, FLAC, VORBIS)
    • AudioMetadata containing record data
    • A tuple (record, name) where record is any of the above, and name is a custom name for the record
  • safe_load: bool, optional (default=True)

    • If True, audio files are loaded and modified to match the Master object's current settings
Returns
  • WrapGenerator: A wrapped version of the added record
Notes
  1. Naming: If a tuple (record, name) is provided, the second element is used as the record name. Otherwise, a name is automatically generated.

  2. Performance: The method uses cached files to optimize memory usage and performance. This may cause varying execution times depending on the state of these cached files.

  3. Compatibility: When safe_load=True, the method ensures that the added record is compatible with the current audio processing settings of the Master object.

Examples
# Adding an audio file
master.add("path/to/audio.wav")

# Adding a wrapped record with a custom name
wrapped_record = some_wrap_generator()
master.add((wrapped_record, "my_custom_name"))

# Adding AudioMetadata record
series_record = AudioMetadata(...) # Record data
master.add(series_record)
Exceptions
  • Raises ValueError if the record type is not recognized or if there's an issue with the file format.
  • Raises ImportError if the number of channels in the record is incompatible with the Master object's settings when safe_load=True.

This method provides a flexible way to add various types of audio data to your project, with options for ensuring compatibility and custom naming.


start
sudio.Master.start(self)

Initializes and starts the audio processing stream.

Description

The start method activates the configured audio stream, enabling real-time audio processing based on the Master object's current settings. It should be called after all desired configurations have been made.

Returns
  • self: Returns the Master instance, allowing for method chaining.
Behavior
  1. Initialization: Sets up the audio stream with the configured parameters (sample rate, format, channels, etc.).
  2. Thread Activation: Starts all processing threads, enabling concurrent audio handling.
  3. Error Handling: Raises exceptions if the stream is already started or if there are issues with stream initialization.
Usage Notes
  • Call this method only once, after all configurations are complete.
  • The audio processing runs in a separate thread after this method is called.
  • Ensure all desired pipelines and audio settings are configured before calling start.
Examples
# Basic usage
master = Master()
master.start()

# Method chaining
master = Master().start()

# Start after configuration
master = Master()
master.add_pipeline(some_pipeline)
master.set_window('hamming')
master.start()
Exceptions
  • Raises AssertionError if the audio stream is already started.
  • May raise other exceptions related to audio stream initialization, depending on the underlying audio library.

This method is crucial for activating the real-time audio processing capabilities of the Master object. Ensure all necessary configurations are done before calling start.


recorder
sudio.Master.recorder(self, record_duration: float, name: str = None)

Records audio for a specified duration and stores it as a new record in the Master object's database.

Parameters
  • record_duration: float
    • The duration of the recording in seconds.
  • name: str, optional
    • A custom name for the recorded audio. If None, a timestamp-based name is generated.
Returns
  • Wrap: A wrapped version of the recorded audio data.
Description

The recorder method captures audio input using the current settings of the Master object (sample rate, number of channels, etc.) for the specified duration. The recorded audio is automatically added to the Master's database and can be accessed later using the provided or generated name.

Behavior
  1. Initialization: Sets up a temporary recording state in the Master object.
  2. Recording: Captures audio data for the specified duration.
  3. Processing: Processes the recorded data, including channel shuffling if necessary.
  4. Storage: Writes the recorded data to a cached file and updates the Master's database.
Usage Notes
  • The recording uses the current audio input settings of the Master object.
  • The method temporarily modifies the internal state of the Master object during recording.
  • Recorded audio is automatically added to the database and can be accessed or manipulated later.
Examples
# Record for 5 seconds with an auto-generated name
recorded_audio = master.recorder(5)

# Record for 10 seconds with a custom name
recorded_audio = master.recorder(10, name="my_recording")

# Use the recorded audio
master.play(recorded_audio)
Exceptions
  • Raises KeyError if the provided name already exists in the database.

This method provides a straightforward way to capture and store audio data within the Master object's ecosystem, making it easy to record and subsequently process or analyze audio segments.


load
   sudio.Master.load(self, name: str, safe_load: bool=True,
                    series: bool=False) -> Union[WrapGenerator, Record])

The load method used to load a predefined record from database. Trying to load a record that was previously loaded, outputs a wrapped version of the named record.

  • parameters:

    • name: The name of the record.
    • safe_load: if safe load is enabled this method tries to load a record in the local database, based on the master settings, like the frame rate and etc.
    • series: If enabled, attempting to load a record that has already been loaded will output the data series of the named record.
  • returns optional; Wrapped object, Record obejct.


get_record_info
   def get_record_info(self, record: Union[str, WrapGenerator, Wrap]) -> dict:

get extra info about the record

  • parameters:

    • name Union[str, WrapGenerator, Wrap]): registered record on the database
  • returns information about saved record ['frameRate' 'sizeInByte' 'duration' 'nchannels' 'nperseg' 'name'].


syncable
   sudio.Master.syncable(self,
                        * target,
                        nchannels: int = None,
                        sample_rate: int = None,
                        sample_format: SampleFormat = SampleFormat.formatUnknown)

Determines whether the wrapped record/s can be synced with specified properties.

  • parameters:

    • target: target: wrapped record\s.(regular records)

    • nchannels: number of channels; if the value is None, the target will be compared to the 'self' properties.

    • sample_rate: sample rate; if the value is None, the target will be compared to the 'self' properties.

    • sample_format: if the value is None, the target will be compared to the 'self' properties.

  • returns returns only objects that need to be synchronized.


sync
   sudio.Master.sync(self,
                    * targets,
                    nchannels: int=None,
                    sample_rate: int=None,
                    sample_format: SampleFormat=SampleFormat.formatUnknown,
                    output='wrapped')

This method used to Synchronize wrapped record/s with the specified properties.

  • parameters:

    • targets: wrapped records\s.
    • nchannels: number of channels; if the value is None, the target will be synced to the 'self' properties.
    • sample_rate: if the value is None, the target will be synced to the 'self' properties.
    • sample_format: if the value is None, the target will be synced to the 'self' properties.
    • output: can be 'wrapped'(regular records), 'series'(dict type) or 'ndarray_data'
  • returns returns synchronized objects.


del_record
sudio.Master.del_record(self, name: str)

The del_record method used to delete a record from database.

  • parameters:

  • name str: name of record.

  • returns None


export
sudio.Master.export(self, record: Union[str, Record, Wrap, WrapGenerator], file_path: str=SAVE_PATH)

Convert the record to the wav audio format.

  • Parameters:
    • record: name of the registered (wrapped) record, a 'Record' object or a (customized) wrapped record.
    • file_path: The name or path of the wav file. A new name for the record to be converted can be placed at the end of the address.
  • returns None

get_record_names
sudio.Master.get_record_names(self) -> list

returns: a list of the saved records in database


get_nperseg
sudio.Master.get_nperseg(self)

returns: number of samples per each data frame (single channel)


get_nchannels
sudio.Master.get_nchannels(self)

Returns the number of audible perspective directions or dimensions of the current wrapped record.


get_sample_rate
sudio.Master.get_sample_rate(self)

returns: current master sample rate


stream
sudio.Master.stream(self, 
                    record: Union[str, Wrap, Record, WrapGenerator],
                    block_mode: bool=False,
                    safe_load: bool=False,
                    on_stop: callable=None,
                    loop_mode: bool=False,
                    use_cached_files=True,
                    stream_mode:StreamMode = StreamMode.optimized
                    ) -> StreamControl

'Record' playback on the mainstream.

  • Parameters:

    • record: predefined record name, (customized) wrapped record, or a 'Record' object.
    • block_mode: This can be true, in which case the current thread will be blocked as long as the stream is busy.
    • safe_load: load an audio file and modify it according to the 'Master' attributes(like the frame rate, number oof channels, etc).
    • on_stop: An optional callback is called at the end of the streaming process.
    • loop_mode: playback continuously.
    • use_cached_files: enable additional cache maintaining process.
    • stream_mode: (see StreamMode enum).
  • returns StreamControl object

:memo: Note: The recorder can only capture normal streams(Non-optimized streams)


mute
sudio.Master.mute(self)

mute the stdin stream (default)


unmute
sudio.Master.unmute(self)

disable mute mode of the stdin stream


echo
sudio.Master.echo(  self, 
                    record: Union[Wrap, str, Record, WrapGenerator]=None,
                    enable: bool=None, 
                    main_output_enable: bool=False)

start to play "Record" on the operating system's default audio output.

  • Parameters:

    • record: optional, default None; It could be a predefined record name, a wrapped record, or a 'Record' object.

    • enable: optional, default None(trigger mode) determines that the standard output of the master is enable or not.

    • main_output_enable: when the 'record' is not None, controls the standard output activity of the master

  • returns self object

:memo: Note: If the 'record' argument takes the value None, the method controls the standard output activity of the master with the 'enable' argument.

:memo: Note: If the 'record' argument takes the value None, the kernel starts streaming the std input to the std output.


wrap
sudio.Master.wrap(self, record: Union[str, Record])

Create a Wrap object.

param record: preloaded record or a Record object returns: Wrap object


clean_cache
sudio.Master.clean_cache(self)

The audio data maintaining process has additional cached files to reduce dynamic memory usage and improve performance, meaning that, The audio data storage methods can have different execution times based on the cached files. This function used to clean additional cached files.

returns: self object


add_pipeline
sudio.Master.add_pipeline(  self, 
                            name: str, 
                            pip: Union[Pipeline, list],
                            process_type: sudio.types.PipelineProcessType=PipelineProcessType.MAIN, 
                            channel: int=None)

Add a new process pipeline to the master object.

  • Parameters:

    • name: string; represents the new pipeline

    • pip: obj; Pipeline object/s In the multi_stream process type, this argument must be a list of the defined pipelines, with the size equal to the nchannel.

    • process_type: PipelineProcessType; MAIN, BRANCH, MULTI_STREAM The sudio kernel inject audio data to the activated pipeline[if exist] and all of the branch type pipelines then takes output from the primary one.


      :memo: Note: Use set_pipeline to activate a main branch or a multi_stream one.

      :memo: Note: A branch pipeline is used for data monitoring(GUI) purposes.


    • channel: obj; None or [0 to nchannel]; just activated in branched pipelines;

      The input data passed to a pipeline can be an numpy array with the shape of the (number of the audio channels, 2 [number of the windows per each frame], nperseg) (in mono mode (2, self._nperseg)).

:memo: Note: the pipeline used to process data and return it to the kernel with the dimensions as same as the input.

:memo: Note: Each pipeline used to process data in different threads, so the the performance will be improved.


set_pipeline
sudio.Master.set_pipeline(self, name: str, enable: bool=True)

activate the registered pipeline on the process stream.

  • Parameters:

    • name: string; A name that represents the new pipeline
    • enable: bool; state of the primary pipeline.

:memo: Note: Only multi_stream and main branches are allowed for activation.


set_window
sudio.Master.set_window(self,
                        window: object = 'hann',
                        noverlap: int = None,
                        NOLA_check: bool = True)

change type of the current processing window.

  • Parameters:

    • window: string, float, tuple, ndarray, optional The type of window, to create (string) or pre designed window in numpy array format. use None to disable windowing process. Default ("hann") Hanning function.

    • noverlap: int, default None

      The noverlap defines the number of overlap between defined windows. If not given then it's value will be selected as

      SE = \frac{nperseg}{2}.
      

      When the length of a data set to be transformed is larger than necessary to provide the desired frequency resolution, a common practice is to subdivide it into smaller sets and window them individually.

      To mitigate the "loss" at the edges of the window, the individual sets may overlap in time.

    • NOLA_check: bool, optional Check whether the Nonzero Overlap Add (NOLA) constraint is met.(If true)


StreamControl

sudio.Master.StreamControl

The StreamControl class used to control data flow of an audio record (live control on audio streaming).


Methods


isready
sudio.Master.StreamControl.isready()

check current stream compatibility with the Master object and return true on ready to streaming.


is_streaming
sudio.Master.StreamControl.is_streaming()

return true if current stream was started before.


start
sudio.Master.StreamControl.start()

check current stream compatibility with the Master object and start to streaming.


resume
sudio.Master.StreamControl.resume()

resume current streaming if current stream was paused.


stop
sudio.Master.StreamControl.stop()

stop current streaming if current stream is activated.


pause
sudio.Master.StreamControl.pause()

pause current streaming if current stream is activated.


enable_loop
sudio.Master.StreamControl.enable_loop()

enable to restart at the end of streaming.


disable_loop
sudio.Master.StreamControl.disable_loop()

disable audio streaming loop mode.


Properties


time
sudio.Master.StreamControl.time
Getter

used to retrive elapsed time of the current streamed record.

Setter

Set the current time of streamed record.



WrapGenerator

sudio.WrapGenerator(self, master: Master, record: Union[str, AudioMetadata])

Generates a Wrap object, which wraps the raw record.


Methods

get_sample_format
sudio.WrapGenerator.get_sample_format(self) -> SampleFormat

Returns sample format of the current generator.


get_sample_width
sudio.WrapGenerator.get_sample_width(self) -> int

Returns sample width of the current wrapped record.


get_master
sudio.WrapGenerator.get_master(self) -> Master

Returns the Master object of the current generator.


get_size
sudio.WrapGenerator.get_size(self) -> int

Returns size of the currently processed record on non-volatile memory.

:memo: Note: Wrapped objects normally stored statically, so all of the calculations need additional IO read/write time, This decrese dynamic memory usage specically for big audio data.


get_cache_size
sudio.WrapGenerator.get_cache_size(self) -> int

Returns size of cached file on non-volatile memory.

:memo: Note: Wrapped objects normally stored statically, so all of the calculations need additional IO read/write time, This decrese dynamic memory usage specically for big audio data.


get_nchannels
sudio.WrapGenerator.get_nchannels(self) -> int

Returns the number of audible perspective directions or dimensions of the current wrapped record.


get_sample_rate
sudio.WrapGenerator.get_sample_rate(self) -> int

Returns frame rate of the current warpped record.


get_duration
sudio.WrapGenerator.get_duration(self) -> float

Returns the duration of the provided audio record.


join
sudio.WrapGenerator.join(self,
                        *other: Union[Wrap, WrapGenerator],
                        sync_sample_format: SampleFormat = None,
                        sync_nchannels: int = None,
                        sync_sample_rate: int = None,
                        safe_load: bool = True
                        ) -> Wrap

Returns a new wrapped record by joining and synchronizing all the elements of the 'other' iterable (Wrap, WrapGenerator), separated by the given separator.

  • parameters:

    • other: wrapped record\s.
    • sync_nchannels: number of channels; if the value is None, the target will be synced
    • sync_sample_format: if the value is None, the target will be synced to the master properties.
    • sync_sample_rate: sample rate; if the value is None, the target will be compared to the master properties.
    • safe_load: load an audio file and modify it according to the 'Master' attributes(like the frame rate, number oof channels, etc).
  • returns new Wrap object.


Magic methods

getitem
sudio.WrapGenerator.__getitem__(self, item) -> Wrap

Slicing : The Wrapped object can be sliced using the standard Python x[start: stop: step] syntax, where x is the wrapped object.

Slicing the time domain:

The basic slice syntax is

[i: j: k, i(2): j(2): k(2), i(n): j(n): k(n)] 

where i is the start time, j is the stop time in integer or float types and k is the step(negative number for inversing). This selects the nXm seconds with index times

i, i+1, i+2, ..., j, i(2), i(2)+1, ..., j(2), i(n), ..., j(n)
j where m = j - i (j > i).

:memo: Note: for i < j, i is the stop time and j is the start time, means that audio data read inversely.

Filtering (Slicing the frequency domain):

The basic slice syntax is

['i': 'j': 'filtering options', 'i(2)': 'j(2)': 'options(2)', ..., 'i(n)': 'j(n)': 'options(n)']

where i is the starting frequency and j is the stopping frequency with type of string in the same units as fs that fs is 2 half-cycles/sample. This activates n number of iir filters with specified frequencies and options.

For the slice syntax [x: y: options] we have:

  • x= None, y= 'j': low pass filter with a cutoff frequency of the j
  • x= 'i', y= None: high pass filter with a cutoff frequency of the i
  • x= 'i', y= 'j': bandpass filter with the critical frequencies of the i, j
  • x= 'i', y= 'j', options='scale=[Any negative number]': bandstop filter with the critical frequencies of the i, j

Filtering options:

  • ftype: optional; The type of IIR filter to design:\n
    • Butterworth : ‘butter’(default)
    • Chebyshev I : ‘cheby1’
    • Chebyshev II : ‘cheby2’
    • Cauer/elliptic: ‘ellip’
    • Bessel/Thomson: ‘bessel’
  • rs: float, optional:\n For Chebyshev and elliptic filters, provides the minimum attenuation in the stop band. (dB)
  • rp: float, optional:\n For Chebyshev and elliptic filters, provides the maximum ripple in the passband. (dB)
  • order: The order of the filter.(default 5)
  • scale: [float, int] optional; The attenuation or Amplification factor, that in the bandstop filter must be a negative number.

Complex slicing: The basic slice syntax is

[a: b, 'i': 'j': 'filtering options', ..., 'i(n)': 'j(n)': 'options(n)', ..., a(n): b(n), 'i': 'j': 'options', ..., 'i(n)': 'j(n)': 'options(n)'] 

or

[a: b, [Filter block 1)], a(2): b(2), [Filter block 2]  ... , a(n): b(n), [Filter block n]]

Where i is the starting frequency, j is the stopping frequency, a is the starting time and b is the stopping time in seconds. This activates n number of filter blocks [described in the filtering section] that each of them operates within a predetermined time range.

note: The sliced object is stored statically so calling the original wrapped returns The sliced object.


del
sudio.WrapGenerator.__del__(self)

Delete the current object and its dependencies (cached files, etc.)


str
sudio.WrapGenerator.__str__(self)

Returns string representation of the current object


mul
sudio.WrapGenerator.__mul__(self, scale) -> Wrap

Returns a new Wrap object, scaling the data of the current record.


truediv
sudio.WrapGenerator.__truediv__(self, scale)

Returns a new Wrap object, dividing the data of the current record.


pow
sudio.WrapGenerator.__pow__(self, power, modulo=None)

Returns a new Wrap object, scaling the data of the current record.


add
sudio.WrapGenerator.__add__(self, other:Union[Wrap, WrapGenerator, int, float])

if the 'other' parameter is a WrapGenerator or a Wrap object this method joins the current object to the other one, otherwise this method used to Return a new Wrap object, scaling the data of the current record.


sub
sudio.WrapGenerator.__sub__(self, other: Union[float, int])

Returns a new Wrap object, subtracting the data of the current record.


call
sudio.WrapGenerator.__call__(self,
                            *args,
                            sync_sample_format_id: int = None,
                            sync_nchannels: int = None,
                            sync_sample_rate: int = None,
                            safe_load: bool = True
                            ) -> Wrap

Synchronize the current object with the master (optional) and create a new Wrap object.

  • parameters:

    • sync_nchannels: number of channels; if the value is None, the target will be synced
    • sync_sample_format_id: if the value is None, the target will be synced to the master properties.
    • sync_sample_rate: sample rate; if the value is None, the target will be compared to the master properties.
    • safe_load: load an audio file and modify it according to the 'Master' attributes(like the frame rate, number oof channels, etc).
  • returns new Wrap object.

:memo: Note: Wrapped objects normally stored statically, so all of the calculations need additional IO read/write time, This decrese dynamic memory usage specically for big audio data.



Wrap

sudio.Wrap(self, master: Master, record: AudioMetadata, generator: WrapGenerator)

Methods

get_sample_format
sudio.Wrap.get_sample_format(self) -> SampleFormat

Returns sample format of the current warpped record.


get_sample_width
sudio.Wrap.get_sample_width(self) -> int

Returns sample width.


get_master
sudio.Wrap.get_master(self) -> Master

Returns the Master object.


get_size
sudio.Wrap.get_size(self) -> int

Returns size of the currently processed record on non-volatile memory.

:memo: Note: Wrapped objects normally stored statically, so all of the calculations need additional IO read/write time, This decrese dynamic memory usage specically for big audio data.


get_sample_rate
sudio.Wrap.get_sample_rate(self) -> int

Returns frame rate of the current warpped record.


get_nchannels
sudio.Wrap.get_nchannels(self) -> int

Returns the number of audible perspective directions or dimensions of the current wrapped record.


get_duration
sudio.Wrap.get_duration(self) -> float

Returns the duration of the provided audio record.


join
sudio.Wrap.join(self, *other) -> Wrap

Returns a new wrapped record by joining and synchronizing all the elements of the 'other' iterable (Wrap, WrapGenerator), separated by the given separator.


unpack
@contextmanager
    def unpack(self, reset=False, astype:SampleFormat=SampleFormat.formatUnknown) -> np.ndarray:

Unpack audio data from cached files to the dynamic memory.

:memo: Note: All calculations in the unpacked block are performed on the precached files (not the original audio data).

  • parameters:
    • reset: Reset the audio pointer to time 0 (Equivalent to slice '[:]').
  • Returns audio data in ndarray format with shape of (number of audio channels, block size).
master = Master()
wrapgen = master.add('file.mp3')
wrap = wrapgen()
with wrap.unpack() as data:
    wrap.set_data(data * .7)
master.echo(wrap)

get_data
sudio.Wrap.get_data(self) -> Union[AudioMetadata, numpy.ndarray]

if the current object is unpacked:

  • Returns the audio data in a ndarray format with shape of (number of audio channels, block size).

otherwise:

  • Returns the current record.

is_packed
sudio.Wrap.is_packed(self) -> bool

Returns true if the Wrap object is packed.


get
@contextmanager
sudio.Wrap.get(self, offset=None, whence=None)

Returns the audio data as a _io.BufferedRandom IO file.


set_data
sudio.Wrap.set_data(self, data: numpy.ndarray)

Set audio data for current wrapped record (object must be unpacked to the volatile memory).


Magic methods

del
sudio.Wrap.__del__(self)

Delete the current object and its dependencies (cached files, etc.)


str
sudio.Wrap.__str__(self)

Returns string representation of the current object


getitem
sudio.Wrap.__getitem__(self, item) -> self

Slicing : The Wrapped object can be sliced using the standard Python x[start: stop: step] syntax, where x is the wrapped object.

Slicing the time domain:

The basic slice syntax is

[i: j: k, i(2): j(2): k(2), i(n): j(n): k(n)] 

where i is the start time, j is the stop time in integer or float types and k is the step(negative number for inversing). This selects the nXm seconds with index times

i, i+1, i+2, ..., j, i(2), i(2)+1, ..., j(2), i(n), ..., j(n)
j where m = j - i (j > i).

:memo: Note: for i < j, i is the stop time and j is the start time, means that audio data read inversely.

Filtering (Slicing the frequency domain):

The basic slice syntax is

['i': 'j': 'filtering options', 'i(2)': 'j(2)': 'options(2)', ..., 'i(n)': 'j(n)': 'options(n)']

where i is the starting frequency and j is the stopping frequency with type of string in the same units as fs that fs is 2 half-cycles/sample. This activates n number of iir filters with specified frequencies and options.

For the slice syntax [x: y: options] we have:

  • x= None, y= 'j': low pass filter with a cutoff frequency of the j
  • x= 'i', y= None: high pass filter with a cutoff frequency of the i
  • x= 'i', y= 'j': bandpass filter with the critical frequencies of the i, j
  • x= 'i', y= 'j', options='scale=[Any negative number]': bandstop filter with the critical frequencies of the i, j

Filtering options:

  • ftype: optional; The type of IIR filter to design:\n
    • Butterworth : ‘butter’(default)
    • Chebyshev I : ‘cheby1’
    • Chebyshev II : ‘cheby2’
    • Cauer/elliptic: ‘ellip’
    • Bessel/Thomson: ‘bessel’
  • rs: float, optional:\n For Chebyshev and elliptic filters, provides the minimum attenuation in the stop band. (dB)
  • rp: float, optional:\n For Chebyshev and elliptic filters, provides the maximum ripple in the passband. (dB)
  • order: The order of the filter.(default 5)
  • scale: [float, int] optional; The attenuation or Amplification factor, that in the bandstop filter must be a negative number.

Complex slicing: The basic slice syntax is

[a: b, 'i': 'j': 'filtering options', ..., 'i(n)': 'j(n)': 'options(n)', ..., a(n): b(n), 'i': 'j': 'options', ..., 'i(n)': 'j(n)': 'options(n)'] 

or

[a: b, [Filter block 1)], a(2): b(2), [Filter block 2]  ... , a(n): b(n), [Filter block n]]

Where i is the starting frequency, j is the stopping frequency, a is the starting time and b is the stopping time in seconds. This activates n number of filter blocks [described in the filtering section] that each of them operates within a predetermined time range.

note: The sliced object is stored statically so calling the original wrapped returns The sliced object.


mul
sudio.Wrap.__mul__(self, scale) -> Wrap

Returns current object, dividing the data of the current record.


truediv
sudio.Wrap.__truediv__(self, scale)

Returns current object, dividing the data of the current record.


pow
sudio.Wrap.__pow__(self, power, modulo=None)

Returns a new Wrap object, scaling the data of the current record.


add
sudio.Wrap.__add__(self, other:Union[Wrap, WrapGenerator, int, float])

if the 'other' parameter is a WrapGenerator or a Wrap object this method joins the current object to the other one, otherwise this method used to Return a current object, scaling the data of the current record.


sub
sudio.Wrap.__sub__(self, other: Union[float, int])

Returns current object, subtracting the data of the current record.




Pipeline

A pipeline is a system of pipes used to transport data, each pipe is a method that processes data and pass it to the next one.

Pipeline helps the audio processing algorithms to break the complexity into smaller blocks, and the use of threading techniques improves the overall performance of the system.

sudio.Pipeline
  graph LR;
      A(Input Queue)-->B(Pipe 0);
      B-->C(Pipe 1);
      C-->D(...);
      D-->E(Output Queue);

Parameters

  • max_size: int, optional Maximum number of callable per Pipeline object


  • io_buffer_size: int, optional Maximum size of the I/O queues.

  • on_busy: [float, str], optional Determines the behavior of the pipeline after the I/O queues are full. (busy state).

    Please use:

    • "drop" to drop old data from the output queue and allocate new space for new ones.
    • "block" to block the pipeline until the last data arrives.
    • timeout in float type to block the pipeline until the end time.

  • list_dispatch: bool, optional dispatch the input list type, to the arguments of the first pipe.


Methods

clear
sudio.Pipeline.clear(self)

Remove all of the items from the I/O queues in block mode.


run
sudio.Pipeline.run(self)

Start data injection into pipeline.


insert
sudio.Pipeline.insert(self,
                      index: int,
                      *func: callable,
                      args: Union[list, tuple, object] = (),
                      init: Union[list, tuple, callable] = ())

Insert callable/s before index.

Parameters
  • index: int

    index value.


  • func: callable

  • Pipeline callable element/s.


  • args: (tuple, list, object), optional

    the static argument/s, will be passed to the pipeline element/s.

    example:

    • multiple arguments for multiple callables

      def f0(arg0, arg1, data):
        return data
      
      def f1(arg, data):
        return data
      
      pip = sudio.Pipeline()
      pip.insert(2, f0, f1, args=(['arg0 for f0', 'arg1 for f0'], 'single arg for f1'))
      
    • single argument for multiple callables

      def f0(arg, data):
        return data
      
      def f1(arg, data):
        return data
      
      pip = sudio.Pipeline()
      pip.insert(2, f0, f1, args='single arg for all')
      
      def f0(data):
        return data
      
      def f1(arg, data):
        return data
      
      pip = sudio.Pipeline()
      pip.insert(2, f0, f1, args=(None, 'single arg for f1'))
      
    • single argument for single callable

      def f0(arg, data):
        return data
      
      pip = sudio.Pipeline()
      pip.insert(2, f0, args='single arg for f0')
      

  • init: (list, tuple, callable), optional

    single or multiple callables suggested for access to the pipeline's shared memory, that called after starting pipeline thread execution.

Reterns
  • self object

append
sudio.Pipeline.append(self,
                      *func: callable,
                      args: Union[list, tuple, object] = (),
                      init: Union[list, tuple, callable] = ())

Append callable/s to the end of the pipeline. For more detailes Please refer to the sudio.Pipeline.insert method.


sync
sudio.Pipeline.sync(self, barrier: threading.Barrier)

Synchronize current pipeline with others using a barrier.


aasync
sudio.Pipeline.aasync(self)

Disable pipeline synchronization.


delay
sudio.Pipeline.delay(self)

used to retrive pipeline execution delay in us.


set_timeout
sudio.Pipeline.set_timeout(self, t: Union[float, int])

Determines the blocking timeout of the pipeline after the I/O queues are full.


get_timeout
sudio.Pipeline.get_timeout(self)

used to retrive the blocking timeout of the pipeline after the I/O queues are full.


put
sudio.Pipeline.put(self, data)

Inject new data into the pipeline object


get
sudio.Pipeline.get(self, block=True, timeout=None):

Remove and return an item from the output queue.

If optional args 'block' is true and 'timeout' is None (the default), block if necessary until an item is available. If 'timeout' is a non-negative number, it blocks at most 'timeout' seconds and raises the Empty exception if no item was available within that time. Otherwise ('block' is false), return an item if one is immediately available, else raise the Empty exception ('timeout' is ignored in that case).


Magic methods


call
sudio.Pipeline.__call__(self, data)

The __ call __ magic method used to Inject data into the current pipeline object


delitem
sudio.Pipeline.__delitem__(self, key)

Delete self[key].


len
sudio.Pipeline.__len__(self)

Return len(self).


getitem
sudio.Pipeline.__getitem__(self, key)

Return self[key].


LICENCE

Open Source (OSI approved): Apache License 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sudio-1.0.9.36.tar.gz (312.0 kB view hashes)

Uploaded Source

Built Distributions

sudio-1.0.9.36-cp312-cp312-win_amd64.whl (1.5 MB view hashes)

Uploaded CPython 3.12 Windows x86-64

sudio-1.0.9.36-cp312-cp312-win32.whl (1.5 MB view hashes)

Uploaded CPython 3.12 Windows x86

sudio-1.0.9.36-cp312-cp312-musllinux_1_2_x86_64.whl (2.4 MB view hashes)

Uploaded CPython 3.12 musllinux: musl 1.2+ x86-64

sudio-1.0.9.36-cp312-cp312-musllinux_1_1_i686.whl (2.1 MB view hashes)

Uploaded CPython 3.12 musllinux: musl 1.1+ i686

sudio-1.0.9.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view hashes)

Uploaded CPython 3.12 manylinux: glibc 2.17+ x86-64

sudio-1.0.9.36-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl (1.5 MB view hashes)

Uploaded CPython 3.12 manylinux: glibc 2.17+ i686

sudio-1.0.9.36-cp312-cp312-macosx_11_0_arm64.whl (1.5 MB view hashes)

Uploaded CPython 3.12 macOS 11.0+ ARM64

sudio-1.0.9.36-cp312-cp312-macosx_10_9_x86_64.whl (1.5 MB view hashes)

Uploaded CPython 3.12 macOS 10.9+ x86-64

sudio-1.0.9.36-cp311-cp311-win_amd64.whl (1.5 MB view hashes)

Uploaded CPython 3.11 Windows x86-64

sudio-1.0.9.36-cp311-cp311-win32.whl (1.5 MB view hashes)

Uploaded CPython 3.11 Windows x86

sudio-1.0.9.36-cp311-cp311-musllinux_1_2_x86_64.whl (2.4 MB view hashes)

Uploaded CPython 3.11 musllinux: musl 1.2+ x86-64

sudio-1.0.9.36-cp311-cp311-musllinux_1_1_i686.whl (2.1 MB view hashes)

Uploaded CPython 3.11 musllinux: musl 1.1+ i686

sudio-1.0.9.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view hashes)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

sudio-1.0.9.36-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl (1.5 MB view hashes)

Uploaded CPython 3.11 manylinux: glibc 2.17+ i686

sudio-1.0.9.36-cp311-cp311-macosx_11_0_arm64.whl (1.5 MB view hashes)

Uploaded CPython 3.11 macOS 11.0+ ARM64

sudio-1.0.9.36-cp311-cp311-macosx_10_9_x86_64.whl (1.5 MB view hashes)

Uploaded CPython 3.11 macOS 10.9+ x86-64

sudio-1.0.9.36-cp310-cp310-win_amd64.whl (1.5 MB view hashes)

Uploaded CPython 3.10 Windows x86-64

sudio-1.0.9.36-cp310-cp310-win32.whl (1.5 MB view hashes)

Uploaded CPython 3.10 Windows x86

sudio-1.0.9.36-cp310-cp310-musllinux_1_2_x86_64.whl (2.4 MB view hashes)

Uploaded CPython 3.10 musllinux: musl 1.2+ x86-64

sudio-1.0.9.36-cp310-cp310-musllinux_1_1_i686.whl (2.1 MB view hashes)

Uploaded CPython 3.10 musllinux: musl 1.1+ i686

sudio-1.0.9.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view hashes)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

sudio-1.0.9.36-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl (1.5 MB view hashes)

Uploaded CPython 3.10 manylinux: glibc 2.17+ i686

sudio-1.0.9.36-cp310-cp310-macosx_11_0_arm64.whl (1.5 MB view hashes)

Uploaded CPython 3.10 macOS 11.0+ ARM64

sudio-1.0.9.36-cp310-cp310-macosx_10_9_x86_64.whl (1.5 MB view hashes)

Uploaded CPython 3.10 macOS 10.9+ x86-64

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page