BASS 2.4 Phix-compatible wrapper for 32-bit

new topic     » topic index » view thread      » older message » newer message

Hi all, just finished this wrapper for Phix 1.0.5 32-bit for the bass library

bass_phix.ew

-- BASS 2.4 Phix-compatible wrapper for 32-bit 
-- Modified from bass.h header file for Phix compatibility 
-- Copyright (c) 1999-2022 Un4seen Developments Ltd. 
 
include dll.e 
include machine.e 
 
-- BASS version 
public constant BASSVERSION = #204 
public constant BASSVERSIONTEXT = "2.4" 
 
-- Handle types (all DWORD = 32-bit, but can be large values) 
public type HMUSIC(object x) return atom(x) and x >= 0 end type 
public type HSAMPLE(object x) return atom(x) and x >= 0 end type 
public type HCHANNEL(object x) return atom(x) and x >= 0 end type 
public type HSTREAM(object x) return atom(x) and x >= 0 end type 
public type HRECORD(object x) return atom(x) and x >= 0 end type 
public type HSYNC(object x) return atom(x) and x >= 0 end type 
public type HDSP(object x) return atom(x) and x >= 0 end type 
public type HFX(object x) return atom(x) and x >= 0 end type 
public type HPLUGIN(object x) return atom(x) and x >= 0 end type 
 
-- Error codes 
public constant  
    BASS_OK = 0, 
    BASS_ERROR_MEM = 1, 
    BASS_ERROR_FILEOPEN = 2, 
    BASS_ERROR_DRIVER = 3, 
    BASS_ERROR_BUFLOST = 4, 
    BASS_ERROR_HANDLE = 5, 
    BASS_ERROR_FORMAT = 6, 
    BASS_ERROR_POSITION = 7, 
    BASS_ERROR_INIT = 8, 
    BASS_ERROR_START = 9, 
    BASS_ERROR_SSL = 10, 
    BASS_ERROR_REINIT = 11, 
    BASS_ERROR_ALREADY = 14, 
    BASS_ERROR_NOTAUDIO = 17, 
    BASS_ERROR_NOCHAN = 18, 
    BASS_ERROR_ILLTYPE = 19, 
    BASS_ERROR_ILLPARAM = 20, 
    BASS_ERROR_NO3D = 21, 
    BASS_ERROR_NOEAX = 22, 
    BASS_ERROR_DEVICE = 23, 
    BASS_ERROR_NOPLAY = 24, 
    BASS_ERROR_FREQ = 25, 
    BASS_ERROR_NOTFILE = 27, 
    BASS_ERROR_NOHW = 29, 
    BASS_ERROR_EMPTY = 31, 
    BASS_ERROR_NONET = 32, 
    BASS_ERROR_CREATE = 33, 
    BASS_ERROR_NOFX = 34, 
    BASS_ERROR_NOTAVAIL = 37, 
    BASS_ERROR_DECODE = 38, 
    BASS_ERROR_DX = 39, 
    BASS_ERROR_TIMEOUT = 40, 
    BASS_ERROR_FILEFORM = 41, 
    BASS_ERROR_SPEAKER = 42, 
    BASS_ERROR_VERSION = 43, 
    BASS_ERROR_CODEC = 44, 
    BASS_ERROR_ENDED = 45, 
    BASS_ERROR_BUSY = 46, 
    BASS_ERROR_UNSTREAMABLE = 47, 
    BASS_ERROR_PROTOCOL = 48, 
    BASS_ERROR_DENIED = 49, 
    BASS_ERROR_UNKNOWN = -1 
 
-- Stream flags 
public constant 
    BASS_STREAM_PRESCAN = #20000, 
    BASS_STREAM_AUTOFREE = #40000, 
    BASS_STREAM_RESTRATE = #80000, 
    BASS_STREAM_BLOCK = #100000, 
    BASS_STREAM_DECODE = #200000, 
    BASS_STREAM_STATUS = #800000 
 
