vorbis.bac

'
' This is a simple command line OGG player. It needs the Public Domain 'stb_vorbis.c' file from:
'
' https://github.com/nothings/stb
'
' Note: for sake of simplicity not all error checking has been implemented. Also, it only handles stereo files.
'
' Created by Peter van Eerten, December 2015 - GPL v3.
'
'------------------------------------------------------------------------------------------------------------

INCLUDE openal.bac                                         : ' Get this from the www.basic-converter.org website

OPTION COLLAPSE TRUE                                       : ' For using SPLIT
OPTION MEMTYPE short                                       : ' For the memory pointer

SPLIT ARGUMENT$ BY " " TO arg$ SIZE amount                 : ' Get the filename
IF amount < 2 THEN
    PRINT "Play an Ogg Vorbis file! Usage: vorbis <file>"
    END
ENDIF

IF NOT(FILEEXISTS(arg$[1])) THEN                           : ' Check if the file exists
PRINT "File ", arg$[1], " not found!"
END
END IF

CONST chunk = 65536                                        : ' This is the chunk size we will be parsing

'------------------------------------------------------------------------------------------------------------

PRAGMA INCLUDE stb-master/stb_vorbis.c                     : ' Include the stb_vorbis header file

PROTO stb_vorbis_get_samples_short_interleaved stb_vorbis_close

DECLARE stream TYPE stb_vorbis*
DECLARE info TYPE stb_vorbis_info
DECLARE data TYPE short*

stream = stb_vorbis_open_filename(arg$[1], NULL, NULL)     : ' Open the file as a stream for decoding
info = stb_vorbis_get_info(stream)

samples = stb_vorbis_stream_length_in_samples(stream) * info.channels

PRINT "Channels: ", info.channels
PRINT "Sample rate: ", info.sample_rate
PRINT "Samples: ", samples
PRINT "Duration: ", stb_vorbis_stream_length_in_seconds(stream), " seconds."

data = MEMORY(chunk)                                       : ' STB working area

'------------------------------------------------------------------------------------------------------------

DECLARE source, processed, state, which TYPE int
DECLARE device, context TYPE void*
DECLARE buffer[2] TYPE int

' Initialization of hardware device
device = alcOpenDevice(NULL)
IF device THEN
    context = alcCreateContext(device, NULL)
    alcMakeContextCurrent(context)
ELSE
    PRINT "Cannot open device. Exiting."
    END 1
ENDIF

PRINT alGetString(AL_VENDOR) FORMAT "Vendor string: '%s'.\n"
PRINT alGetString(AL_VERSION) FORMAT "Version string: '%s'.\n"
PRINT alGetString(AL_RENDERER) FORMAT "Renderer: '%s'.\n"
PRINT alcGetString(device, ALC_DEVICE_SPECIFIER) FORMAT "Using '%s' device.\n"

' Clear Error Code (so we can catch any new errors)
alGetError()

' Create two buffers
alGenBuffers(2, buffer)

' Fill the 2 buffers, we lazily assume there is more than chunk size of data - buffer #1
stb_vorbis_get_samples_short_interleaved(stream, info.channels, data, chunk)
alBufferData(buffer[0], AL_FORMAT_STEREO16, data, chunk*sizeof(short), info.sample_rate)

' Buffer #2
stb_vorbis_get_samples_short_interleaved(stream, info.channels, data, chunk)
alBufferData(buffer[1], AL_FORMAT_STEREO16, data, chunk*sizeof(short), info.sample_rate)

'Create a sound source
alGenSources(1, &source)

' Attach buffers to the sound source, queue them
alSourceQueueBuffers(source, 2, buffer)

' Start rendering the music asynchronously
alSourcePlay(source)

' Now dynamically feed the music player
REPEAT

    ' Check if one of our buffers is processed and 'empty'
    alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed)

    IF processed THEN

        ' Unqueue the empty buffer, this buffer is returned in variable 'which'
        alSourceUnqueueBuffers(source, 1, &which)

        ' Get the next chunk of music
        samples = stb_vorbis_get_samples_short_interleaved(stream, info.channels, data, chunk)

        ' Refill the empty buffer
        alBufferData(which, AL_FORMAT_STEREO16, data, samples*2*sizeof(short), info.sample_rate)
        
        ' Put the refilled buffer back in the queue
        alSourceQueueBuffers(source, 1, &which)

    ENDIF

    ' Prevent CPU load of 100%
    SLEEP 15

    ' Are we still playing or not?
    alGetSourcei(source, AL_SOURCE_STATE, &state)

UNTIL state <> AL_PLAYING

' Cleanup STB
stb_vorbis_close(stream)
FREE data

' Cleanup OpenAL
alDeleteSources(1, &source)
alDeleteBuffers(2, buffer)
alcMakeContextCurrent(NULL)
alcDestroyContext(context)
alcCloseDevice(device)

Generated by GNU Enscript 1.6.5.90.