Skip to content

Commit

Permalink
Created BassFlag and BassInitializer to organize audio player.
Browse files Browse the repository at this point in the history
  • Loading branch information
MahmoudAtef999 committed Dec 24, 2024
1 parent e7c1fa6 commit 64b690c
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 14 deletions.
46 changes: 46 additions & 0 deletions utils/audio_player/bass_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import ctypes
from ctypes import c_int, c_longlong, c_void_p, c_uint
from enum import IntFlag
from exceptions.audio_pplayer import PlaybackInitializationError


class BassFlag(IntFlag):
AUTO_FREE = 0x40000 # Automatically free the stream when it stops/ends
STREAM_BLOCK = 0x100000 # Download/play internet file stream in small blocks


class BassInitializer:
def __init__(self, bass_library_path: str = "bass.dll"):
"""Initialize the BassInitializer object without loading the library or initializing BASS."""
self.bass_library_path = os.path.abspath(bass_library_path)
self.bass = None
self.setup()

def setup(self):
"""Prepares the BASS environment by loading the DLL and setting up argument types."""
if not os.path.exists(self.bass_library_path):
raise FileNotFoundError(f"BASS library not found at {self.bass_library_path}")

# Load the BASS library
self.bass = ctypes.CDLL(self.bass_library_path)

# Setup argument and return types for BASS functions
self.bass.BASS_StreamCreateFile.argtypes = [c_int, c_void_p, c_longlong, c_longlong, c_uint]
self.bass.BASS_StreamCreateFile.restype = c_int
self.bass.BASS_ErrorGetCode.restype = c_int

def initialize(self):
"""Initializes BASS with error handling."""
if not self.bass:
raise RuntimeError("BASS library is not set up. Call `setup()` first.")

if not self.bass.BASS_Init(-1, 44100, 0, 0, 0):
raise PlaybackInitializationError()

return self.bass

def close(self):
"""Shuts down and frees resources used by BASS."""
if self.bass:
self.bass.BASS_Free()
21 changes: 7 additions & 14 deletions utils/audio_player/bass_player.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,30 @@
import os
import time
import ctypes
from ctypes import c_int, c_int64, c_long, c_uint, c_longlong, c_void_p
func_type = ctypes.WINFUNCTYPE
QWORD = ctypes.c_int64

from typing import List, Optional
from urllib.parse import urlparse
from .status import PlaybackStatus
from .bass_init import BassInitializer, BassFlag
from exceptions.audio_pplayer import (
AudioFileNotFoundError, LoadFileError, UnsupportedFormatError, PlaybackControlError,
InvalidSourceError, PlaybackInitializationError, PlaybackControlError
)

# Load bass.dll
bass_path = os.path.abspath("bass.dll")
bass = ctypes.CDLL(bass_path)

# Constants
BASS_STREAM_AUTOFREE = 0x40000 # Automatically free the stream when it stops/ends

# Initialize BASS
if not bass.BASS_Init(-1, 44100, 0, 0, 0):
raise PlaybackInitializationError()
bass = BassInitializer().initialize()

class AudioPlayer:
instances = []

def __init__(self, volume: float, auto_free: bool =True) -> None:
def __init__(self, volume: float, flag: int = BassFlag.AUTO_FREE) -> None:
self.source: Optional[str] = None
self.current_channel: Optional[int] = None
self.volume = volume
self.supported_extensions = ('.wav', '.mp3', '.ogg')
self.auto_free = auto_free
self.flag = flag
self.BASS_ChannelBytes2Seconds = func_type(ctypes.c_double, ctypes.c_ulong, QWORD)(('BASS_ChannelBytes2Seconds', bass))
self.BASS_ChannelSeconds2Bytes = func_type(QWORD, ctypes.c_ulong, ctypes.c_double)(('BASS_ChannelSeconds2Bytes', bass))
self.BASS_ChannelSetPosition = func_type(ctypes.c_bool, ctypes.c_ulong, QWORD, ctypes.c_ulong)(('BASS_ChannelSetPosition', bass))
Expand All @@ -54,12 +47,12 @@ def load_audio(self, source: str, attempts: Optional[int] = 3) -> None:
parsed_url = urlparse(source)
if parsed_url.scheme in ("http", "https") and parsed_url.netloc:
# Stream from URL
self.current_channel = bass.BASS_StreamCreateURL(source.encode(), 0, BASS_STREAM_AUTOFREE if self.auto_free else 0, None, None)
self.current_channel = bass.BASS_StreamCreateURL(source.encode(), 0, self.flag, None, None)
else:
# Load from local file
if not os.path.isfile(source):
raise AudioFileNotFoundError(source)
self.current_channel = bass.BASS_StreamCreateFile(False, source.encode('utf-8'), 0, 0, BASS_STREAM_AUTOFREE if self.auto_free else 0)
self.current_channel = bass.BASS_StreamCreateFile(False, source.encode('utf-8'), 0, 0, self.flag)

if not self.current_channel:
if attempts:
Expand Down

0 comments on commit 64b690c

Please sign in to comment.