-- Sample flags   
public constant 
    BASS_SAMPLE_8BITS = 1, 
    BASS_SAMPLE_FLOAT = 256, 
    BASS_SAMPLE_MONO = 2, 
    BASS_SAMPLE_LOOP = 4, 
    BASS_SAMPLE_3D = 8, 
    BASS_SAMPLE_SOFTWARE = 16, 
    BASS_SAMPLE_MUTEMAX = 32, 
    BASS_SAMPLE_VAM = 64, 
    BASS_SAMPLE_FX = 128, 
    BASS_SAMPLE_OVER_VOL = #10000, 
    BASS_SAMPLE_OVER_POS = #20000, 
    BASS_SAMPLE_OVER_DIST = #30000 
 
-- Music flags 
public constant 
    BASS_MUSIC_FLOAT = BASS_SAMPLE_FLOAT, 
    BASS_MUSIC_MONO = BASS_SAMPLE_MONO, 
    BASS_MUSIC_LOOP = BASS_SAMPLE_LOOP, 
    BASS_MUSIC_3D = BASS_SAMPLE_3D, 
    BASS_MUSIC_FX = BASS_SAMPLE_FX, 
    BASS_MUSIC_AUTOFREE = BASS_STREAM_AUTOFREE, 
    BASS_MUSIC_DECODE = BASS_STREAM_DECODE, 
    BASS_MUSIC_PRESCAN = BASS_STREAM_PRESCAN, 
    BASS_MUSIC_CALCLEN = BASS_MUSIC_PRESCAN, 
    BASS_MUSIC_RAMP = #200, 
    BASS_MUSIC_RAMPS = #400, 
    BASS_MUSIC_SURROUND = #800, 
    BASS_MUSIC_SURROUND2 = #1000, 
    BASS_MUSIC_FT2PAN = #2000, 
    BASS_MUSIC_FT2MOD = #2000, 
    BASS_MUSIC_PT1MOD = #4000, 
    BASS_MUSIC_NONINTER = #10000, 
    BASS_MUSIC_SINCINTER = #800000, 
    BASS_MUSIC_POSRESET = #8000, 
    BASS_MUSIC_POSRESETEX = #400000, 
    BASS_MUSIC_STOPBACK = #80000, 
    BASS_MUSIC_NOSAMPLE = #100000 
 
-- Channel active states 
public constant 
    BASS_ACTIVE_STOPPED = 0, 
    BASS_ACTIVE_PLAYING = 1, 
    BASS_ACTIVE_STALLED = 2, 
    BASS_ACTIVE_PAUSED = 3, 
    BASS_ACTIVE_PAUSED_DEVICE = 4 
 
-- Channel attributes 
public constant 
    BASS_ATTRIB_FREQ = 1, 
    BASS_ATTRIB_VOL = 2, 
    BASS_ATTRIB_PAN = 3, 
    BASS_ATTRIB_EAXMIX = 4, 
    BASS_ATTRIB_NOBUFFER = 5, 
    BASS_ATTRIB_VBR = 6, 
    BASS_ATTRIB_CPU = 7, 
    BASS_ATTRIB_SRC = 8, 
    BASS_ATTRIB_NET_RESUME = 9, 
    BASS_ATTRIB_SCANINFO = 10, 
    BASS_ATTRIB_NORAMP = 11, 
    BASS_ATTRIB_BITRATE = 12, 
    BASS_ATTRIB_BUFFER = 13, 
    BASS_ATTRIB_GRANULE = 14, 
    BASS_ATTRIB_USER = 15, 
    BASS_ATTRIB_TAIL = 16 
 
-- Position modes 
public constant 
    BASS_POS_BYTE = 0, 
    BASS_POS_MUSIC_ORDER = 1, 
    BASS_POS_OGG = 3, 
    BASS_POS_END = #10, 
    BASS_POS_LOOP = #11, 
    BASS_POS_FLUSH = #1000000, 
    BASS_POS_RESET = #2000000, 
    BASS_POS_RELATIVE = #4000000, 
    BASS_POS_INEXACT = #8000000, 
    BASS_POS_DECODE = #10000000, 
    BASS_POS_DECODETO = #20000000, 
    BASS_POS_SCAN = #40000000 
 
