BASS 2.4 Phix-compatible wrapper for 32-bit
- Posted by ron77 2 weeks ago
- 315 views
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()