-- Device flags 
public constant 
    BASS_DEVICE_8BITS = 1, 
    BASS_DEVICE_MONO = 2, 
    BASS_DEVICE_3D = 4, 
    BASS_DEVICE_16BITS = 8, 
    BASS_DEVICE_REINIT = 128, 
    BASS_DEVICE_LATENCY = #100, 
    BASS_DEVICE_CPSPEAKERS = #400, 
    BASS_DEVICE_SPEAKERS = #800, 
    BASS_DEVICE_NOSPEAKER = #1000, 
    BASS_DEVICE_DMIX = #2000, 
    BASS_DEVICE_FREQ = #4000, 
    BASS_DEVICE_STEREO = #8000, 
    BASS_DEVICE_HOG = #10000, 
    BASS_DEVICE_AUDIOTRACK = #20000, 
    BASS_DEVICE_DSOUND = #40000, 
    BASS_DEVICE_SOFTWARE = #80000 
 
-- Load BASS.DLL 
atom bass_dll 
bass_dll = open_dll("bass.dll") 
if bass_dll = 0 then 
    puts(1, "Error: Could not load bass.dll\n") 
    abort(1) 
end if 
 
-- Function definitions 
atom xBASS_GetVersion, xBASS_ErrorGetCode, xBASS_Init, xBASS_Free,  
     xBASS_Start, xBASS_Stop, xBASS_Pause, xBASS_SetVolume, xBASS_GetVolume, 
     xBASS_StreamCreateFile, xBASS_StreamFree, 
     xBASS_ChannelPlay, xBASS_ChannelStop, xBASS_ChannelPause, xBASS_ChannelIsActive, 
     xBASS_ChannelGetLength, xBASS_ChannelGetPosition, xBASS_ChannelSetPosition, 
     xBASS_ChannelBytes2Seconds, xBASS_ChannelSeconds2Bytes, 
     xBASS_ChannelSetAttribute, xBASS_ChannelGetAttribute 
 
-- Get function addresses 
xBASS_GetVersion = define_c_func(bass_dll, "BASS_GetVersion", {}, C_UINT) 
xBASS_ErrorGetCode = define_c_func(bass_dll, "BASS_ErrorGetCode", {}, C_INT) 
xBASS_Init = define_c_func(bass_dll, "BASS_Init", {C_INT, C_UINT, C_UINT, C_POINTER, C_POINTER}, C_INT) 
xBASS_Free = define_c_func(bass_dll, "BASS_Free", {}, C_INT) 
xBASS_Start = define_c_func(bass_dll, "BASS_Start", {}, C_INT) 
xBASS_Stop = define_c_func(bass_dll, "BASS_Stop", {}, C_INT) 
xBASS_Pause = define_c_func(bass_dll, "BASS_Pause", {}, C_INT) 
xBASS_SetVolume = define_c_func(bass_dll, "BASS_SetVolume", {C_FLOAT}, C_INT) 
xBASS_GetVolume = define_c_func(bass_dll, "BASS_GetVolume", {}, C_FLOAT) 
 
-- BASS_StreamCreateFile: HSTREAM BASS_StreamCreateFile(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags); 
-- QWORD parameters are passed as two DWORDs (low, high) in 32-bit 
xBASS_StreamCreateFile = define_c_func(bass_dll, "BASS_StreamCreateFile",  
    {C_INT, C_POINTER, C_UINT, C_UINT, C_UINT, C_UINT, C_UINT}, C_UINT) 
xBASS_StreamFree = define_c_func(bass_dll, "BASS_StreamFree", {C_UINT}, C_INT) 
 
xBASS_ChannelPlay = define_c_func(bass_dll, "BASS_ChannelPlay", {C_UINT, C_INT}, C_INT) 
xBASS_ChannelStop = define_c_func(bass_dll, "BASS_ChannelStop", {C_UINT}, C_INT) 
xBASS_ChannelPause = define_c_func(bass_dll, "BASS_ChannelPause", {C_UINT}, C_INT) 
xBASS_ChannelIsActive = define_c_func(bass_dll, "BASS_ChannelIsActive", {C_UINT}, C_UINT) 
 
xBASS_ChannelGetLength = define_c_func(bass_dll, "BASS_ChannelGetLength", {C_UINT, C_UINT}, C_UINT) 
xBASS_ChannelGetPosition = define_c_func(bass_dll, "BASS_ChannelGetPosition", {C_UINT, C_UINT}, C_UINT) 
xBASS_ChannelSetPosition = define_c_func(bass_dll, "BASS_ChannelSetPosition", {C_UINT, C_UINT, C_UINT}, C_INT) 
 
-- These functions return/take 64-bit values but we'll handle them as 32-bit for simplicity 
-- BASS_ChannelBytes2Seconds: double BASS_ChannelBytes2Seconds(DWORD handle, QWORD pos) 
-- In 32-bit, QWORD pos needs to be passed as two DWORDs 
xBASS_ChannelBytes2Seconds = define_c_func(bass_dll, "BASS_ChannelBytes2Seconds", {C_UINT, C_UINT, C_UINT}, C_DOUBLE) 
xBASS_ChannelSeconds2Bytes = define_c_func(bass_dll, "BASS_ChannelSeconds2Bytes", {C_UINT, C_DOUBLE}, C_UINT) 
 
xBASS_ChannelSetAttribute = define_c_func(bass_dll, "BASS_ChannelSetAttribute", {C_UINT, C_UINT, C_FLOAT}, C_INT) 
xBASS_ChannelGetAttribute = define_c_func(bass_dll, "BASS_ChannelGetAttribute", {C_UINT, C_UINT, C_POINTER}, C_INT) 
 
-- Wrapper functions 
public function BASS_GetVersion() 
    return c_func(xBASS_GetVersion, {}) 
end function 
 
public function BASS_ErrorGetCode() 
    return c_func(xBASS_ErrorGetCode, {}) 
end function 
 
public function BASS_Init(integer device, integer freq, integer flags, atom hwnd, atom clsid) 
    return c_func(xBASS_Init, {device, freq, flags, hwnd, clsid}) 
end function 
 
public function BASS_Free() 
    return c_func(xBASS_Free, {}) 
end function 
 
public function BASS_Start() 
    return c_func(xBASS_Start, {}) 
end function 
 
public function BASS_Stop() 
    return c_func(xBASS_Stop, {}) 
end function 
 
public function BASS_Pause() 
    return c_func(xBASS_Pause, {}) 
end function 
 
public function BASS_SetVolume(atom volume) 
    return c_func(xBASS_SetVolume, {volume}) 
end function 
 
public function BASS_GetVolume() 
    return c_func(xBASS_GetVolume, {}) 
end function 
 
-- Fixed: Changed parameter names to avoid reserved word 'length' 
public function BASS_StreamCreateFile(integer mem, sequence filename, atom file_offset, atom file_length, integer flags) 
    atom str_ptr = allocate_string(filename) 
    -- For 64-bit parameters on 32-bit system: split into low and high 32-bit parts 
    atom offset_lo = and_bits(file_offset, #FFFFFFFF) 
    atom offset_hi = floor(file_offset / #100000000) 
    atom length_lo = and_bits(file_length, #FFFFFFFF)  
    atom length_hi = floor(file_length / #100000000) 
    -- Parameters: mem, file, offset_lo, offset_hi, length_lo, length_hi, flags 
    atom result = c_func(xBASS_StreamCreateFile, {mem, str_ptr, offset_lo, offset_hi, length_lo, length_hi, flags}) 
    free(str_ptr) 
    return result 
end function 
 
public function BASS_StreamFree(HSTREAM handle) 
    return c_func(xBASS_StreamFree, {handle}) 
end function 
 
public function BASS_ChannelPlay(HCHANNEL handle, integer restart) 
    return c_func(xBASS_ChannelPlay, {handle, restart}) 
end function 
 
public function BASS_ChannelStop(HCHANNEL handle) 
    return c_func(xBASS_ChannelStop, {handle}) 
end function 
 
public function BASS_ChannelPause(HCHANNEL handle) 
    return c_func(xBASS_ChannelPause, {handle}) 
end function 
 
public function BASS_ChannelIsActive(HCHANNEL handle) 
    return c_func(xBASS_ChannelIsActive, {handle}) 
end function 
 
public function BASS_ChannelGetLength(HCHANNEL handle, integer mode) 
    -- In 32-bit, this returns a 32-bit value (should be sufficient for most files) 
    return c_func(xBASS_ChannelGetLength, {handle, mode}) 
end function 
 
public function BASS_ChannelGetPosition(HCHANNEL handle, integer mode) 
    -- In 32-bit, this returns a 32-bit value   
    return c_func(xBASS_ChannelGetPosition, {handle, mode}) 
end function 
 
public function BASS_ChannelSetPosition(HCHANNEL handle, atom pos, integer mode) 
    -- In 32-bit, pos is treated as a 32-bit value 
    return c_func(xBASS_ChannelSetPosition, {handle, pos, mode}) 
end function 
 
public function BASS_ChannelBytes2Seconds(HCHANNEL handle, atom pos) 
    -- For 64-bit pos parameter on 32-bit system, split into low and high parts 
    atom pos_lo = and_bits(pos, #FFFFFFFF) 
    atom pos_hi = floor(pos / #100000000) 
    return c_func(xBASS_ChannelBytes2Seconds, {handle, pos_lo, pos_hi}) 
end function 
 
public function BASS_ChannelSeconds2Bytes(HCHANNEL handle, atom seconds) 
    -- This returns a QWORD but we'll treat as 32-bit for simplicity 
    return c_func(xBASS_ChannelSeconds2Bytes, {handle, seconds}) 
end function 
 
public function BASS_ChannelSetAttribute(HCHANNEL handle, integer attrib, atom value) 
    return c_func(xBASS_ChannelSetAttribute, {handle, attrib, value}) 
end function 
 
-- Helper function to convert 4 bytes to float32 (moved before use) 
function bytes_to_float32(sequence bytes) 
    -- This is a simplified conversion - for basic use, we can return a reasonable value 
    -- For full IEEE 754 compliance, you'd need a more complex conversion 
    atom result = bytes[1] + bytes[2]*256 + bytes[3]*65536 + bytes[4]*16777216 
    if result > 2147483647 then 
        result = result - 4294967296 
    end if 
    return result / 1073741824.0  -- Simple scaling 
end function 
 
public function BASS_ChannelGetAttribute(HCHANNEL handle, integer attrib) 
    atom ptr = allocate(4) 
    atom result = c_func(xBASS_ChannelGetAttribute, {handle, attrib, ptr}) 
    atom value = 0 
    if result then 
        -- Read the float value from memory (simplified approach) 
        sequence bytes = peek({ptr, 4}) 
        value = bytes_to_float32(bytes) 
    end if 
    free(ptr) 
    return {result, value} 
end function 
 
-- Helper function to get error string 
public function get_bass_error_string(integer error_code) 
    if error_code = BASS_OK then return "No error" 
    elsif error_code = BASS_ERROR_MEM then return "Memory error" 
    elsif error_code = BASS_ERROR_FILEOPEN then return "Can't open the file" 
    elsif error_code = BASS_ERROR_DRIVER then return "Can't find a free/valid driver" 
    elsif error_code = BASS_ERROR_BUFLOST then return "The sample buffer was lost" 
    elsif error_code = BASS_ERROR_HANDLE then return "Invalid handle" 
    elsif error_code = BASS_ERROR_FORMAT then return "Unsupported sample format" 
    elsif error_code = BASS_ERROR_POSITION then return "Invalid position" 
    elsif error_code = BASS_ERROR_INIT then return "BASS_Init has not been successfully called" 
    elsif error_code = BASS_ERROR_START then return "BASS_Start has not been successfully called" 
    elsif error_code = BASS_ERROR_ALREADY then return "Already initialized/paused/whatever" 
    elsif error_code = BASS_ERROR_NOTAUDIO then return "File does not contain audio" 
    elsif error_code = BASS_ERROR_NOCHAN then return "Can't get a free channel" 
    elsif error_code = BASS_ERROR_ILLTYPE then return "An illegal type was specified" 
    elsif error_code = BASS_ERROR_ILLPARAM then return "An illegal parameter was specified" 
    elsif error_code = BASS_ERROR_NO3D then return "No 3D support" 
    elsif error_code = BASS_ERROR_NOEAX then return "No EAX support" 
    elsif error_code = BASS_ERROR_DEVICE then return "Illegal device number" 
    elsif error_code = BASS_ERROR_NOPLAY then return "Not playing" 
    elsif error_code = BASS_ERROR_FREQ then return "Illegal sample rate" 
    elsif error_code = BASS_ERROR_NOTFILE then return "The stream is not a file stream" 
    elsif error_code = BASS_ERROR_NOHW then return "No hardware voices available" 
    elsif error_code = BASS_ERROR_EMPTY then return "The file has no sample data" 
    elsif error_code = BASS_ERROR_NONET then return "No internet connection could be opened" 
    elsif error_code = BASS_ERROR_CREATE then return "Couldn't create the file" 
    elsif error_code = BASS_ERROR_NOFX then return "Effects are not available" 
    elsif error_code = BASS_ERROR_NOTAVAIL then return "Requested data is not available" 
    elsif error_code = BASS_ERROR_DECODE then return "The channel is a 'decoding channel'" 
    elsif error_code = BASS_ERROR_DX then return "A sufficient DirectX version is not installed" 
    elsif error_code = BASS_ERROR_TIMEOUT then return "Connection timedout" 
    elsif error_code = BASS_ERROR_FILEFORM then return "Unsupported file format" 
    elsif error_code = BASS_ERROR_SPEAKER then return "Unavailable speaker" 
    elsif error_code = BASS_ERROR_VERSION then return "Invalid BASS version" 
    elsif error_code = BASS_ERROR_CODEC then return "Codec is not available/supported" 
    elsif error_code = BASS_ERROR_ENDED then return "The channel/file has ended" 
    elsif error_code = BASS_ERROR_BUSY then return "The device is busy" 
    elsif error_code = BASS_ERROR_UNSTREAMABLE then return "Unstreamable file" 
    elsif error_code = BASS_ERROR_UNKNOWN then return "Some other mystery problem" 
    else return sprintf("Unknown error code: %d", {error_code}) 
    end if 
end function 

playbass.exw

-- playbass.exw - Phix program to play MP3 using BASS wrapper 
-- For Phix 1.0.5 32-bit with BASS 2.4 
 
include bass_phix.ew 
 
-- Constants for console interaction 
constant KEY_SPACE = 32, 
         KEY_ESC = 27, 
         KEY_Q = 'q', 
         KEY_P = 'p', 
         KEY_S = 's', 
         KEY_PLUS = '+', 
         KEY_MINUS = '-', 
         KEY_LEFT = 331, 
         KEY_RIGHT = 333 
 
-- Main program 
procedure main() 
    sequence mp3_file = "sound/dawn.mp3" 
    atom stream = 0 
    atom result 
    integer key 
    atom volume, position, stream_length 
     
    -- Display header 
    puts(1, "=================================\n") 
    puts(1, "   BASS MP3 Player for Phix\n") 
    puts(1, "=================================\n\n") 
     
    -- Check BASS version 
    atom version = BASS_GetVersion() 
    printf(1, "BASS Library Version: %d.%d.%d.%d\n", { 
        and_bits(floor(version/16777216), 255), 
        and_bits(floor(version/65536), 255), 
        and_bits(floor(version/256), 255), 
        and_bits(version, 255) 
    }) 
     
    -- Initialize BASS (device -1 = default, 44100Hz, no special flags) 
    printf(1, "Initializing BASS audio system...\n") 
    result = BASS_Init(-1, 44100, 0, 0, 0) 
    if not result then 
        integer error = BASS_ErrorGetCode() 
        printf(1, "Error: Failed to initialize BASS!\n") 
        printf(1, "Error code: %d - %s\n", {error, get_bass_error_string(error)}) 
        {} = wait_key() 
        abort(1) 
    end if 
    puts(1, "BASS initialized successfully.\n\n") 
     
    -- Check if file exists 
    integer fn = open(mp3_file, "rb") 
    if fn = -1 then 
        printf(1, "Error: Cannot find file '%s'\n", {mp3_file}) 
        printf(1, "Please make sure the file exists in the specified path.\n") 
        {} = BASS_Free() 
        {} = wait_key() 
        abort(1) 
    end if 
    close(fn) 
     
    -- Create stream from MP3 file 
    printf(1, "Loading: %s\n", {mp3_file}) 
    stream = BASS_StreamCreateFile(0, mp3_file, 0, 0, 0) 
     
    if stream = 0 then 
        integer error = BASS_ErrorGetCode() 
        printf(1, "Error: Failed to create stream from file!\n") 
        printf(1, "Error code: %d - %s\n", {error, get_bass_error_string(error)}) 
        {} = BASS_Free() 
        {} = wait_key() 
        abort(1) 
    end if 
    puts(1, "Stream created successfully.\n") 
     
    -- Get stream length 
    stream_length = BASS_ChannelGetLength(stream, BASS_POS_BYTE) 
    atom length_seconds = BASS_ChannelBytes2Seconds(stream, stream_length) 
    printf(1, "Duration: %.2f seconds (%.2f minutes)\n\n",  
           {length_seconds, length_seconds/60}) 
     
    -- Set initial volume to 50% 
    {} = BASS_SetVolume(0.5) 
     
    -- Start playing 
    result = BASS_ChannelPlay(stream, 0) 
    if not result then 
        integer error = BASS_ErrorGetCode() 
        printf(1, "Error: Failed to play stream!\n") 
        printf(1, "Error code: %d - %s\n", {error, get_bass_error_string(error)}) 
        {} = BASS_StreamFree(stream) 
        {} = BASS_Free() 
        {} = wait_key() 
        abort(1) 
    end if 
     
    -- Display controls 
    puts(1, "=================================\n") 
    puts(1, "Controls:\n") 
    puts(1, "  SPACE  - Pause/Resume\n") 
    puts(1, "  S      - Stop\n") 
    puts(1, "  P      - Play from start\n") 
    puts(1, "  +/-    - Volume up/down\n") 
    puts(1, "  ←/→    - Seek backward/forward\n") 
    puts(1, "  Q/ESC  - Quit\n") 
    puts(1, "=================================\n\n") 
    puts(1, "Playing... Press Q or ESC to quit.\n\n") 
     
    -- Main playback loop 
    integer quit = 0 
    integer last_pos_display = -1 
     
    while not quit do 
        -- Check if stream is still active 
        integer active = BASS_ChannelIsActive(stream) 
         
        -- Get current position 
        position = BASS_ChannelGetPosition(stream, BASS_POS_BYTE) 
        atom pos_seconds = BASS_ChannelBytes2Seconds(stream, position) 
         
        -- Update status display (only when position changes significantly) 
        integer current_second = floor(pos_seconds) 
        if current_second != last_pos_display then 
            last_pos_display = current_second 
             
            -- Get current volume 
            volume = BASS_GetVolume() 
             
            -- Clear line and display status 
            printf(1, "\rStatus: ") 
             
            if active = BASS_ACTIVE_PLAYING then 
                printf(1, "Playing  ") 
            elsif active = BASS_ACTIVE_PAUSED then 
                printf(1, "Paused   ") 
            elsif active = BASS_ACTIVE_STOPPED then 
                printf(1, "Stopped  ") 
            else 
                printf(1, "Unknown  ") 
            end if 
             
            printf(1, "| Time: %02d:%02d / %02d:%02d | Volume: %3d%% ", 
                   {floor(pos_seconds/60), remainder(floor(pos_seconds), 60), 
                    floor(length_seconds/60), remainder(floor(length_seconds), 60), 
                    floor(volume * 100)}) 
        end if 
         
        -- Check for key press (non-blocking) 
        if get_key() != -1 then 
            key = wait_key() 
             
            -- Convert to lowercase for letter keys 
            if key >= 'A' and key <= 'Z' then 
                key = key + 32 
            end if 
             
            if key = KEY_ESC or key = KEY_Q then 
                quit = 1 
                 
            elsif key = KEY_SPACE then 
                -- Toggle pause/resume 
                if active = BASS_ACTIVE_PLAYING then 
                    {} = BASS_ChannelPause(stream) 
                    puts(1, "\n>> Paused\n") 
                else 
                    {} = BASS_ChannelPlay(stream, 0) 
                    puts(1, "\n>> Resumed\n") 
                end if 
                 
            elsif key = KEY_S then 
                -- Stop 
                {} = BASS_ChannelStop(stream) 
                {} = BASS_ChannelSetPosition(stream, 0, BASS_POS_BYTE) 
                puts(1, "\n>> Stopped\n") 
                 
            elsif key = KEY_P then 
                -- Play from start 
                {} = BASS_ChannelPlay(stream, 1) 
                puts(1, "\n>> Playing from start\n") 
                 
            elsif key = KEY_PLUS or key = '=' then 
                -- Volume up 
                volume = BASS_GetVolume() 
                if volume < 1.0 then 
                    volume = volume + 0.1 
                    if volume > 1.0 then volume = 1.0 end if 
                    {} = BASS_SetVolume(volume) 
                    printf(1, "\n>> Volume: %d%%\n", {floor(volume * 100)}) 
                end if 
                 
            elsif key = KEY_MINUS then 
                -- Volume down 
                volume = BASS_GetVolume() 
                if volume > 0.0 then 
                    volume = volume - 0.1 
                    if volume < 0.0 then volume = 0.0 end if 
                    {} = BASS_SetVolume(volume) 
                    printf(1, "\n>> Volume: %d%%\n", {floor(volume * 100)}) 
                end if 
                 
            elsif key = KEY_LEFT then 
                -- Seek backward 5 seconds 
                position = BASS_ChannelGetPosition(stream, BASS_POS_BYTE) 
                atom new_pos = position - BASS_ChannelSeconds2Bytes(stream, 5) 
                if new_pos < 0 then new_pos = 0 end if 
                {} = BASS_ChannelSetPosition(stream, new_pos, BASS_POS_BYTE) 
                puts(1, "\n>> Seeking backward 5 seconds\n") 
                 
            elsif key = KEY_RIGHT then 
                -- Seek forward 5 seconds 
                position = BASS_ChannelGetPosition(stream, BASS_POS_BYTE) 
                atom new_pos = position + BASS_ChannelSeconds2Bytes(stream, 5) 
                if new_pos > stream_length then new_pos = stream_length end if 
                {} = BASS_ChannelSetPosition(stream, new_pos, BASS_POS_BYTE) 
                puts(1, "\n>> Seeking forward 5 seconds\n") 
            end if 
        end if 
         
        -- Check if playback ended 
        if active = BASS_ACTIVE_STOPPED and position >= stream_length - 1000 then 
            puts(1, "\n\n>> Playback completed.\n") 
            quit = 1 
        end if 
         
        -- Small delay to reduce CPU usage 
        sleep(0.05) 
    end while 
     
    -- Cleanup 
    puts(1, "\n\nCleaning up...\n") 
    {} = BASS_StreamFree(stream) 
    {} = BASS_Free() 
    puts(1, "Done. Goodbye!\n") 
     
end procedure 
 
-- Run the main program 
main() 
new topic     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu