diff --git a/Makefile b/Makefile index f47b3b665..16f14c4d6 100644 --- a/Makefile +++ b/Makefile @@ -287,7 +287,7 @@ ifneq ($(filter y,$(DEP_AUDIO_MINIAUDIO)),) CXXFLAGS += -DDEPENDENCY_AUDIO_MINIAUDIO QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME)) - LICENSE_IN_USE += miniaudio libxmp-lite radv2 stbvorbis + LICENSE_IN_USE += miniaudio libxmp-lite radv2 stbvorbis hivelytracker ifdef DEP_AUDIO_DECODE_MIDI LICENSE_IN_USE += tinysoundfont tinymidiloader diff --git a/internal/c/libqb/include/audio.h b/internal/c/libqb/include/audio.h index 8e53bd3f7..2d81576e7 100644 --- a/internal/c/libqb/include/audio.h +++ b/internal/c/libqb/include/audio.h @@ -86,6 +86,7 @@ void sub__sndrawdone(int32_t handle, int32_t passed); double func__sndrawlen(int32_t handle, int32_t passed); mem_block func__memsound(int32_t handle, int32_t targetChannel); +int32_t func__sndnew(int32_t frames, int32_t channels, int32_t bits); void snd_init(); void snd_un_init(); diff --git a/internal/c/parts/audio/audio.cpp b/internal/c/parts/audio/audio.cpp index 84670bcf3..793ef2f58 100644 --- a/internal/c/parts/audio/audio.cpp +++ b/internal/c/parts/audio/audio.cpp @@ -12,52 +12,34 @@ // //----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// HEADER FILES -//----------------------------------------------------------------------------------------------------- +#include +#include +#define STB_VORBIS_HEADER_ONLY +#include "extras/stb_vorbis.c" +#include "miniaudio.h" // Set this to 1 if we want to print debug messages to stderr #define AUDIO_DEBUG 0 #include "audio.h" -#include -#include -// Enable Ogg Vorbis decoding -#define STB_VORBIS_HEADER_ONLY -#include "extras/stb_vorbis.c" -// The main miniaudio header -#include "miniaudio.h" -// Although Matt says we should not be doing this, this has worked out to be ok so far -// We need 'qbs' and also the 'mem' stuff from here -// I am not using 'list' anymore and have migrated the code to use C++ vectors instead -// We'll likely keep the 'include' this way because I do not want to duplicate stuff and cause issues -// For now, we'll wait for Matt until he sorts out things to smaller and logical files +#include "mutex.h" +// We need 'qbs' and 'mem' stuff from here. This should eventually change when things are moved to smaller, logical and self-contained files #include "../../libqb.h" -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// CONSTANTS -//----------------------------------------------------------------------------------------------------- // This should be defined elsewhere (in libqb?). Since it is not, we are doing it here #define INVALID_MEM_LOCK 1073741821 - // This should be defined elsewhere (in libqb?). Since it is not, we are doing it here #define MEM_TYPE_SOUND 5 - // In QuickBASIC false means 0 and true means -1 (sad, but true XD) #define QB_FALSE MA_FALSE #define QB_TRUE -MA_TRUE - // This is returned to the caller if handle allocation fails with a -1 // AllocateSoundHandle() does not return 0 because it is a valid internal handle // Handle 0 is 'handled' as a special case #define INVALID_SOUND_HANDLE 0 - -// This is the string that must be passed in the requirements parameter to stream a sound from storage +// This is the string that can be passed in the requirements parameter to stream a sound from storage #define REQUIREMENT_STRING_STREAM "STREAM" -//----------------------------------------------------------------------------------------------------- +// This is the string that can be passed in the requirements parameter to load a sound from memory +#define REQUIREMENT_STRING_MEMORY "MEMORY" -//----------------------------------------------------------------------------------------------------- -// MACROS -//----------------------------------------------------------------------------------------------------- #define SAMPLE_FRAME_SIZE(_type_, _channels_) (sizeof(_type_) * (_channels_)) // This basically checks if the handle is within vector limits and 'isUsed' is set to true @@ -72,13 +54,10 @@ #else # define ZERO_VARIABLE(_v_) memset(&(_v_), 0, sizeof(_v_)) #endif -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// FORWARD DECLARATIONS -//----------------------------------------------------------------------------------------------------- -// This adds our customer backend (format decoders) VTables to our ma_resource_manager_config +// These attaches our customer backend (format decoders) VTables to various miniaudio structs void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig); +void AudioEngineAttachCustomBackendVTables(ma_decoder_config *maDecoderConfig); // These are stuff that was not declared anywhere else // We will wait for Matt to cleanup the C/C++ source file and include header files that declare this stuff @@ -93,319 +72,369 @@ extern ptrszint dblock; // Required for Play(). Did not find this declar extern uint64 mem_lock_id; // Another one that we need for the mem stuff extern mem_lock *mem_lock_base; // Same as above extern mem_lock *mem_lock_tmp; // Same as above -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// STRUCTURES, CLASSES & ENUMERATIONS -//----------------------------------------------------------------------------------------------------- -/// -/// Type of sound -/// +/// @brief Type of sound enum struct SoundType { None, // No sound or internal sound whose buffer is managed by the QBPE audio engine Static, // Static sounds that are completely managed by miniaudio Raw // Raw sound stream that is managed by the QBPE audio engine }; -/// -/// This struct encapsulates a sample frame block and it's management. -/// We choose these 'classes' to be barebones and completely transparent for performance reasons. -/// -struct SampleFrameBlockNode { - SampleFrameBlockNode *next; // Next block node in the chain - ma_uint32 samples; // Size of the block in 'samples'. See below - ma_uint32 offset; // Where is the write cursor in the buffer (in samples!)? - float *buffer; // The actual sample frame block buffer - bool force; // When this is set, the buffer will be processed even when if it is not full - - SampleFrameBlockNode() = delete; // No default constructor - SampleFrameBlockNode(const SampleFrameBlockNode &) = delete; // No default copy constructor - SampleFrameBlockNode &operator=(SampleFrameBlockNode &) = delete; // No assignment operator - - /// - /// The constructor parameter is in sample frames. - /// For a stereo sample frame we'll need (sample frames * 2) samples. - /// Each sample is sizeof(float) bytes. - /// - /// Number of sample frames needed - SampleFrameBlockNode(ma_uint32 sampleFrames) { - next = nullptr; // Set this to null. This will managed by the 'Queue' struct - samples = sampleFrames << 1; // 2 channels (stereo) - offset = 0; // Set the write cursor to 0 - force = false; // Set the force flag to false by default - buffer = new float[samples](); // Allocate a zeroed float buffer of size floats. Ah, Creative Silence! - } - - /// - /// Free the sample frame block that was allocated. - /// - ~SampleFrameBlockNode() { delete[] buffer; } - - /// - /// Pushes a sample frame in the block and increments the offset. - /// miniaudio expects it's stereo PCM data interleaved (LRLR format). - /// No clipping is required because miniaudio does that for us (sweet!) - /// - /// Left floating point sample - /// Right floating point sample - /// Return true if operation was succcessful. False if block is full - bool PushSampleFrame(float l, float r) { - if (buffer && offset < samples) { - buffer[offset] = l; - ++offset; - buffer[offset] = r; - ++offset; - - return true; - } - - return false; - } - - /// - /// Check if the buffer is completely filled. - /// - /// Returns true if buffer is full - bool IsBufferFull() { return offset >= samples || force; } +/// @brief A simple FP32 stereo sample frame +struct SampleFrame { + float l; + float r; }; -/// -/// This is a light weight queue of type SampleFrameBlockNode and also the guts of SndRaw. -/// We could have used some std container but I wanted something really lean, simple and transparent. -/// -struct SampleFrameBlockQueue { - SampleFrameBlockNode *first; // First sample frame block - SampleFrameBlockNode *last; // Last sample frame block - size_t blockCount; // Sample frame block count - size_t frameCount; // Number of sample frames we have in the queue - ma_uint32 sampleRate; // The sample rate reported by ma_engine - ma_uint32 blockSampleFrames; // How many sample frames do we need per 'block'. See below - float *buffer; // This is the ping-pong buffer where the samples block will be 'streamed' to - ma_uint32 bufferSampleFrames; // Size of the ping-pong buffer in *samples frames* - bool updateFlag; // We will only update the buffer with fresh samples when this flag is not equal to the check condition - ma_uint32 bufferUpdatePosition; // The position (in samples) in the buffer where we should be copying a sample block - ma_uint32 sampleFramesPlaying; // The number of sample frames that was sent for playback - ma_uint64 maEngineTime; // miniaudio engine time use for correct length calculation - ma_sound *maSound; // Pointer to a ma_sound object that was passed in the constructor - ma_engine *maEngine; // Pointer to a ma_engine object - ma_audio_buffer maBuffer; // miniaudio buffer object - ma_audio_buffer_config maBufferConfig; // miniaudio buffer configuration - ma_result maResult; // This is the result of the last miniaudio operation (used for trapping errors) +/// @brief A miniaudiio raw audio stream datasource +struct RawStream { + ma_data_source_base maDataSource; // miniaudio data source (this must be the first member of our struct) + ma_data_source_config maDataSourceConfig; // config struct for the data source + ma_engine *maEngine; // pointer to a ma_engine object that was passed while creating the data source + ma_sound *maSound; // pointer to a ma_sound object that was passed while creating the data source + ma_uint32 sampleRate; // the sample rate reported by ma_engine + struct Buffer { // we'll give this a name that we'll use below + std::vector data; // this holds the actual sample frames + size_t cursor; // the read cursor (in frames) in the stream + } buffer[2]; // we need two of these to do a proper ping-pong + Buffer *consumer; // this is what the miniaudio thread will use to pull data from + Buffer *producer; // this is what the main thread will use to push data to + libqb_mutex *m; // we'll use a mutex to give exclusive access to resources used by both threads + bool stop; // set this to true to stop supply of samples completely (including silent samples) - SampleFrameBlockQueue() = delete; // No default constructor - SampleFrameBlockQueue(const SampleFrameBlockQueue &) = delete; // No default copy constructor - SampleFrameBlockQueue &operator=(SampleFrameBlockQueue &) = delete; // No assignment operator + static const size_t DEFAULT_SIZE = 1024; // this is almost twice the amout what miniaudio actually asks for in frameCount - /// - /// This initializes the queue and calculates the sample frames per block - /// - /// A pointer to a miniaudio engine object - /// A pointer to a miniaudio sound object - SampleFrameBlockQueue(ma_engine *pmaEngine, ma_sound *pmaSound) { - first = last = nullptr; - blockCount = frameCount = maEngineTime = sampleFramesPlaying = bufferUpdatePosition = 0; + // Delete default, copy and move constructors and assignments + RawStream() = delete; + RawStream(const RawStream &) = delete; + RawStream &operator=(const RawStream &) = delete; + RawStream &operator=(RawStream &&) = delete; + RawStream(RawStream &&) = delete; + + /// @brief This is use to setup the vectors, mutex and set some defaults + RawStream(ma_engine *pmaEngine, ma_sound *pmaSound) { maSound = pmaSound; // Save the pointer to the ma_sound object (this is basically from a QBPE sound handle) maEngine = pmaEngine; // Save the pointer to the ma_engine object (this should come from the QBPE sound engine) sampleRate = ma_engine_get_sample_rate(maEngine); // Save the sample rate - - // We can get away with '>> 3' because the sound loop function is called @ ~60Hz - // This should work even on entry level systems. Tested on AMD A6-9200 (230.4 GFLOPS), Crostini Linux - // Also note that the nodes will allocates twice this to account for 2 channels - blockSampleFrames = sampleRate >> 3; - - bufferSampleFrames = blockSampleFrames * 2; // We want the playback buffer twice the size of a block to do a proper ping-pong - buffer = new float[bufferSampleFrames * 2](); // Allocate a zeroed float buffer of bufferSizeSampleFrames * 2 floats (2 is for 2 channels - stereo) - updateFlag = false; // Set this to false because we want the initial check to fail - - if (buffer) { - // Setup the ma buffer - maBufferConfig = ma_audio_buffer_config_init(ma_format::ma_format_f32, 2, bufferSampleFrames, buffer, NULL); - maResult = ma_audio_buffer_init(&maBufferConfig, &maBuffer); - AUDIO_DEBUG_CHECK(maResult == MA_SUCCESS); - - // Create a ma_sound from the ma_buffer - maResult = ma_sound_init_from_data_source(maEngine, &maBuffer, 0, NULL, maSound); - AUDIO_DEBUG_CHECK(maResult == MA_SUCCESS); - - // Play the ma_sound - maResult = ma_sound_start(maSound); - AUDIO_DEBUG_CHECK(maResult == MA_SUCCESS); - - // Set the buffer to loop forever - ma_sound_set_looping(maSound, MA_TRUE); - } - - AUDIO_DEBUG_PRINT("Raw sound stream created with %u sample frame block size", blockSampleFrames); + buffer[0].cursor = buffer[1].cursor = 0; // reset the cursors + buffer[0].data.reserve(DEFAULT_SIZE); // ensure we have a contigious block to account for expansion without reallocation + buffer[1].data.reserve(DEFAULT_SIZE); // ensure we have a contigious block to account for expansion without reallocation + consumer = &buffer[0]; // set default consumer + producer = &buffer[1]; // set default producer + stop = false; // by default we will send silent samples to keep the playback going + m = libqb_mutex_new(); } - /// - /// This simply pops all sample blocks - /// - ~SampleFrameBlockQueue() { - if (buffer) { - // Stop playback - maResult = ma_sound_stop(maSound); - AUDIO_DEBUG_CHECK(maResult == MA_SUCCESS); + /// @brief We use this to destroy the mutex + ~RawStream() { libqb_mutex_free(m); } - // Delete the ma_sound object - ma_sound_uninit(maSound); - - // Delete the ma_buffer object - ma_audio_buffer_uninit(&maBuffer); - } - - while (PopSampleFrameBlock()) - ; - - delete[] buffer; - - AUDIO_DEBUG_PRINT("Raw sound stream closed"); + /// @brief This swaps the consumer and producer Buffers. This is mutex protected and called by the miniaudio thread + void SwapBuffers() { + libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors + consumer->cursor = 0; // reset the cursor + consumer->data.clear(); // clear the consumer vector + std::swap(consumer, producer); // quicky swap the Buffer pointers } - /// - /// This pushes a sample frame into the queue. - /// If there are no sample frame blocks then it creates one. - /// If the last sample frame block is full it creates a new one and links it to the chain. - /// Note that in QBPE all samples frames are assumed to be stereo. - /// Mono sample frames are simply simulated by playing the same data from left and right. - /// No clipping is required because miniaudio does that for us (sweet!) - /// - /// The left sample - /// The right sample - /// Returns true if operation was successful - bool PushSampleFrame(float l, float r) { - // Attempt to push the frame into the last node if one exists - // If successfull return true - if (last && last->PushSampleFrame(l, r)) { - ++frameCount; // Increment the frame count - return true; - } - - // If we reached here, then it means that either there are no nodes or the last one is full - // Simply create a new node and then link it to the chain - SampleFrameBlockNode *node = new SampleFrameBlockNode(blockSampleFrames); - - // Return false if memory allocation failed or we're mot able to save the sample frame - if (!node || !node->PushSampleFrame(l, r)) { - delete node; - return false; // Ignore the sample frame and exit silently - } - - if (last) - last->next = node; // Add the node to the last node if we have nodes in the queue - else - first = node; // Else this is the first node - - last = node; // The last item in the queue is node - ++blockCount; // Increase the frame block count - ++frameCount; // Increment the frame count - - return true; - } - - /// - /// This pops a sample frame block from the front of the queue. - /// The sample frame block can be accessed before popping using the 'first' member. - /// Popping a block frees and invalidates the memory it was using. So, pop a block only when we are sure that we do not need it. - /// - /// Returns true if we were able to pop. False means the queue is empty - bool PopSampleFrameBlock() { - // Only if the queue has some sample frame blocks then... - if (blockCount) { - SampleFrameBlockNode *node = first; // Set node to the first frame in the queue - - --blockCount; // Decrement the block count now so that we know what to do with 'last' - frameCount -= node->offset >> 1; // Decrease frame count by number of sample frames written in the block (/ 2 for channels) - first = node->next; // Detach the node. If this is the last node then 'first' will be NULL cause node->next is NULL - - if (!blockCount) - last = nullptr; // This means that node was the last node - - delete node; // Free the node - + /// @brief This pushes a sample frame at the end of the queue. This is mutex protected and called by the main thread + /// @param l Sample frame left channel data + /// @param r Sample frame right channel data + void PushSampleFrame(float l, float r) { + libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors + producer->data.push_back({l, r}); // push the sample frame to the back of the producer queue + } + + /// @brief This pushes a whole buffer of mono sample frames to the queue. This is mutex protected and called by the main thread + /// @param buffer The buffer containing the sample frames. This cannot be NULL + /// @param frames The total number of frames in the buffer + void PushMonoSampleFrames(float *buffer, ma_uint64 frames) { + libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors + for (ma_uint64 i = 0; i < frames; i++) { + producer->data.push_back({buffer[i], buffer[i]}); + } + } + + /// @brief Returns the length, in sample frames of sound queued + /// @return The length left to play in sample frames + ma_uint64 GetSampleFramesRemaining() { + libqb_mutex_guard lock(m); // lock the mutex before accessing the vectors + return (consumer->data.size() - consumer->cursor) + (producer->data.size() - producer->cursor); // sum of producer and consumer sample frames + } + + /// @brief Returns the length, in seconds of sound queued + /// @return The length left to play in seconds + double GetTimeRemaining() { return (double)GetSampleFramesRemaining() / (double)sampleRate; } +}; + +/// @brief This is what is used by miniaudio to pull a chunk of raw sample frames to play. The samples being read is removed from the queue +/// @param pDataSource Pointer to the raw stream data source (cast to RawStream type) +/// @param pFramesOut The sample frames sent to miniaudio +/// @param frameCount The sample frame count requested by miniaudio +/// @param pFramesRead The sample frame count that was actually sent (this must not exceed frameCount) +/// @return MA_SUCCESS on success or an appropriate MA_FAILED_* value on failure +static ma_result RawStreamOnRead(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) { + if (pFramesRead) + *pFramesRead = 0; + + if (frameCount == 0) + return MA_INVALID_ARGS; + + if (!pDataSource) + return MA_INVALID_ARGS; + + auto pRawStream = (RawStream *)pDataSource; // cast to RawStream instance pointer + auto result = MA_SUCCESS; // must be initialized to MA_SUCCESS + auto maBuffer = (SampleFrame *)pFramesOut; // cast to sample frame pointer + + ma_uint64 sampleFramesCount = pRawStream->consumer->data.size() - pRawStream->consumer->cursor; // total amount of samples we need to send to miniaudio + // Swap buffers if we do not have anything left to play + if (!sampleFramesCount) { + pRawStream->SwapBuffers(); + sampleFramesCount = pRawStream->consumer->data.size() - pRawStream->consumer->cursor; // get the total number of samples again + } + sampleFramesCount = std::min(sampleFramesCount, frameCount); // we'll always send lower of what miniaudio wants or what we have + + ma_uint64 sampleFramesRead = 0; // sample frame counter + // Now send the samples to miniaudio + while (sampleFramesRead < sampleFramesCount) { + maBuffer[sampleFramesRead] = pRawStream->consumer->data[pRawStream->consumer->cursor]; + ++sampleFramesRead; // increment the frame counter + pRawStream->consumer->cursor++; // increment the read cursor + } + + // To keep the stream going, play silence if there are no frames to play + if (!sampleFramesRead && !pRawStream->stop) { + while (sampleFramesRead < frameCount) { + maBuffer[sampleFramesRead] = {}; + ++sampleFramesRead; + } + } + + if (pFramesRead) + *pFramesRead = sampleFramesRead; + + return result; +} + +/// @brief This is a dummy callback function which just tells miniaudio that it succeeded +/// @param pDataSource Pointer to the raw stream data source (cast to RawStream type) +/// @param frameIndex The frame index to seek to (unused) +/// @return Always MA_SUCCESS +static ma_result RawStreamOnSeek(ma_data_source *pDataSource, ma_uint64 frameIndex) { + // NOP. Just pretend to be successful. + (void)pDataSource; + (void)frameIndex; + + return MA_SUCCESS; +} + +/// @brief Returns the audio format to miniaudio +/// @param pDataSource Pointer to the raw stream data source (cast to RawStream type) +/// @param pFormat The ma_format to use (we always return ma_format_f32 because that is what QB64 uses) +/// @param pChannels The number of audio channels (always 2 - stereo) +/// @param pSampleRate The sample rate of the stream (we always return the engine sample rate) +/// @param pChannelMap Sent to ma_channel_map_init_standard +/// @param channelMapCap Sent to ma_channel_map_init_standard +/// @return Always MA_SUCCESS +static ma_result RawStreamOnGetDataFormat(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, + ma_channel *pChannelMap, size_t channelMapCap) { + auto pRawStream = (RawStream *)pDataSource; + + if (pFormat) + *pFormat = ma_format::ma_format_f32; // QB64 SndRaw API uses FP32 samples + + if (pChannels) + *pChannels = 2; // stereo + + if (pSampleRate) + *pSampleRate = pRawStream->sampleRate; // we'll play at the audio engine sampling rate + + if (pChannelMap) + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, 2); // stereo + + return MA_SUCCESS; +} + +/// @brief Raw stream data source vtable +static ma_data_source_vtable rawStreamDataSourceVtable = { + RawStreamOnRead, // Returns a bunch of samples from a raw sample stream queue. The samples being returned is removed from the queue + RawStreamOnSeek, // NOP for raw sample stream + RawStreamOnGetDataFormat, // Returns the audio format to miniaudio + NULL, // No notion of a cursor for raw sample stream + NULL, // No notion of a length for raw sample stream + NULL, // Cannot loop raw sample stream + 0 // flags +}; + +/// @brief This creates, initializes and sets up a raw stream for playback +/// @param pmaEngine This should come from the QBPE sound engine +/// @param pmaSound This should come from a QBPE sound handle +/// @return Returns a pointer to a data souce if successful, NULL otherwise +static RawStream *RawStreamCreate(ma_engine *pmaEngine, ma_sound *pmaSound) { + if (!pmaEngine || !pmaSound) { // these should not be NULL + AUDIO_DEBUG_PRINT("Invalid arguments"); + + return nullptr; + } + + auto pRawStream = new RawStream(pmaEngine, pmaSound); // create the data source object + if (!pRawStream) { + AUDIO_DEBUG_PRINT("Failed to create data source"); + + return nullptr; + } + + ZERO_VARIABLE(pRawStream->maDataSource); + + pRawStream->maDataSourceConfig = ma_data_source_config_init(); + pRawStream->maDataSourceConfig.vtable = &rawStreamDataSourceVtable; // attach the vtable to the data source + + auto result = ma_data_source_init(&pRawStream->maDataSourceConfig, &pRawStream->maDataSource); + if (result != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("Error %i: failed to initialize data source", result); + + delete pRawStream; + + return nullptr; + } + + result = ma_sound_init_from_data_source(pmaEngine, &pRawStream->maDataSource, MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, + pmaSound); // attach data source to the ma_sound + if (result != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("Error %i: failed to initalize sound from data source", result); + + delete pRawStream; + + return nullptr; + } + + result = ma_sound_start(pmaSound); // play the ma_sound + if (result != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("Error %i: failed to start sound playback", result); + + ma_sound_uninit(pmaSound); // delete the ma_sound object + + delete pRawStream; + + return nullptr; + } + + AUDIO_DEBUG_PRINT("Raw sound stream created"); + + return pRawStream; +} + +/// @brief Stops and then frees a raw stream data source previously created with RawStreamCreate() +/// @param pRawStream Pointer to the data source object +static void RawStreamDestroy(RawStream *pRawStream) { + if (pRawStream) { + auto result = ma_sound_stop(pRawStream->maSound); // stop playback + AUDIO_DEBUG_CHECK(result == MA_SUCCESS); + + ma_sound_uninit(pRawStream->maSound); // delete the ma_sound object + + delete pRawStream; // delete the raw stream object + + AUDIO_DEBUG_PRINT("Raw sound stream destroyed"); + } +} + +/// @brief A class that can manage a list of buffers using unique keys +class BufferMap { + private: + /// @brief A buffer that is made up of a raw pointer, size and reference count + struct Buffer { + void *data; + size_t size; + size_t refCount; + }; + + std::unordered_map buffers; + + public: + // Delete assignment operators + BufferMap &operator=(const BufferMap &) = delete; + BufferMap &operator=(BufferMap &&) = delete; + + /// @brief This will simply free all buffers that were allocated + ~BufferMap() { + for (auto &it : buffers) { + free(it.second.data); + AUDIO_DEBUG_PRINT("Buffer freed of size %llu", it.second.size); + } + } + + /// @brief Adds a buffer to the map using a unique key only if it was not added before + /// @param data The raw data pointer. The data is copied + /// @param size The size of the data + /// @param key The unique key that should be used + /// @return True if successful + bool AddBuffer(const void *data, size_t size, intptr_t key) { + if (data && size && key && buffers.find(key) == buffers.end()) { + Buffer buf = {}; + + buf.data = malloc(size); + if (!buf.data) + return false; + + buf.size = size; + buf.refCount = 1; + memcpy(buf.data, data, size); + buffers.emplace(key, std::move(buf)); + + AUDIO_DEBUG_PRINT("Added buffer of size %llu to map", size); return true; } + AUDIO_DEBUG_PRINT("Failed to add buffer of size %llu", size); return false; } - /// - /// Returns the length, in sample frames of sound queued. - /// - /// The length left to play in sample frames - ma_uint64 GetSampleFramesRemaining() { - // Calculate the time difference (ma_engine time is really just of a sum of sample frames sent to the device) - ma_uint64 maEngineDeltaTime = ma_engine_get_time(maEngine) - maEngineTime; - - // Decrement the delta from the sample frames that are playing - // Using std::min here is probably risky since these are all unsigned types - sampleFramesPlaying = maEngineDeltaTime > sampleFramesPlaying ? 0 : (ma_uint32)(sampleFramesPlaying - maEngineDeltaTime); - - // Add this to the frames in the queue - return sampleFramesPlaying + frameCount; - } - - /// - /// Returns the length, in seconds of sound queued. - /// - /// The length left to play in seconds - double GetTimeRemaining() { - ma_uint64 sampleFramesRemaining = GetSampleFramesRemaining(); - - // This will help us avoid situations where we can get a non-zero value even if GetSampleFramesRemaining returns 0 - if (!sampleFramesRemaining) - return 0; - else - return (double)sampleFramesRemaining / sampleRate; - } - - /// - /// Check if everything is ready to go - /// - /// Returns true if everything is a go - bool IsSetupValid() { return buffer && maEngine && maSound && maResult == MA_SUCCESS; } - - /// - /// This keeps the ping-pong (ring? whatever...) buffer fed and the sound stream going - /// - void Update() { - // Figure out which pcm frame of the buffer is miniaudio playing - ma_uint64 readCursor; - maResult = ma_sound_get_cursor_in_pcm_frames(maSound, &readCursor); - AUDIO_DEBUG_CHECK(maResult == MA_SUCCESS); - - bool checkCondition = readCursor < blockSampleFrames; // Since buffer sample frame size = blockSampleFrames * 2 - - // Only proceed to update if our flag is not the same as our condition - if (checkCondition != updateFlag) { - // The line below does two sneaky things that deserve explanation - // 1. We are using bufferSampleFrames which is set to exactly halfway through the buffer since we are using stereo (see constructor) - // 2. The boolean condition above will always be 1 if the read cursor is in the lower-half and hence push the position to the top-half - // 3. Obviously, this means that if the condition is 0 then position will be set to the lower-half - bufferUpdatePosition = checkCondition * bufferSampleFrames; // This line basically toggles the buffer copy position - - // Check if we have any blocks in the queue and stream only if the block is full - if (blockCount && first->IsBufferFull()) { - // We check this here so that even if the buffer is not allocated, the block object will be popped off - if (first->buffer) { - // Simply copy the first block in the queue - std::copy(first->buffer, first->buffer + first->samples, buffer + bufferUpdatePosition); - } - - // Save the number of samples frames sent for playback and the current time for correct time calculation - sampleFramesPlaying = blockSampleFrames; - maEngineTime = ma_engine_get_time(maEngine); - - // And then pop it off - PopSampleFrameBlock(); - } else { // Else we'll stream silence - // We are using bufferSampleFrames here for the same reason as the explanation above - std::fill(buffer + bufferUpdatePosition, buffer + bufferUpdatePosition + bufferSampleFrames, NULL); - } - - updateFlag = checkCondition; // Save our check condition to our flag + /// @brief Increments the buffer reference count + /// @param key The unique key for the buffer + void AddRef(intptr_t key) { + const auto it = buffers.find(key); + if (it != buffers.end()) { + auto &buf = it->second; + buf.refCount += 1; + AUDIO_DEBUG_PRINT("Increased reference count to %llu", buf.refCount); + } else { + AUDIO_DEBUG_PRINT("Buffer not found"); } } + + /// @brief Decrements the buffer reference count and frees the buffer if the reference count reaches zero + /// @param key The unique key for the buffer + void Release(intptr_t key) { + const auto it = buffers.find(key); + if (it != buffers.end()) { + auto &buf = it->second; + buf.refCount -= 1; + AUDIO_DEBUG_PRINT("Decreased reference count to %llu", buf.refCount); + + if (buf.refCount < 1) { + free(buf.data); + AUDIO_DEBUG_PRINT("Buffer freed of size %llu", buf.size); + buffers.erase(key); + } + } else { + AUDIO_DEBUG_PRINT("Buffer not found"); + } + } + + /// @brief Gets the raw pointer and size of the buffer with the given key + /// @param key The unique key for the buffer + /// @return An std::pair of the buffer raw pointer and size + std::pair GetBuffer(intptr_t key) const { + const auto it = buffers.find(key); + if (it == buffers.end()) { + AUDIO_DEBUG_PRINT("Buffer not found"); + return {nullptr, 0}; + } + const auto &buf = it->second; + AUDIO_DEBUG_PRINT("Returning buffer of size %llu", buf.size); + return {buf.data, buf.size}; + } }; /// @@ -413,17 +442,25 @@ struct SampleFrameBlockQueue { /// This describes every sound the system will ever play (including raw streams). /// struct SoundHandle { - bool isUsed; // Is this handle in active use? - SoundType type; // Type of sound (see SoundType enum class) - bool autoKill; // Do we need to auto-clean this sample / stream after playback is done? - ma_sound maSound; // miniaudio sound - ma_uint32 maFlags; // miniaudio flags that were used when initializing the sound - SampleFrameBlockQueue *rawQueue; // Raw sample frame queue - void *memLockOffset; // This is a pointer from new_mem_lock() - uint64 memLockId; // This is mem_lock_id created by new_mem_lock() + bool isUsed; // Is this handle in active use? + SoundType type; // Type of sound (see SoundType enum class) + bool autoKill; // Do we need to auto-clean this sample / stream after playback is done? + ma_sound maSound; // miniaudio sound + ma_uint32 maFlags; // miniaudio flags that were used when initializing the sound + ma_decoder_config maDecoderConfig; // miniaudio decoder configuration + ma_decoder *maDecoder; // this is used for files that are loaded directly from memory + intptr_t bufferKey; // a key that will uniquely identify the data the decoder will use + ma_audio_buffer_config maAudioBufferConfig; // miniaudio buffer configuration + ma_audio_buffer *maAudioBuffer; // this is used for user created audio buffers (memory is managed by miniaudio) + RawStream *rawStream; // Raw sample frame queue + void *memLockOffset; // This is a pointer from new_mem_lock() + uint64 memLockId; // This is mem_lock_id created by new_mem_lock() - SoundHandle(const SoundHandle &) = delete; // No default copy constructor - SoundHandle &operator=(SoundHandle &) = delete; // No assignment operator + // Delete copy and move constructors and assignments + SoundHandle(const SoundHandle &) = delete; + SoundHandle &operator=(const SoundHandle &) = delete; + SoundHandle(SoundHandle &&) = delete; + SoundHandle &operator=(SoundHandle &&) = delete; /// /// Just initializes some important members. @@ -434,11 +471,14 @@ struct SoundHandle { isUsed = false; type = SoundType::None; autoKill = false; - rawQueue = nullptr; ZERO_VARIABLE(maSound); maFlags = MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_WAIT_INIT; - memLockId = INVALID_MEM_LOCK; + maDecoder = nullptr; + bufferKey = 0; + maAudioBuffer = nullptr; + rawStream = nullptr; memLockOffset = nullptr; + memLockId = INVALID_MEM_LOCK; } }; @@ -450,7 +490,7 @@ struct AudioEngine { bool initializationFailed; // This is set to true if a past initialization attempt failed ma_resource_manager_config maResourceManagerConfig; // miniaudio resource manager configuration ma_resource_manager maResourceManager; // miniaudio resource manager - ma_engine_config maEngineConfig; // miniaudio engine configuration (will be used to pass in the resource manager) + ma_engine_config maEngineConfig; // miniaudio engine configuration (will be used to pass in the resource manager) ma_engine maEngine; // This is the primary miniaudio engine 'context'. Everything happens using this! ma_result maResult; // This is the result of the last miniaudio operation (used for trapping errors) ma_uint32 sampleRate; // Sample rate used by the miniaudio engine @@ -459,9 +499,13 @@ struct AudioEngine { std::vector soundHandles; // This is the audio handle list used by the engine and by everything else int32_t lowestFreeHandle; // This is the lowest handle then was recently freed. We'll start checking for free handles from here bool musicBackground; // Should 'Sound' and 'Play' work in the background or block the caller? + BufferMap bufferMap; // This is used to keep track of and manage memory used by 'in-memory' sound files - AudioEngine(const AudioEngine &) = delete; // No default copy constructor - AudioEngine &operator=(AudioEngine &) = delete; // No assignment operator + // Delete copy and move constructors and assignments + AudioEngine(const AudioEngine &) = delete; + AudioEngine &operator=(const AudioEngine &) = delete; + AudioEngine &operator=(AudioEngine &&) = delete; + AudioEngine(AudioEngine &&) = delete; /// /// Just initializes some important members. @@ -485,8 +529,6 @@ struct AudioEngine { /// The choice of using a vector was simple - performance. Vector performance when using 'indexes' is next to no other. /// The vector will be pruned only when snd_un_init gets called. /// We will however, be good citizens and will also 'delete' the objects when snd_un_init gets called. - /// All this means that a sloppy programmer may be able to grow the vector and eventually the system may run out of memory and crash. - /// But that's ok. Sloppy programmers (like me) must be punished until they learn! XD /// This also increments 'lowestFreeHandle' to allocated handle + 1. /// /// Returns a non-negative handle if successful @@ -545,11 +587,14 @@ struct AudioEngine { // This will set it to 'in use' after applying some defaults. soundHandles[h]->type = SoundType::None; soundHandles[h]->autoKill = false; - soundHandles[h]->rawQueue = nullptr; ZERO_VARIABLE(soundHandles[h]->maSound); // We do not use pitch shifting, so this will give a little performance boost // Spatialization is disabled by default but will be enabled on the fly if required soundHandles[h]->maFlags = MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_WAIT_INIT; + soundHandles[h]->maDecoder = nullptr; + soundHandles[h]->bufferKey = 0; + soundHandles[h]->maAudioBuffer = nullptr; + soundHandles[h]->rawStream = nullptr; soundHandles[h]->memLockId = INVALID_MEM_LOCK; soundHandles[h]->memLockOffset = nullptr; soundHandles[h]->isUsed = true; @@ -579,8 +624,8 @@ struct AudioEngine { break; case SoundType::Raw: - delete soundHandles[handle]->rawQueue; - soundHandles[handle]->rawQueue = nullptr; + RawStreamDestroy(soundHandles[handle]->rawStream); + soundHandles[handle]->rawStream = nullptr; break; @@ -594,11 +639,28 @@ struct AudioEngine { AUDIO_DEBUG_PRINT("Condition not handled"); // It should not come here } + // Free any initialized miniaudio decoder + if (soundHandles[handle]->maDecoder) { + ma_decoder_uninit(soundHandles[handle]->maDecoder); + delete soundHandles[handle]->maDecoder; + soundHandles[handle]->maDecoder = nullptr; + bufferMap.Release(soundHandles[handle]->bufferKey); + AUDIO_DEBUG_PRINT("Decoder uninitialized"); + } + + // Free any initialized audio buffer + if (soundHandles[handle]->maAudioBuffer) { + ma_audio_buffer_uninit_and_free(soundHandles[handle]->maAudioBuffer); + soundHandles[handle]->maAudioBuffer = nullptr; + AUDIO_DEBUG_PRINT("Audio buffer uninitialized & freed"); + } + // Invalidate any memsound stuff if (soundHandles[handle]->memLockOffset) { free_mem_lock((mem_lock *)soundHandles[handle]->memLockOffset); soundHandles[handle]->memLockId = INVALID_MEM_LOCK; soundHandles[handle]->memLockOffset = nullptr; + AUDIO_DEBUG_PRINT("MemSound stuff invalidated"); } // Now simply set the 'isUsed' member to false so that the handle can be recycled @@ -613,85 +675,56 @@ struct AudioEngine { } } }; -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// GLOBAL VARIABLES -//----------------------------------------------------------------------------------------------------- // This keeps track of the audio engine state static AudioEngine audioEngine; -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// FUNCTIONS -//----------------------------------------------------------------------------------------------------- -/// -/// This creates 16-bit signed stereo data. The sound buffer is allocated and then returned. -/// Do we really need stereo for Play(), Sound() and Beep()? -/// -/// The sound frequency -/// The duration of the sound in seconds -/// The volume of the sound (0.0 - 1.0) -/// A pointer to an integer that will receive the buffer size in bytes. This cannot be NULL -/// -static ma_uint8 *GenerateWaveform(double frequency, double length, double volume, ma_int32 *soundwave_bytes) { - static ma_uint8 *data; - static ma_int32 i; - static ma_int16 x, lastx; - static ma_int16 *sp; - static double samples; - static ma_int32 samplesi; - static ma_int32 direction; - static double value; - static double volume_multiplier; - static ma_int32 waveend; - static double gradient; - - // calculate total number of samples required - samples = length * audioEngine.sampleRate; - samplesi = samples; +/// @brief This creates a mono FP32 waveform based on frequency, length and volume +/// @param frequency The sound frequency +/// @param length The duration of the sound in seconds +/// @param volume The volume of the sound (0.0 - 1.0) +/// @param soundwave_bytes A pointer to an integer that will receive the buffer size in bytes. This cannot be NULL +/// @return A buffer containing the floating point waveform +static float *GenerateWaveform(double frequency, double length, double volume, int *soundwave_bytes) { + // Calculate the number of samples + auto samples = length * audioEngine.sampleRate; + auto samplesi = (int)samples; if (!samplesi) samplesi = 1; - *soundwave_bytes = samplesi * SAMPLE_FRAME_SIZE(ma_int16, 2); - - // Frequency equal to or above 20000 will produce silence - // This is per QuickBASIC 4.5 behavior - if (frequency < 20000) { - data = (ma_uint8 *)malloc(*soundwave_bytes); - } else { - data = (ma_uint8 *)calloc(*soundwave_bytes, sizeof(ma_uint8)); - return data; - } + // Calculate the number of bytes in the waveform data + *soundwave_bytes = samplesi * SAMPLE_FRAME_SIZE(float, 1); + // Allocate memory for the waveform data + auto data = (float *)malloc(*soundwave_bytes); if (!data) return nullptr; - sp = (ma_int16 *)data; + // Set all bytes to 0 (silence) if frequency is >= 20000. This is per QuickBASIC 4.5 behavior + if (frequency >= 20000) { + memset(data, 0, *soundwave_bytes); + return data; + } - direction = 1; - value = 0; - volume_multiplier = volume * 32767.0; - waveend = 0; - - // frequency*4.0*length is the total distance value will travel (+1,-2,+1[repeated]) + // Generate the waveform + auto direction = 1; + auto value = 0.0; + // frequency * 4.0 * length is the total distance value will travel (+1,-2,+1[repeated]) // samples is the number of steps to do this in - if (samples) - gradient = (frequency * 4.0 * length) / samples; - else - gradient = 0; // avoid division by 0 + auto gradient = samples ? (frequency * 4.0 * length) / samples : 0.0; + auto waveend = 0; + auto lastx = 1.0f; // set to 1 to avoid passing initial comparison + + for (auto i = 0; i < samplesi; i++) { + auto x = (float)(value * volume); + data[i] = x; + + if (x > 0 && lastx <= 0) + waveend = i; - lastx = 1; // set to 1 to avoid passing initial comparison - for (i = 0; i < samplesi; i++) { - x = value * volume_multiplier; - *sp++ = x; - *sp++ = x; - if (x > 0) { - if (lastx <= 0) { - waveend = i; - } - } lastx = x; + + // Update value and direction if (direction) { if ((value += gradient) >= 1.0) { direction = 0; @@ -703,61 +736,42 @@ static ma_uint8 *GenerateWaveform(double frequency, double length, double volume value = -2.0 - value; } } - } // i + } + // Update reduced size if waveend is non-zero if (waveend) - *soundwave_bytes = waveend * SAMPLE_FRAME_SIZE(ma_int16, 2); + *soundwave_bytes = waveend * SAMPLE_FRAME_SIZE(float, 1); return data; } -/// -/// Returns the of a sound buffer in bytes. -/// -/// Length in seconds -/// Length in bytes -static ma_int32 WaveformBufferSize(double length) { - static ma_int32 samples; - - samples = (ma_int32)(length * audioEngine.sampleRate); +/// @brief Returns the of a sound buffer in bytes +/// @param length Length in seconds +/// @return Length in bytes +static int WaveformBufferSize(double length) { + auto samples = (int)(length * audioEngine.sampleRate); if (!samples) samples = 1; - return samples * SAMPLE_FRAME_SIZE(ma_int16, 2); + return samples * SAMPLE_FRAME_SIZE(float, 1); } -/// -/// This sends a buffer to a raw queue for playback. -/// Buffer required in 16-bit stereo at native frequency. -/// The buffer is freed. -/// -/// Sound buffer -/// Length of buffer in bytes -/// So we have to wait until playback completes -/// A pointer to a raw queue object -static void SendWaveformToQueue(ma_uint8 *data, ma_int32 bytes, bool block) { - static ma_int32 i; - static ma_int64 time_ms; - +/// @brief This sends a FP32 mono buffer to a raw stream for playback and then frees the buffer +/// @param data FP32 mono sound buffer +/// @param bytes Length of buffer in bytes +/// @param block If this is true the function is wait until the wavefrom has finished playing +static void SendWaveformToQueue(float *data, int bytes, bool block) { if (!data) return; - // Move data into sndraw handle - for (i = 0; i < bytes; i += SAMPLE_FRAME_SIZE(ma_int16, 2)) { - audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->PushSampleFrame((float)((ma_int16 *)(data + i))[0] / 32768.0f, - (float)((ma_int16 *)(data + i))[1] / 32768.0f); - } - + // Push data to the raw stream + audioEngine.soundHandles[audioEngine.sndInternal]->rawStream->PushMonoSampleFrames(data, bytes / SAMPLE_FRAME_SIZE(float, 1)); free(data); // free the sound data - // This will push any unfinished block for playback - if (audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->last) - audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->last->force = true; - // This will wait for the block to finish (if specified) // We'll be good citizens and give-up our time-slices while waiting if (block) { - time_ms = (ma_int64)(audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->GetTimeRemaining() * 950.0 - 250.0); + auto time_ms = (audioEngine.soundHandles[audioEngine.sndInternal]->rawStream->GetSampleFramesRemaining() * 1000) / audioEngine.sampleRate; if (time_ms > 0) Sleep(time_ms); } @@ -769,32 +783,27 @@ static void SendWaveformToQueue(ma_uint8 *data, ma_int32 bytes, bool block) { /// Sound frequency /// Duration in clock ticks. There are 18.2 clock ticks per second void sub_sound(double frequency, double lengthInClockTicks) { - static ma_uint8 *data; - static ma_int32 soundwave_bytes; - - if (new_error || !audioEngine.isInitialized || audioEngine.sndInternal != 0) + if (new_error || !audioEngine.isInitialized || audioEngine.sndInternal != 0 || lengthInClockTicks == 0.0) return; - if ((frequency < 37.0) && (frequency != 0)) - goto error; - if (frequency > 32767.0) - goto error; - if (lengthInClockTicks < 0.0) - goto error; - if (lengthInClockTicks > 65535.0) - goto error; - if (lengthInClockTicks == 0.0) + if ((frequency < 37.0 && frequency != 0) || frequency > 32767.0 || lengthInClockTicks < 0.0 || lengthInClockTicks > 65535.0) { + error(5); return; + } - audioEngine.soundHandles[audioEngine.sndInternal]->type = SoundType::Raw; // This will start processing handle 0 as a raw stream + // Kickstart the raw stream if it is not already + if (!audioEngine.soundHandles[audioEngine.sndInternal]->rawStream) { + audioEngine.soundHandles[audioEngine.sndInternal]->rawStream = + RawStreamCreate(&audioEngine.maEngine, &audioEngine.soundHandles[audioEngine.sndInternal]->maSound); + AUDIO_DEBUG_CHECK(audioEngine.soundHandles[audioEngine.sndInternal]->rawStream != nullptr); + if (!audioEngine.soundHandles[audioEngine.sndInternal]->rawStream) + return; + audioEngine.soundHandles[audioEngine.sndInternal]->type = SoundType::Raw; + } - data = GenerateWaveform(frequency, lengthInClockTicks / 18.2, 1, &soundwave_bytes); + int soundwave_bytes; + auto data = GenerateWaveform(frequency, lengthInClockTicks / 18.2, 1, &soundwave_bytes); SendWaveformToQueue(data, soundwave_bytes, !audioEngine.musicBackground); - - return; - -error: - error(5); } /// @@ -816,10 +825,7 @@ void sub_beep() { sub_sound(900, 5); } /// Returns the number of sample frames left to play for Play(), Sound() & Beep() int32_t func_play(int32_t ignore) { if (audioEngine.isInitialized && audioEngine.sndInternal == 0) { - // This will push any unfinished block for playback - if (audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->last) - audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->last->force = true; - return (int32_t)audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue->GetSampleFramesRemaining(); + return (int32_t)audioEngine.soundHandles[audioEngine.sndInternal]->rawStream->GetSampleFramesRemaining(); } return 0; @@ -834,29 +840,37 @@ int32_t func_play(int32_t ignore) { /// /// The string to play void sub_play(qbs *str) { - static ma_int32 soundwave_bytes; - static ma_uint8 *b, *wave, *wave2; + static int soundwave_bytes; + static uint8_t *b, *wave, *wave2; static double d; - static ma_int32 i, bytes_left, a, x, x2, x3, x4, wave_bytes, wave_base; - static ma_int32 o = 4; + static int i, bytes_left, a, x, x2, wave_bytes, wave_base; + static int o = 4; static double t = 120; // quarter notes per minute (120/60=2 per second) static double l = 4; static double pause = 1.0 / 8.0; // ML 0.0, MN 1.0/8.0, MS 1.0/4.0 static double length, length2; // derived from l and t static double frequency; static double v = 50; - static ma_int32 n; // the semitone-intervaled note to be played - static ma_int32 n_changed; //+,#,- applied? - static ma_int64 number; - static ma_int32 number_entered; - static ma_int32 followup; // 1=play note - static ma_int32 playit; - static ma_int32 fullstops = 0; + static int n; // the semitone-intervaled note to be played + static int n_changed; //+,#,- applied? + static int64_t number; + static int number_entered; + static int followup; // 1=play note + static bool playit; + static int fullstops = 0; if (new_error || !audioEngine.isInitialized || audioEngine.sndInternal != 0) return; - audioEngine.soundHandles[audioEngine.sndInternal]->type = SoundType::Raw; // This will start processing handle 0 as a raw stream + // Kickstart the raw stream if it is not already + if (!audioEngine.soundHandles[audioEngine.sndInternal]->rawStream) { + audioEngine.soundHandles[audioEngine.sndInternal]->rawStream = + RawStreamCreate(&audioEngine.maEngine, &audioEngine.soundHandles[audioEngine.sndInternal]->maSound); + AUDIO_DEBUG_CHECK(audioEngine.soundHandles[audioEngine.sndInternal]->rawStream != nullptr); + if (!audioEngine.soundHandles[audioEngine.sndInternal]->rawStream) + return; + audioEngine.soundHandles[audioEngine.sndInternal]->type = SoundType::Raw; + } b = str->chr; bytes_left = str->len; @@ -868,7 +882,7 @@ void sub_play(qbs *str) { number = 0; followup = 0; length = 1.0 / (t / 60.0) * (4.0 / l); - playit = 0; + playit = false; wave_base = 0; // point at which new sounds will be inserted next_byte: @@ -967,12 +981,11 @@ next_byte: return; } // valid number of bits? // create a mask - static ma_int64 i64num, mask, i64x; - mask = (((ma_int64)1) << x2) - 1; - i64num = (*(ma_int64 *)(dblock + x)) & mask; + auto mask = (((int64_t)1) << x2) - 1; + auto i64num = (*(int64_t *)(dblock + x)) & mask; // signed? if (i & 128) { - mask = ((ma_int64)1) << (x2 - 1); + mask = ((int64_t)1) << (x2 - 1); if (i64num & mask) { // top bit on? mask = -1; mask <<= x2; @@ -1067,7 +1080,7 @@ next_byte: wave_base += soundwave_bytes; } - playit = 1; + playit = true; followup = 0; if (i == 44) goto next_byte; @@ -1111,8 +1124,8 @@ next_byte: if (!audioEngine.musicBackground) { audioEngine.musicBackground = true; if (playit) { - playit = 0; - SendWaveformToQueue(wave, wave_bytes, true); + playit = false; + SendWaveformToQueue((float *)wave, wave_bytes, true); } wave = NULL; } @@ -1141,7 +1154,9 @@ next_byte: error(5); return; } - n = -33 + number; + if (number == 0) + number = 125; // #217: this will generate a frequency > 44k and will force GenerateWaveform() to generate silence + n = -45 + number; // #217: fixes incorrect octave goto followup1; followup = 0; if (bytes_left < 0) @@ -1224,7 +1239,7 @@ next_byte: frequency = pow(2.0, ((double)n) / 12.0) * 440.0; // create wave - wave2 = GenerateWaveform(frequency, length2 * (1.0 - pause), v / 100.0, &soundwave_bytes); + wave2 = (ma_uint8 *)GenerateWaveform(frequency, length2 * (1.0 - pause), v / 100.0, &soundwave_bytes); if (pause > 0) { wave2 = (ma_uint8 *)realloc(wave2, soundwave_bytes + WaveformBufferSize(length2 * pause)); memset(wave2 + soundwave_bytes, 0, WaveformBufferSize(length2 * pause)); @@ -1250,21 +1265,13 @@ next_byte: } // mix or copy if (x) { - // mix - static ma_int16 *sp, *sp2; - sp = (ma_int16 *)(wave + wave_base); - sp2 = (ma_int16 *)wave2; - x2 = soundwave_bytes / 2; - for (x = 0; x < x2; x++) { - x3 = *sp2++; - x4 = *sp; - x4 += x3; - if (x4 > 32767) - x4 = 32767; - if (x4 < -32767) - x4 = -32767; - *sp++ = x4; - } // x + auto sp = (float *)(wave + wave_base); + auto sp2 = (float *)wave2; + auto samples = soundwave_bytes / SAMPLE_FRAME_SIZE(float, 1); + + for (x = 0; x < samples; x++) { + sp[x] += sp2[x]; + } } else { // copy memcpy(wave + wave_base, wave2, soundwave_bytes); @@ -1275,7 +1282,7 @@ next_byte: wave_base += soundwave_bytes; } - playit = 1; + playit = true; n_changed = 0; followup = 0; if (i == 44) @@ -1374,9 +1381,8 @@ done: return; } // unhandled data - if (playit) { - SendWaveformToQueue(wave, wave_bytes, !audioEngine.musicBackground); - } // playit + if (playit) + SendWaveformToQueue((float *)wave, wave_bytes, !audioEngine.musicBackground); } /// @@ -1385,6 +1391,50 @@ done: /// miniaudio sample rtate int32_t func__sndrate() { return audioEngine.sampleRate; } +/// @brief Creates a ma_decoder and ma_sound from a memory buffer for a valid sound handle +/// @param buffer A raw pointer to the sound file in memory +/// @param size The size of the file in memory +/// @param handle A valid sound handle +/// @return MA_SUCCESS if successful. Else, a valid ma_result +static ma_result InitializeSoundFromMemory(const void *buffer, size_t size, int32_t handle) { + if (!IS_SOUND_HANDLE_VALID(handle) || audioEngine.soundHandles[handle]->maDecoder || !buffer || !size) + return MA_INVALID_ARGS; + + audioEngine.soundHandles[handle]->maDecoder = new ma_decoder(); // allocate and zero memory + if (!audioEngine.soundHandles[handle]->maDecoder) { + AUDIO_DEBUG_PRINT("Failed to allocate memory for miniaudio decoder"); + return MA_OUT_OF_MEMORY; + } + + // Setup the decoder & attach the custom backed vtables + audioEngine.soundHandles[handle]->maDecoderConfig = ma_decoder_config_init_default(); + AudioEngineAttachCustomBackendVTables(&audioEngine.soundHandles[handle]->maDecoderConfig); + audioEngine.soundHandles[handle]->maDecoderConfig.sampleRate = audioEngine.sampleRate; + + audioEngine.maResult = ma_decoder_init_memory(buffer, size, &audioEngine.soundHandles[handle]->maDecoderConfig, + audioEngine.soundHandles[handle]->maDecoder); // initialize the decoder + if (audioEngine.maResult != MA_SUCCESS) { + delete audioEngine.soundHandles[handle]->maDecoder; + audioEngine.soundHandles[handle]->maDecoder = nullptr; + AUDIO_DEBUG_PRINT("Error %i: failed to initialize miniaudio decoder", audioEngine.maResult); + return audioEngine.maResult; + } + + // Finally, load the sound as a data source + audioEngine.maResult = ma_sound_init_from_data_source(&audioEngine.maEngine, audioEngine.soundHandles[handle]->maDecoder, + audioEngine.soundHandles[handle]->maFlags, NULL, &audioEngine.soundHandles[handle]->maSound); + + if (audioEngine.maResult != MA_SUCCESS) { + ma_decoder_uninit(audioEngine.soundHandles[handle]->maDecoder); + delete audioEngine.soundHandles[handle]->maDecoder; + audioEngine.soundHandles[handle]->maDecoder = nullptr; + AUDIO_DEBUG_PRINT("Error %i: failed to initialize sound", audioEngine.maResult); + return audioEngine.maResult; + } + + return MA_SUCCESS; +} + /// /// This loads a sound file into memory and returns a LONG handle value above 0. /// @@ -1397,7 +1447,7 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) { static qbs *fileNameZ = nullptr; static qbs *reqs = nullptr; - if (!audioEngine.isInitialized) + if (!audioEngine.isInitialized || !fileName->len) return INVALID_SOUND_HANDLE; if (!fileNameZ) @@ -1406,10 +1456,6 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) { if (!reqs) reqs = qbs_new(0, 0); - qbs_set(fileNameZ, qbs_add(fileName, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0) - if (fileNameZ->len == 1) - return INVALID_SOUND_HANDLE; // Return INVALID_SOUND_HANDLE if file name is null length string - // Alocate a sound handle int32_t handle = audioEngine.AllocateSoundHandle(); if (handle < 1) // We are not expected to open files with handle 0 @@ -1418,27 +1464,45 @@ int32_t func__sndopen(qbs *fileName, qbs *requirements, int32_t passed) { // Set some handle properties audioEngine.soundHandles[handle]->type = SoundType::Static; - // Set the flags to specifiy how we want the audio file to be opened - if (passed && requirements->len) { + // Prepare the requirements string + if (passed && requirements->len) qbs_set(reqs, qbs_ucase(requirements)); // Convert tmp str to perm str - if (func_instr(1, reqs, qbs_new_txt(REQUIREMENT_STRING_STREAM), 1)) - audioEngine.soundHandles[handle]->maFlags |= MA_SOUND_FLAG_STREAM; // Check if the user wants to stream the file + + // Set the flags to specifiy how we want the audio file to be opened + if (passed && requirements->len && func_instr(1, reqs, qbs_new_txt(REQUIREMENT_STRING_STREAM), 1)) { + audioEngine.soundHandles[handle]->maFlags |= MA_SOUND_FLAG_STREAM; // Check if the user wants to stream the file + AUDIO_DEBUG_PRINT("Sound will stream"); } else { audioEngine.soundHandles[handle]->maFlags |= MA_SOUND_FLAG_DECODE; // Else decode and load the whole sound in memory + AUDIO_DEBUG_PRINT("Sound will be fully decoded"); } - // Forward the request to miniaudio to open the sound file - audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, (const char *)fileNameZ->chr, audioEngine.soundHandles[handle]->maFlags, NULL, NULL, - &audioEngine.soundHandles[handle]->maSound); + // Load the file from file or memory based on the requirements string + if (passed && requirements->len && func_instr(1, reqs, qbs_new_txt(REQUIREMENT_STRING_MEMORY), 1)) { + // Configure a miniaudio decoder to load the sound from memory + AUDIO_DEBUG_PRINT("Loading sound from memory"); - // If the sound failed to copy, then free the handle and return INVALID_SOUND_HANDLE + audioEngine.soundHandles[handle]->bufferKey = (intptr_t)fileName->chr; // make a unique key and save it + audioEngine.bufferMap.AddBuffer(fileName->chr, fileName->len, audioEngine.soundHandles[handle]->bufferKey); // make a copy of the buffer + auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[handle]->bufferKey); // get the buffer pointer and size + audioEngine.maResult = InitializeSoundFromMemory(buffer, bufferSize, handle); // create the ma_sound + } else { + AUDIO_DEBUG_PRINT("Loading sound from file '%s'", fileNameZ->chr); + qbs_set(fileNameZ, qbs_add(fileName, qbs_new_txt_len("\0", 1))); // s1 = filename + CHR$(0) + + // Forward the request to miniaudio to open the sound file + audioEngine.maResult = ma_sound_init_from_file(&audioEngine.maEngine, (const char *)fileNameZ->chr, audioEngine.soundHandles[handle]->maFlags, NULL, + NULL, &audioEngine.soundHandles[handle]->maSound); + } + + // If the sound failed to initialize, then free the handle and return INVALID_SOUND_HANDLE if (audioEngine.maResult != MA_SUCCESS) { - AUDIO_DEBUG_PRINT("'%s' failed to open", fileNameZ->chr); + AUDIO_DEBUG_PRINT("Error %i: failed to open sound", audioEngine.maResult); audioEngine.soundHandles[handle]->isUsed = false; return INVALID_SOUND_HANDLE; } - AUDIO_DEBUG_PRINT("'%s' successfully opened", fileNameZ->chr); + AUDIO_DEBUG_PRINT("Sound successfully loaded"); return handle; } @@ -1455,6 +1519,9 @@ void sub__sndclose(int32_t handle) { // So it is completly safe to call it this way sub__sndrawdone(handle, true); + if (audioEngine.soundHandles[handle]->type == SoundType::Raw) + audioEngine.soundHandles[handle]->rawStream->stop = true; // Signal miniaudio thread that we are going to end playback + // Simply set the autokill flag to true and let the sound loop handle disposing the sound audioEngine.soundHandles[handle]->autoKill = true; } @@ -1470,23 +1537,68 @@ int32_t func__sndcopy(int32_t src_handle) { if (!audioEngine.isInitialized || !IS_SOUND_HANDLE_VALID(src_handle) || audioEngine.soundHandles[src_handle]->type != SoundType::Static) return INVALID_SOUND_HANDLE; - // Alocate a sound handle - int32_t dst_handle = audioEngine.AllocateSoundHandle(); - // Initialize the sound handle data - if (dst_handle < 1) // We are not expected to open files with handle 0 - return INVALID_SOUND_HANDLE; + int32_t dst_handle = INVALID_SOUND_HANDLE; - audioEngine.soundHandles[dst_handle]->type = SoundType::Static; // Set some handle properties - audioEngine.soundHandles[dst_handle]->maFlags = audioEngine.soundHandles[src_handle]->maFlags; // Copy the flags + // Miniaudio will not copy sounds attached to ma_audio_buffers so we'll handle the duplication ourselves + // Sadly, since this involves a buffer copy there may be a delay before the sound can play (especially if the sound is lengthy) + // The delay may be noticeable when _SNDPLAYCOPY is used multiple times on the a _SNDNEW sound + if (audioEngine.soundHandles[src_handle]->maAudioBuffer) { + AUDIO_DEBUG_PRINT("Doing custom sound copy for ma_audio_buffer"); - // Initialize a new copy of the sound - audioEngine.maResult = ma_sound_init_copy(&audioEngine.maEngine, &audioEngine.soundHandles[src_handle]->maSound, - audioEngine.soundHandles[dst_handle]->maFlags, NULL, &audioEngine.soundHandles[dst_handle]->maSound); + auto frames = audioEngine.soundHandles[src_handle]->maAudioBuffer->ref.sizeInFrames; + auto channels = audioEngine.soundHandles[src_handle]->maAudioBuffer->ref.channels; + auto format = audioEngine.soundHandles[src_handle]->maAudioBuffer->ref.format; - // If the sound failed to copy, then free the handle and return INVALID_SOUND_HANDLE - if (audioEngine.maResult != MA_SUCCESS) { - audioEngine.soundHandles[dst_handle]->isUsed = false; - return INVALID_SOUND_HANDLE; + // First create a new _SNDNEW sound with the same properties at the source + dst_handle = func__sndnew(frames, channels, CHAR_BIT * ma_get_bytes_per_sample(format)); + if (dst_handle < 1) + return INVALID_SOUND_HANDLE; + + // Next memcopy the samples from the source to the dest + memcpy((void *)audioEngine.soundHandles[dst_handle]->maAudioBuffer->ref.pData, audioEngine.soundHandles[src_handle]->maAudioBuffer->ref.pData, + frames * ma_get_bytes_per_frame(format, channels)); // naughty const void* casting, but should be OK + } else if (audioEngine.soundHandles[src_handle]->maDecoder) { + AUDIO_DEBUG_PRINT("Doing custom sound copy for ma_decoder"); + + dst_handle = audioEngine.AllocateSoundHandle(); // alocate a sound handle + if (dst_handle < 1) + return INVALID_SOUND_HANDLE; + + audioEngine.soundHandles[dst_handle]->type = SoundType::Static; // set some handle properties + audioEngine.soundHandles[dst_handle]->maFlags = audioEngine.soundHandles[src_handle]->maFlags; // copy the flags + audioEngine.soundHandles[dst_handle]->bufferKey = audioEngine.soundHandles[src_handle]->bufferKey; // copy the BufferMap unique key + audioEngine.bufferMap.AddRef(audioEngine.soundHandles[dst_handle]->bufferKey); // increase the reference count + auto [buffer, bufferSize] = audioEngine.bufferMap.GetBuffer(audioEngine.soundHandles[dst_handle]->bufferKey); // get the buffer pointer and size + audioEngine.maResult = InitializeSoundFromMemory(buffer, bufferSize, dst_handle); // create the ma_sound + + if (audioEngine.maResult != MA_SUCCESS) { + audioEngine.bufferMap.Release(audioEngine.soundHandles[dst_handle]->bufferKey); + audioEngine.soundHandles[dst_handle]->isUsed = false; + AUDIO_DEBUG_PRINT("Error %i: failed to copy sound", audioEngine.maResult); + + return INVALID_SOUND_HANDLE; + } + } else { + AUDIO_DEBUG_PRINT("Doing regular miniaudio sound copy"); + + dst_handle = audioEngine.AllocateSoundHandle(); // alocate a sound handle + if (dst_handle < 1) + return INVALID_SOUND_HANDLE; + + audioEngine.soundHandles[dst_handle]->type = SoundType::Static; // set some handle properties + audioEngine.soundHandles[dst_handle]->maFlags = audioEngine.soundHandles[src_handle]->maFlags; // copy the flags + + // Initialize a new copy of the sound + audioEngine.maResult = ma_sound_init_copy(&audioEngine.maEngine, &audioEngine.soundHandles[src_handle]->maSound, + audioEngine.soundHandles[dst_handle]->maFlags, NULL, &audioEngine.soundHandles[dst_handle]->maSound); + + // If the sound failed to copy, then free the handle and return INVALID_SOUND_HANDLE + if (audioEngine.maResult != MA_SUCCESS) { + audioEngine.soundHandles[dst_handle]->isUsed = false; + AUDIO_DEBUG_PRINT("Error %i: failed to copy sound", audioEngine.maResult); + + return INVALID_SOUND_HANDLE; + } } return dst_handle; @@ -1514,6 +1626,8 @@ void sub__sndplay(int32_t handle) { if (ma_sound_is_looping(&audioEngine.soundHandles[handle]->maSound)) { ma_sound_set_looping(&audioEngine.soundHandles[handle]->maSound, MA_FALSE); } + + AUDIO_DEBUG_PRINT("Playing sound %i", handle); } } @@ -1529,7 +1643,9 @@ void sub__sndplay(int32_t handle) { void sub__sndplaycopy(int32_t src_handle, double volume, double x, double y, double z, int32_t passed) { // We are simply going to use sndcopy, then setup some stuff like volume and autokill and then use sndplay // We are not checking if the audio engine was initialized because if not we'll get an invalid handle anyway - int32_t dst_handle = func__sndcopy(src_handle); + auto dst_handle = func__sndcopy(src_handle); + + AUDIO_DEBUG_PRINT("Source handle = %i, destination handle = %i", src_handle, dst_handle); // Check if we succeeded and then proceed if (dst_handle > 0) { @@ -1548,9 +1664,9 @@ void sub__sndplaycopy(int32_t src_handle, double volume, double x, double y, dou sub__sndplay(dst_handle); // Play the sound audioEngine.soundHandles[dst_handle]->autoKill = true; // Set to auto kill - } - AUDIO_DEBUG_PRINT("Playing sound copy %i: volume %lf, 3D (%lf, %lf, %lf)", dst_handle, volume, x, y, z); + AUDIO_DEBUG_PRINT("Playing sound copy %i: volume %lf, 3D (%lf, %lf, %lf)", dst_handle, volume, x, y, z); + } } /// @@ -1740,7 +1856,7 @@ double func__sndgetpos(int32_t handle) { /// The position to set in seconds void sub__sndsetpos(int32_t handle, double seconds) { if (audioEngine.isInitialized && IS_SOUND_HANDLE_VALID(handle) && audioEngine.soundHandles[handle]->type == SoundType::Static) { - float lengthSeconds; + float lengthSeconds = 0; audioEngine.maResult = ma_sound_get_length_in_seconds(&audioEngine.soundHandles[handle]->maSound, &lengthSeconds); // Get the length in seconds if (audioEngine.maResult != MA_SUCCESS) return; @@ -1809,17 +1925,10 @@ int32_t func__sndopenraw() { audioEngine.soundHandles[handle]->type = SoundType::Raw; // Create the raw sound object - audioEngine.soundHandles[handle]->rawQueue = new SampleFrameBlockQueue(&audioEngine.maEngine, &audioEngine.soundHandles[handle]->maSound); - if (!audioEngine.soundHandles[handle]->rawQueue) + audioEngine.soundHandles[handle]->rawStream = RawStreamCreate(&audioEngine.maEngine, &audioEngine.soundHandles[handle]->maSound); + if (!audioEngine.soundHandles[handle]->rawStream) return INVALID_SOUND_HANDLE; - // Check if everything was setup correctly - if (!audioEngine.soundHandles[handle]->rawQueue->IsSetupValid()) { - delete audioEngine.soundHandles[handle]->rawQueue; - audioEngine.soundHandles[handle]->rawQueue = nullptr; - return INVALID_SOUND_HANDLE; - } - return handle; } @@ -1845,7 +1954,7 @@ void sub__sndraw(float left, float right, int32_t handle, int32_t passed) { if (!(passed & 1)) right = left; - audioEngine.soundHandles[handle]->rawQueue->PushSampleFrame(left, right); + audioEngine.soundHandles[handle]->rawStream->PushSampleFrame(left, right); } } @@ -1855,16 +1964,19 @@ void sub__sndraw(float left, float right, int32_t handle, int32_t passed) { /// A sound handle /// How many parameters were passed? void sub__sndrawdone(int32_t handle, int32_t passed) { + // This is NOP now because miniaudio data source automatically pulls in all the samples without us doing anything + // As such, we need to think about the future of this function. Probably just leave it this way? + (void)handle; + (void)passed; + /* // Use the default raw handle if handle was not passed if (!passed) handle = audioEngine.sndInternalRaw; if (audioEngine.isInitialized && IS_SOUND_HANDLE_VALID(handle) && audioEngine.soundHandles[handle]->type == SoundType::Raw) { - // Set the last block's force flag to true - if (audioEngine.soundHandles[handle]->rawQueue->last) { - audioEngine.soundHandles[handle]->rawQueue->last->force = true; - } + // NOP } + */ } /// @@ -1879,20 +1991,77 @@ double func__sndrawlen(int32_t handle, int32_t passed) { handle = audioEngine.sndInternalRaw; if (audioEngine.isInitialized && IS_SOUND_HANDLE_VALID(handle) && audioEngine.soundHandles[handle]->type == SoundType::Raw) { - // This is for mainitianing compatibility with the SndRaw examples in the wiki - // Ideally, we should use _SNDRAWDONE at least once before checking for _SNDRAWLEN in a loop - // However, none of the examples in the wiki seem to do that - // So, we'll set the last blocks force flag to true only when there are > 1 block - // This should help avoid those examples from locking up in an infinite loop - if (audioEngine.soundHandles[handle]->rawQueue->blockCount > 1) - audioEngine.soundHandles[handle]->rawQueue->last->force = true; - - return audioEngine.soundHandles[handle]->rawQueue->GetTimeRemaining(); + return audioEngine.soundHandles[handle]->rawStream->GetTimeRemaining(); } return 0; } +/// +/// This returns a sound handle to a newly created sound's raw data in memory with the given specification. +/// The user can then fill the buffer with whatever they want (using _MEMSOUND) and play it. +/// This is basically the sound equivalent of _NEWIMAGE. +/// +/// The number of sample frames required +/// The number of sound channels. This can be 1 (mono) or 2 (stereo)/param> +/// The bit depth of the sound. This can be 8 (unsigned 8-bit), 16 (signed 16-bit) or 32 (FP32) +/// A new sound handle if successful or 0 on failure +int32_t func__sndnew(int32_t frames, int32_t channels, int32_t bits) { + if (!audioEngine.isInitialized || frames <= 0) { + AUDIO_DEBUG_CHECK(frames > 0); + return INVALID_SOUND_HANDLE; + } + + // Validate all parameters + if ((channels != 1 && channels != 2) || (bits != 16 && bits != 32 && bits != 8)) { + AUDIO_DEBUG_PRINT("Invalid channels (%i) or bits (%i)", channels, bits); + return INVALID_SOUND_HANDLE; + } + + // Alocate a sound handle + int32_t handle = audioEngine.AllocateSoundHandle(); + if (handle < 1) + return INVALID_SOUND_HANDLE; + + // Set some handle properties + audioEngine.soundHandles[handle]->type = SoundType::Static; + + // Setup the ma_audio_buffer + audioEngine.soundHandles[handle]->maAudioBufferConfig = ma_audio_buffer_config_init( + (bits == 32 ? ma_format::ma_format_f32 : (bits == 16 ? ma_format::ma_format_s16 : ma_format::ma_format_u8)), channels, frames, NULL, NULL); + + // This currently has no effect. Sample rate always defaults to engine sample rate + // Sample rate support for audio buffer is coming in miniaudio version 0.12 + // Once we have support, we can add sample rate as an optional 4th parameter + // audioEngine.soundHandles[handle]->maAudioBufferConfig.sampleRate = audioEngine.sampleRate; + + // Allocate and initialize ma_audio_buffer + audioEngine.maResult = + ma_audio_buffer_alloc_and_init(&audioEngine.soundHandles[handle]->maAudioBufferConfig, &audioEngine.soundHandles[handle]->maAudioBuffer); + if (audioEngine.maResult != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("Error %i: failed to initialize audio buffer", audioEngine.maResult); + audioEngine.soundHandles[handle]->isUsed = false; + return INVALID_SOUND_HANDLE; + } + + // Create a ma_sound from the ma_audio_buffer + audioEngine.maResult = ma_sound_init_from_data_source(&audioEngine.maEngine, audioEngine.soundHandles[handle]->maAudioBuffer, + audioEngine.soundHandles[handle]->maFlags, NULL, &audioEngine.soundHandles[handle]->maSound); + if (audioEngine.maResult != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("Error %i: failed to initialize data source", audioEngine.maResult); + ma_audio_buffer_uninit_and_free(audioEngine.soundHandles[handle]->maAudioBuffer); + audioEngine.soundHandles[handle]->maAudioBuffer = nullptr; + audioEngine.soundHandles[handle]->isUsed = false; + return INVALID_SOUND_HANDLE; + } + + AUDIO_DEBUG_PRINT("Frames = %i, channels = %i, bits = %i, ma_format = %i, pointer = %p", audioEngine.soundHandles[handle]->maAudioBuffer->ref.sizeInFrames, + audioEngine.soundHandles[handle]->maAudioBuffer->ref.channels, bits, audioEngine.soundHandles[handle]->maAudioBuffer->ref.format, + audioEngine.soundHandles[handle]->maAudioBuffer->ref.pData); + + return handle; +} + /// /// This function returns a _MEM value referring to a sound's raw data in memory using a designated sound handle created by the _SNDOPEN function. /// miniaudio supports a variety of sample and channel formats. Translating all of that to basic 2 channel 16-bit format that @@ -1905,63 +2074,106 @@ double func__sndrawlen(int32_t handle, int32_t passed) { /// This should be 0 (for interleaved) or 1 (for mono). Anything else will result in failure /// A _MEM value that can be used to access the sound data mem_block func__memsound(int32_t handle, int32_t targetChannel) { - static mem_block mb; - static ma_format maFormat; - static ma_uint32 channels; - static ma_uint64 sampleFrames; - static ma_resource_manager_data_buffer *ds; + ma_format maFormat = ma_format::ma_format_unknown; + ma_uint32 channels = 0; + ma_uint64 sampleFrames = 0; + ptrszint data = NULL; - // The sound cannot be steaming and must be completely decoded in memory + // Setup mem_block (assuming failure) + mem_block mb = {}; + mb.lock_offset = (ptrszint)mem_lock_base; + mb.lock_id = INVALID_MEM_LOCK; + + // Return invalid mem_block if audio is not initialized, handle is invalid or sound type is not static if (!audioEngine.isInitialized || !IS_SOUND_HANDLE_VALID(handle) || audioEngine.soundHandles[handle]->type != SoundType::Static || - audioEngine.soundHandles[handle]->maFlags & MA_SOUND_FLAG_STREAM || !(audioEngine.soundHandles[handle]->maFlags & MA_SOUND_FLAG_DECODE)) - goto error; - - // Get the pointer to the data source - ds = (ma_resource_manager_data_buffer *)ma_sound_get_data_source(&audioEngine.soundHandles[handle]->maSound); - if (!ds || !ds->pNode) { - AUDIO_DEBUG_PRINT("Data source pointer OR data source node pointer is NULL"); - goto error; + (targetChannel != 0 && targetChannel != 1)) { + AUDIO_DEBUG_PRINT("Invalid handle (%i), sound type (%i) or channel (%i)", handle, audioEngine.soundHandles[handle]->type, targetChannel); + return mb; } - // Check if the data is one contigious buffer or a link list of decoded pages - // We cannot have a mem object for a link list of decoded pages for obvious reasons - if (ds->pNode->data.type != ma_resource_manager_data_supply_type::ma_resource_manager_data_supply_type_decoded) { - AUDIO_DEBUG_PRINT("Data is not a contigious buffer. Type = %u", ds->pNode->data.type); - goto error; + // Check what kind of sound we are dealing with and take appropriate path + if (audioEngine.soundHandles[handle]->maAudioBuffer) { // we are dealing with a user created audio buffer + AUDIO_DEBUG_PRINT("Entering ma_audio_buffer path"); + maFormat = audioEngine.soundHandles[handle]->maAudioBuffer->ref.format; + channels = audioEngine.soundHandles[handle]->maAudioBuffer->ref.channels; + sampleFrames = audioEngine.soundHandles[handle]->maAudioBuffer->ref.sizeInFrames; + data = (ptrszint)audioEngine.soundHandles[handle]->maAudioBuffer->ref.pData; + } else { // we are dealing with a sound loaded from file or memory + AUDIO_DEBUG_PRINT("Entering ma_resource_manager_data_buffer path"); + + // The sound cannot be steaming and must be completely decoded in memory + if (audioEngine.soundHandles[handle]->maFlags & MA_SOUND_FLAG_STREAM || !(audioEngine.soundHandles[handle]->maFlags & MA_SOUND_FLAG_DECODE)) { + AUDIO_DEBUG_PRINT("Sound data is not completely decoded"); + return mb; + } + + // Get the pointer to the data source + auto ds = (ma_resource_manager_data_buffer *)ma_sound_get_data_source(&audioEngine.soundHandles[handle]->maSound); + if (!ds || !ds->pNode) { + AUDIO_DEBUG_PRINT("Data source pointer OR data source node pointer is NULL"); + return mb; + } + + // Check if the data is one contigious buffer or a link list of decoded pages + // We cannot have a mem object for a link list of decoded pages for obvious reasons + if (ds->pNode->data.type != ma_resource_manager_data_supply_type::ma_resource_manager_data_supply_type_decoded) { + AUDIO_DEBUG_PRINT("Data is not a contigious buffer. Type = %u", ds->pNode->data.type); + return mb; + } + + // Check the data pointer + if (!ds->pNode->data.backend.decoded.pData) { + AUDIO_DEBUG_PRINT("Data source data pointer is NULL"); + return mb; + } + + // Query the data format + if (ma_sound_get_data_format(&audioEngine.soundHandles[handle]->maSound, &maFormat, &channels, NULL, NULL, 0) != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("Data format query failed"); + return mb; + } + + // Get the length in sample frames + if (ma_sound_get_length_in_pcm_frames(&audioEngine.soundHandles[handle]->maSound, &sampleFrames) != MA_SUCCESS) { + AUDIO_DEBUG_PRINT("PCM frames query failed"); + return mb; + } + + data = (ptrszint)ds->pNode->data.backend.decoded.pData; } - // Check the data pointer - if (!ds->pNode->data.backend.decoded.pData) { - AUDIO_DEBUG_PRINT("Data source data pointer is NULL"); - goto error; + AUDIO_DEBUG_PRINT("Format = %u, channels = %u, frames = %llu", maFormat, channels, sampleFrames); + + // Setup type: This was not done in the old code + // But we are doing it here. By examing the type the user can now figure out if they have to use FP32 or integers + switch (maFormat) { + case ma_format::ma_format_f32: + mb.type = 4 + 256; // FP32 + break; + + case ma_format::ma_format_s32: + mb.type = 4 + 128; // signed int32 + break; + + case ma_format::ma_format_s16: + mb.type = 2 + 128; // signed int16 + break; + + case ma_format::ma_format_u8: + mb.type = 1 + 128 + 1024; // unsigned int8 + break; + + default: + AUDIO_DEBUG_PRINT("Unsupported audio format"); + return mb; } - AUDIO_DEBUG_PRINT("Data source data pointer = %p", ds->pNode->data.backend.decoded.pData); - - // Query the data format - if (ma_sound_get_data_format(&audioEngine.soundHandles[handle]->maSound, &maFormat, &channels, NULL, NULL, 0) != MA_SUCCESS) { - AUDIO_DEBUG_PRINT("Data format query failed"); - goto error; - } - - // Do not proceed if invalid (unsupported) channel values were passed - if (targetChannel != 0 && targetChannel != 1) { - AUDIO_DEBUG_PRINT("Sound channels = %u, Target channel %i not supported", channels, targetChannel); - goto error; - } - - // Get the length in sample frames - if (ma_sound_get_length_in_pcm_frames(&audioEngine.soundHandles[handle]->maSound, &sampleFrames) != MA_SUCCESS) { - AUDIO_DEBUG_PRINT("PCM frames query failed"); - goto error; - } - - AUDIO_DEBUG_PRINT("Format = %u, Channels = %u, Frames = %llu", maFormat, channels, sampleFrames); - if (audioEngine.soundHandles[handle]->memLockOffset) { + AUDIO_DEBUG_PRINT("Returning previously created mem_lock"); mb.lock_offset = (ptrszint)audioEngine.soundHandles[handle]->memLockOffset; mb.lock_id = audioEngine.soundHandles[handle]->memLockId; } else { + AUDIO_DEBUG_PRINT("Returning new mem_lock"); new_mem_lock(); mem_lock_tmp->type = MEM_TYPE_SOUND; mb.lock_offset = (ptrszint)mem_lock_tmp; @@ -1970,36 +2182,13 @@ mem_block func__memsound(int32_t handle, int32_t targetChannel) { audioEngine.soundHandles[handle]->memLockId = mem_lock_id; } - // Setup type: This was not done in the old code - // But we are doing it here. By examing the type the user can now figure out if they have to use FP32 or integers - if (maFormat == ma_format::ma_format_f32) - mb.type = 4 + 256; // FP32 - else if (maFormat == ma_format::ma_format_s32) - mb.type = 4 + 128; // Int32 - else if (maFormat == ma_format::ma_format_s16) - mb.type = 2 + 128; // Int16 - else if (maFormat == ma_format::ma_format_u8) - mb.type = 1 + 128 + 1024; // Int8 - mb.elementsize = ma_get_bytes_per_frame(maFormat, channels); // Set the element size. This is the size of each PCM frame in bytes - mb.offset = (ptrszint)ds->pNode->data.backend.decoded.pData; // Setup offset + mb.offset = data; // Setup offset mb.size = sampleFrames * mb.elementsize; // Setup size (in bytes) mb.sound = handle; // Copy the handle mb.image = 0; // Not needed. Set to 0 - AUDIO_DEBUG_PRINT("ElementSize = %lli, Size = %lli, Type = %lli", mb.elementsize, mb.size, mb.type); - - return mb; - -error: - mb.offset = 0; - mb.size = 0; - mb.lock_offset = (ptrszint)mem_lock_base; - mb.lock_id = INVALID_MEM_LOCK; - mb.type = 0; - mb.elementsize = 0; - mb.sound = 0; - mb.image = 0; + AUDIO_DEBUG_PRINT("ElementSize = %lli, size = %lli, type = %lli, pointer = %p", mb.elementsize, mb.size, mb.type, mb.offset); return mb; } @@ -2053,12 +2242,6 @@ void snd_init() { // We will use this handle internally for Play(), Beep(), Sound() etc. audioEngine.sndInternal = audioEngine.AllocateSoundHandle(); AUDIO_DEBUG_CHECK(audioEngine.sndInternal == 0); // The first handle must return 0 and this is what is used by Beep and Sound - - // Just do a basic setup and mark the type as 'none' - // If Play(), Sound(), Beep() are called, those will mark it as 'raw' - audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue = - new SampleFrameBlockQueue(&audioEngine.maEngine, &audioEngine.soundHandles[audioEngine.sndInternal]->maSound); - audioEngine.soundHandles[audioEngine.sndInternal]->type = SoundType::None; } /// @@ -2066,11 +2249,6 @@ void snd_init() { /// void snd_un_init() { if (audioEngine.isInitialized) { - // Special handling for handle 0 - audioEngine.soundHandles[audioEngine.sndInternal]->type = SoundType::None; - delete audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue; - audioEngine.soundHandles[audioEngine.sndInternal]->rawQueue = nullptr; - // Free all sound handles here for (size_t handle = 0; handle < audioEngine.soundHandles.size(); handle++) { audioEngine.FreeSoundHandle(handle); // Let FreeSoundHandle do it's thing @@ -2106,14 +2284,11 @@ void snd_mainloop() { for (size_t handle = 0; handle < audioEngine.soundHandles.size(); handle++) { // Only process handles that are in use if (audioEngine.soundHandles[handle]->isUsed) { - // Keep raw audio streams going - if (audioEngine.soundHandles[handle]->type == SoundType::Raw) - audioEngine.soundHandles[handle]->rawQueue->Update(); - // Look for stuff that is set to auto-destruct if (audioEngine.soundHandles[handle]->autoKill) { switch (audioEngine.soundHandles[handle]->type) { case SoundType::Static: + case SoundType::Raw: // Dispose the sound if it has finished playing // Note that this means that temporary looping sounds will never close // Well thats on the programmer. Probably they want it that way @@ -2122,13 +2297,6 @@ void snd_mainloop() { break; - case SoundType::Raw: - // Close the raw stream if we have no more frames in the queue or playing - if (!audioEngine.soundHandles[handle]->rawQueue->GetSampleFramesRemaining()) - audioEngine.FreeSoundHandle(handle); - - break; - case SoundType::None: if (handle != 0) AUDIO_DEBUG_PRINT("Sound type is 'None' when handle value is not 0"); @@ -2143,5 +2311,3 @@ void snd_mainloop() { } } } -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- diff --git a/internal/c/parts/audio/build.mk b/internal/c/parts/audio/build.mk index 445e40cb9..5430b6fbb 100644 --- a/internal/c/parts/audio/build.mk +++ b/internal/c/parts/audio/build.mk @@ -13,7 +13,7 @@ MINIAUDIO_REAL_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MIN MINIAUDIO_STUB_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/%.o,$(MINIAUDIO_STUB_SRCS)) ifdef DEP_AUDIO_MINIAUDIO - MINIAUDIO_OBJS := $(MINIAUDIO_REAL_OBJS) $(MA_VTABLES_OBJS) $(LIBXMP_LIB) + MINIAUDIO_OBJS := $(MINIAUDIO_REAL_OBJS) $(MA_VTABLES_OBJS) $(HIVELY_OBJS) $(LIBXMP_LIB) ifdef DEP_AUDIO_DECODE_MIDI MINIAUDIO_OBJS += $(MIDI_MA_VTABLES_OBJS) @@ -28,6 +28,6 @@ endif # DEPENDENCY_CONSOLE_ONLY is added here to keep these .cpp files from including # the FreeGLUT headers via `libqb.h`. Ideally this is fixed properly in the future. $(PATH_INTERNAL_C)/parts/audio/%.o: $(PATH_INTERNAL_C)/parts/audio/%.cpp - $(CXX) $(CXXFLAGS) -DDEPENDENCY_CONSOLE_ONLY -Wall $< -c -o $@ + $(CXX) -O2 $(CXXFLAGS) -DDEPENDENCY_CONSOLE_ONLY -Wall $< -c -o $@ CLEAN_LIST += $(MINIAUDIO_REAL_OBJS) $(MINIAUDIO_STUB_OBJS) diff --git a/internal/c/parts/audio/extras/build.mk b/internal/c/parts/audio/extras/build.mk index ca00a15c1..3b860e358 100644 --- a/internal/c/parts/audio/extras/build.mk +++ b/internal/c/parts/audio/extras/build.mk @@ -34,14 +34,22 @@ LIBXMP_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/ LIBXMP_LIB := $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite.a $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/libxmp-lite/%.c - $(CC) $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC $< -c -o $@ + $(CC) -O2 $(CFLAGS) -Wall -DLIBXMP_CORE_PLAYER -DLIBXMP_NO_PROWIZARD -DLIBXMP_NO_DEPACKERS -DBUILDING_STATIC $< -c -o $@ $(LIBXMP_LIB): $(LIBXMP_OBJS) $(AR) rcs $@ $^ +HIVELY_SRCS := hvl_replay.c + +HIVELY_OBJS += $(patsubst %.c,$(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.o,$(HIVELY_SRCS)) + +$(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.o: $(PATH_INTERNAL_C)/parts/audio/extras/hivelytracker/%.c + $(CC) -O2 $(CFLAGS) -Wall $< -c -o $@ + MA_VTABLES_SRCS := \ mod_ma_vtable.cpp \ - radv2_ma_vtable.cpp + radv2_ma_vtable.cpp \ + hively_ma_vtable.cpp MA_VTABLES_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/audio/extras/%.o,$(MA_VTABLES_SRCS)) @@ -78,5 +86,5 @@ $(PATH_INTERNAL_TEMP)/soundfont.o: $(PATH_INTERNAL_TEMP)/soundfont.sf2 endif endif -CLEAN_LIST += $(LIBXMP_LIB) $(LIBXMP_OBJS) $(MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_STUB_OBJS) +CLEAN_LIST += $(LIBXMP_LIB) $(LIBXMP_OBJS) $(HIVELY_OBJS) $(MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_OBJS) $(MIDI_MA_VTABLES_STUB_OBJS) diff --git a/internal/c/parts/audio/extras/hively_ma_vtable.cpp b/internal/c/parts/audio/extras/hively_ma_vtable.cpp new file mode 100644 index 000000000..f9f6a04e5 --- /dev/null +++ b/internal/c/parts/audio/extras/hively_ma_vtable.cpp @@ -0,0 +1,528 @@ +//-------------------------------------------------------------------------------------------------- +// ___ ___ __ _ _ ___ ___ _ _ _ ___ _ +// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___ +// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_) +// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___| +// |___/ +// +// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/) +// +// This implements a data source that decodes Amiga AHX and HLV formats +// +// https://github.com/pete-gordon/hivelytracker (BSD 3-Clause) +// +// Copyright (c) 2022 Samuel Gomes +// https://github.com/a740g +// +//-------------------------------------------------------------------------------------------------- + +#include "../miniaudio.h" +#include "audio.h" +#include "filepath.h" +#include "hivelytracker/hvl_replay.h" +#include + +constexpr auto MAX_HIVELY_FRAMES = 10 * 60 * 50; // maximium *hively* frames before timeout + +struct ma_hively { + // This part is for miniaudio + ma_data_source_base ds; /* The decoder can be used independently as a data source. */ + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void *pReadSeekTellUserData; + ma_format format; + + // This part is format specific + hvl_tune *player; // player context + ma_uint64 lengthInSampleFrames; // total length of the tune in sample frames + ma_int16 *buffer; // render buffer (16-bit stereo) + ma_uint64 bufferSamples; // total number of samples in the buffer + ma_uint64 bufferReadCursor; // where is the buffer read cursor (in samples) +}; + +static ma_result ma_hively_seek_to_pcm_frame(ma_hively *pmaHively, ma_uint64 frameIndex) { + if (pmaHively == NULL) { + return MA_INVALID_ARGS; + } + + // We can only reset the player to the beginning + if (frameIndex == 0) { + if (!hvl_InitSubsong(pmaHively->player, 0)) + return MA_INVALID_OPERATION; + + pmaHively->player->ht_SongEndReached = 0; + + return MA_SUCCESS; + } + + return MA_INVALID_OPERATION; // Anything else is not seekable +} + +static ma_result ma_hively_get_data_format(ma_hively *pmaHively, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, ma_channel *pChannelMap, + size_t channelMapCap) { + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + + if (pChannels != NULL) { + *pChannels = 0; + } + + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + + if (pChannelMap != NULL) { + memset(pChannelMap, 0, sizeof(*pChannelMap) * channelMapCap); + } + + if (pmaHively == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pmaHively->format; + } + + if (pChannels != NULL) { + *pChannels = 2; // Stereo + } + + if (pSampleRate != NULL) { + *pSampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, 2); + } + + return MA_SUCCESS; +} + +static ma_result ma_hively_read_pcm_frames(ma_hively *pmaHively, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) { + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pmaHively == NULL) { + return MA_INVALID_ARGS; + } + + auto result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + auto buffer = (ma_int16 *)pFramesOut; + + if (pmaHively->bufferReadCursor >= pmaHively->bufferSamples) { + // We are out of samples so reset the cursor and render some + pmaHively->bufferReadCursor = 0; + hvl_DecodeFrame(pmaHively->player, (int8 *)pmaHively->buffer, ((int8 *)pmaHively->buffer) + 2, 4); + } + + while (totalFramesRead < frameCount) { + if (pmaHively->bufferReadCursor >= pmaHively->bufferSamples) // break out of the loop if we finished the block + break; + + // Left channel sample + *buffer = pmaHively->buffer[pmaHively->bufferReadCursor]; + ++buffer; + pmaHively->bufferReadCursor++; + + // Right channel sample + *buffer = pmaHively->buffer[pmaHively->bufferReadCursor]; + ++buffer; + pmaHively->bufferReadCursor++; + + ++totalFramesRead; + } + + // Are we done with the tune? + if (pmaHively->player->ht_SongEndReached) + result = MA_AT_END; + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +static ma_result ma_hively_get_cursor_in_pcm_frames(ma_hively *pmaHively, ma_uint64 *pCursor) { + if (!pCursor) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (!pmaHively) { + return MA_INVALID_ARGS; + } + + return MA_NOT_IMPLEMENTED; +} + +static ma_result ma_hively_get_length_in_pcm_frames(ma_hively *pmaHively, ma_uint64 *pLength) { + if (!pLength) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (!pmaHively) { + return MA_INVALID_ARGS; + } + + if (pmaHively->lengthInSampleFrames < 1) { + return MA_INVALID_FILE; + } + + *pLength = pmaHively->lengthInSampleFrames; + + return MA_SUCCESS; +} + +static ma_result ma_hively_ds_read(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead) { + return ma_hively_read_pcm_frames((ma_hively *)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_hively_ds_seek(ma_data_source *pDataSource, ma_uint64 frameIndex) { + return ma_hively_seek_to_pcm_frame((ma_hively *)pDataSource, frameIndex); +} + +static ma_result ma_hively_ds_get_data_format(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, + ma_channel *pChannelMap, size_t channelMapCap) { + return ma_hively_get_data_format((ma_hively *)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_hively_ds_get_cursor(ma_data_source *pDataSource, ma_uint64 *pCursor) { + return ma_hively_get_cursor_in_pcm_frames((ma_hively *)pDataSource, pCursor); +} + +static ma_result ma_hively_ds_get_length(ma_data_source *pDataSource, ma_uint64 *pLength) { + return ma_hively_get_length_in_pcm_frames((ma_hively *)pDataSource, pLength); +} + +/// @brief HivelyTracker data source vtable +static ma_data_source_vtable ma_data_source_vtable_hively = { + ma_hively_ds_read, // Decodes and returns multiple frames of audio + ma_hively_ds_seek, // Can only support seeking to position 0 + ma_hively_ds_get_data_format, // Returns the audio format to miniaudio + ma_hively_ds_get_cursor, // Not supported + ma_hively_ds_get_length, // Returns the precalculated length + NULL, // onSetLooping: NOP + 0 // flags: none +}; + +static int ma_hively_of_callback__read(void *pUserData, unsigned char *pBufferOut, int bytesToRead) { + ma_hively *pmaHively = (ma_hively *)pUserData; + ma_result result; + size_t bytesRead; + + result = pmaHively->onRead(pmaHively->pReadSeekTellUserData, (void *)pBufferOut, bytesToRead, &bytesRead); + + if (result != MA_SUCCESS) { + return -1; + } + + return (int)bytesRead; +} + +static int ma_hively_of_callback__seek(void *pUserData, ma_int64 offset, int whence) { + ma_hively *pmaHively = (ma_hively *)pUserData; + ma_result result; + ma_seek_origin origin; + + if (whence == SEEK_SET) { + origin = ma_seek_origin_start; + } else if (whence == SEEK_END) { + origin = ma_seek_origin_end; + } else { + origin = ma_seek_origin_current; + } + + result = pmaHively->onSeek(pmaHively->pReadSeekTellUserData, offset, origin); + if (result != MA_SUCCESS) { + return -1; + } + + return 0; +} + +static ma_int64 ma_hively_of_callback__tell(void *pUserData) { + ma_hively *pmaHively = (ma_hively *)pUserData; + ma_result result; + ma_int64 cursor; + + if (pmaHively->onTell == NULL) { + return -1; + } + + result = pmaHively->onTell(pmaHively->pReadSeekTellUserData, &cursor); + if (result != MA_SUCCESS) { + return -1; + } + + return cursor; +} + +static ma_result ma_hively_init_internal(const ma_decoding_backend_config *pConfig, ma_hively *pmaHively) { + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pmaHively == NULL) { + return MA_INVALID_ARGS; + } + + memset(pmaHively, 0, sizeof(*pmaHively)); + pmaHively->format = ma_format::ma_format_s16; // We'll render 16-bit signed samples + + if (pConfig != NULL && pConfig->preferredFormat == ma_format::ma_format_s16) { + pmaHively->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_data_source_vtable_hively; + + result = ma_data_source_init(&dataSourceConfig, &pmaHively->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +// This help us calculate the total frame size of the tune +// Note that this must be called before rendering the tune as it actually "plays" it to a dummy buffer to calulate the length +static ma_uint64 ma_hively_get_length_in_pcm_frames_internal(ma_hively *pmaHively) { + ma_uint64 totalFramesRead = 0; + + auto frame = 0; + + while (frame < MAX_HIVELY_FRAMES) { + if (pmaHively->player->ht_SongEndReached) + break; + + hvl_DecodeFrame(pmaHively->player, (int8 *)pmaHively->buffer, ((int8 *)pmaHively->buffer) + 2, 4); + + totalFramesRead += pmaHively->bufferSamples >> 1; // divide by 2 for 2 channels + ++frame; + } + + // Reset playback position + hvl_InitSubsong(pmaHively->player, 0); + pmaHively->player->ht_SongEndReached = 0; + + return totalFramesRead; // Return the total frames rendered +} + +static ma_result ma_hively_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData, + const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hively *pmaHively) { + ma_result result; + + (void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */ + + result = ma_hively_init_internal(pConfig, pmaHively); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pmaHively->onRead = onRead; + pmaHively->onSeek = onSeek; + pmaHively->onTell = onTell; + pmaHively->pReadSeekTellUserData = pReadSeekTellUserData; + + // Find the size of the file + if (ma_hively_of_callback__seek(pmaHively, 0, SEEK_END) != 0) { + return MA_BAD_SEEK; + } + + // Calculate the length + ma_int64 file_size = ma_hively_of_callback__tell(pmaHively); + if (file_size < 1) { + return MA_INVALID_FILE; + } + + // Seek to the beginning of the file + if (ma_hively_of_callback__seek(pmaHively, 0, SEEK_SET) != 0) { + return MA_BAD_SEEK; + } + + // Allocate some memory for the tune + ma_uint8 *tune = new ma_uint8[file_size]; + if (tune == nullptr) { + return MA_OUT_OF_MEMORY; + } + + // Read the file + if (ma_hively_of_callback__read(pmaHively, tune, (int)file_size) < 1) { + delete[] tune; + return MA_IO_ERROR; + } + + hvl_InitReplayer(); // we'll initialize the re-player here + + // Ok, we have the tune in memory, now loads it + pmaHively->player = hvl_ParseTune(tune, file_size, MA_DEFAULT_SAMPLE_RATE, 3); + if (!pmaHively->player || !hvl_InitSubsong(pmaHively->player, 0)) { + if (pmaHively->player) + hvl_FreeTune(pmaHively->player); + pmaHively->player = nullptr; + } + + // Free the memory now that we don't need it anymore + delete[] tune; + + if (pmaHively->player == nullptr) { + // This means our loader failed + return MA_INVALID_FILE; + } + + // Calculate the buffer size and then allocate memory + pmaHively->bufferSamples = (MA_DEFAULT_SAMPLE_RATE * 2) / 50; + pmaHively->buffer = new ma_int16[pmaHively->bufferSamples]; + if (!pmaHively->buffer) { + hvl_FreeTune(pmaHively->player); + pmaHively->player = nullptr; + + return MA_OUT_OF_MEMORY; + } + + // Calculate the sample frames + pmaHively->lengthInSampleFrames = ma_hively_get_length_in_pcm_frames_internal(pmaHively); + + return MA_SUCCESS; +} + +static ma_result ma_hively_init_file(const char *pFilePath, const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, + ma_hively *pmaHively) { + ma_result result; + + (void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */ + + result = ma_hively_init_internal(pConfig, pmaHively); + if (result != MA_SUCCESS) { + return result; + } + + // Check the file extension + if (filepath_has_extension(pFilePath, "hvl") || filepath_has_extension(pFilePath, "ahx")) { + hvl_InitReplayer(); // we'll initialize the re-player here + + pmaHively->player = hvl_LoadTune(pFilePath, MA_DEFAULT_SAMPLE_RATE, 3); + if (!pmaHively->player || !hvl_InitSubsong(pmaHively->player, 0)) { + if (pmaHively->player) + hvl_FreeTune(pmaHively->player); + pmaHively->player = nullptr; + + return MA_INVALID_FILE; + } + + // Calculate the buffer size and then allocate memory + pmaHively->bufferSamples = (MA_DEFAULT_SAMPLE_RATE * 2) / 50; + pmaHively->buffer = new ma_int16[pmaHively->bufferSamples]; + if (!pmaHively->buffer) { + hvl_FreeTune(pmaHively->player); + pmaHively->player = nullptr; + + return MA_OUT_OF_MEMORY; + } + } else { + return MA_INVALID_FILE; + } + + // Calculate the sample frames + pmaHively->lengthInSampleFrames = ma_hively_get_length_in_pcm_frames_internal(pmaHively); + + return MA_SUCCESS; +} + +static void ma_hively_uninit(ma_hively *pmaHively, const ma_allocation_callbacks *pAllocationCallbacks) { + if (pmaHively == NULL) { + return; + } + + (void)pAllocationCallbacks; + + // Free all resources + pmaHively->lengthInSampleFrames = 0; + pmaHively->bufferSamples = 0; + pmaHively->bufferReadCursor = 0; + hvl_FreeTune(pmaHively->player); + pmaHively->player = nullptr; + delete[] pmaHively->buffer; + pmaHively->buffer = nullptr; + + ma_data_source_uninit(&pmaHively->ds); +} + +static ma_result ma_decoding_backend_init__hively(void *pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData, + const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, + ma_data_source **ppBackend) { + ma_result result; + ma_hively *pmaHively; + + (void)pUserData; + + pmaHively = (ma_hively *)ma_malloc(sizeof(ma_hively), pAllocationCallbacks); + if (pmaHively == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_hively_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pmaHively); + if (result != MA_SUCCESS) { + ma_free(pmaHively, pAllocationCallbacks); + return result; + } + + *ppBackend = pmaHively; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__hively(void *pUserData, const char *pFilePath, const ma_decoding_backend_config *pConfig, + const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source **ppBackend) { + ma_result result; + ma_hively *pmaHively; + + (void)pUserData; + + pmaHively = (ma_hively *)ma_malloc(sizeof(ma_hively), pAllocationCallbacks); + if (pmaHively == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_hively_init_file(pFilePath, pConfig, pAllocationCallbacks, pmaHively); + if (result != MA_SUCCESS) { + ma_free(pmaHively, pAllocationCallbacks); + return result; + } + + *ppBackend = pmaHively; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__hively(void *pUserData, ma_data_source *pBackend, const ma_allocation_callbacks *pAllocationCallbacks) { + ma_hively *pmaHively = (ma_hively *)pBackend; + + (void)pUserData; + + ma_hively_uninit(pmaHively, pAllocationCallbacks); + ma_free(pmaHively, pAllocationCallbacks); +} + +ma_decoding_backend_vtable ma_vtable_hively = {ma_decoding_backend_init__hively, ma_decoding_backend_init_file__hively, NULL, /* onInitFileW() */ + NULL, /* onInitMemory() */ + ma_decoding_backend_uninit__hively}; +//----------------------------------------------------------------------------------------------------- diff --git a/internal/c/parts/audio/extras/hivelytracker/hvl_replay.c b/internal/c/parts/audio/extras/hivelytracker/hvl_replay.c new file mode 100644 index 000000000..de09f4219 --- /dev/null +++ b/internal/c/parts/audio/extras/hivelytracker/hvl_replay.c @@ -0,0 +1,2048 @@ +/* +** Changes for the 1.4 release are commented. You can do +** a search for "1.4" and merge them into your own replay +** code. +** +** Changes for 1.5 are marked also. +** +** ... as are those for 1.6 +** +** ... and for 1.8 +*/ + +#include "hvl_replay.h" +#include +#include +#include +#include + +#define WHITENOISELEN (0x280 * 3) + +#define WO_LOWPASSES 0 +#define WO_TRIANGLE_04 (WO_LOWPASSES + ((0xfc + 0xfc + 0x80 * 0x1f + 0x80 + 3 * 0x280) * 31)) +#define WO_TRIANGLE_08 (WO_TRIANGLE_04 + 0x04) +#define WO_TRIANGLE_10 (WO_TRIANGLE_08 + 0x08) +#define WO_TRIANGLE_20 (WO_TRIANGLE_10 + 0x10) +#define WO_TRIANGLE_40 (WO_TRIANGLE_20 + 0x20) +#define WO_TRIANGLE_80 (WO_TRIANGLE_40 + 0x40) +#define WO_SAWTOOTH_04 (WO_TRIANGLE_80 + 0x80) +#define WO_SAWTOOTH_08 (WO_SAWTOOTH_04 + 0x04) +#define WO_SAWTOOTH_10 (WO_SAWTOOTH_08 + 0x08) +#define WO_SAWTOOTH_20 (WO_SAWTOOTH_10 + 0x10) +#define WO_SAWTOOTH_40 (WO_SAWTOOTH_20 + 0x20) +#define WO_SAWTOOTH_80 (WO_SAWTOOTH_40 + 0x40) +#define WO_SQUARES (WO_SAWTOOTH_80 + 0x80) +#define WO_WHITENOISE (WO_SQUARES + (0x80 * 0x20)) +#define WO_HIGHPASSES (WO_WHITENOISE + WHITENOISELEN) +#define WAVES_SIZE (WO_HIGHPASSES + ((0xfc + 0xfc + 0x80 * 0x1f + 0x80 + 3 * 0x280) * 31)) + +const uint16 lentab[45] = {3, 7, 0xf, 0x1f, 0x3f, 0x7f, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, (0x280 * 3) - 1}; + +const int16 vib_tab[64] = {0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, + 212, 197, 180, 161, 141, 120, 97, 74, 49, 24, 0, -24, -49, -74, -97, -120, -141, -161, -180, -197, -212, -224, + -235, -244, -250, -253, -255, -253, -250, -244, -235, -224, -212, -197, -180, -161, -141, -120, -97, -74, -49, -24}; + +const uint16 period_tab[61] = {0x0000, 0x0D60, 0x0CA0, 0x0BE8, 0x0B40, 0x0A98, 0x0A00, 0x0970, 0x08E8, 0x0868, 0x07F0, 0x0780, 0x0714, 0x06B0, 0x0650, 0x05F4, + 0x05A0, 0x054C, 0x0500, 0x04B8, 0x0474, 0x0434, 0x03F8, 0x03C0, 0x038A, 0x0358, 0x0328, 0x02FA, 0x02D0, 0x02A6, 0x0280, 0x025C, + 0x023A, 0x021A, 0x01FC, 0x01E0, 0x01C5, 0x01AC, 0x0194, 0x017D, 0x0168, 0x0153, 0x0140, 0x012E, 0x011D, 0x010D, 0x00FE, 0x00F0, + 0x00E2, 0x00D6, 0x00CA, 0x00BE, 0x00B4, 0x00AA, 0x00A0, 0x0097, 0x008F, 0x0087, 0x007F, 0x0078, 0x0071}; + +const int32 stereopan_left[5] = {128, 96, 64, 32, 0}; +const int32 stereopan_right[5] = {128, 160, 193, 225, 255}; + +const int16 filter_thing[2790] = { + -1161, -4413, -7161, -13094, 635, 13255, 2189, 6401, 9041, 16130, 13460, 5360, 6349, 12699, 19049, 25398, 30464, 32512, 32512, + 32515, 31625, 29756, 27158, 24060, 20667, 17156, 13970, 11375, 9263, 7543, 6142, 5002, 4074, 3318, 2702, 2178, 1755, 1415, + 1141, 909, 716, 563, 444, 331, -665, -2082, -6170, -9235, -13622, 12545, 9617, 3951, 8345, 11246, 18486, 6917, 3848, + 8635, 17271, 25907, 32163, 32512, 32455, 30734, 27424, 23137, 18397, 13869, 10429, 7843, 5897, 4435, 3335, 2507, 1885, 1389, + 1023, 720, 530, 353, 260, 173, 96, 32, -18, -55, -79, -92, -95, -838, -3229, -7298, -12386, -7107, 13946, + 6501, 5970, 9133, 14947, 16881, 6081, 3048, 10921, 21843, 31371, 32512, 32068, 28864, 23686, 17672, 12233, 8469, 5862, 4058, + 2809, 1944, 1346, 900, 601, 371, 223, 137, 64, 7, -34, -58, -69, -70, -63, -52, -39, -26, -14, + -5, 4984, -4476, -8102, -14892, 2894, 12723, 4883, 8010, 9750, 17887, 11790, 5099, 2520, 13207, 26415, 32512, 32457, 28690, + 22093, 14665, 9312, 5913, 3754, 2384, 1513, 911, 548, 330, 143, 3, -86, -130, -139, -125, -97, -65, -35, + -11, 6, 15, 19, 19, 16, 12, 8, 6877, -5755, -9129, -15709, 9705, 10893, 4157, 9882, 10897, 19236, 8153, + 4285, 2149, 15493, 30618, 32512, 30220, 22942, 14203, 8241, 4781, 2774, 1609, 933, 501, 220, 81, 35, 2, -18, + -26, -25, -20, -13, -7, -1, 2, 4, 4, 3, 2, 1, 0, 0, -1, 2431, -6956, -10698, -14594, + 12720, 8980, 3714, 10892, 12622, 19554, 6915, 3745, 1872, 17779, 32512, 32622, 26286, 16302, 8605, 4542, 2397, 1265, 599, + 283, 45, -92, -141, -131, -93, -49, -14, 8, 18, 18, 14, 8, 3, 0, -2, -3, -2, -2, + -1, 0, 0, -3654, -8008, -12743, -11088, 13625, 7342, 3330, 11330, 14859, 18769, 6484, 3319, 1660, 20065, 32512, 30699, + 21108, 10616, 5075, 2425, 1159, 477, 196, 1, -93, -109, -82, -44, -12, 7, 14, 13, 9, 4, 0, + -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, -7765, -8867, -14957, -5862, 13550, 6139, 2988, 11284, 17054, + 16602, 6017, 2979, 1489, 22351, 32512, 28083, 15576, 6708, 2888, 1243, 535, 188, 32, -47, -64, -47, -22, -3, + 7, 8, 5, 3, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9079, -9532, + -16960, -335, 13001, 5333, 2704, 11192, 18742, 13697, 5457, 2703, 1351, 24637, 32512, 24556, 10851, 4185, 1614, 622, 184, + 15, -57, -59, -34, -9, 5, 8, 6, 2, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -8576, -10043, -18551, 4372, 12190, 4809, 2472, 11230, 19803, 11170, 4953, 2473, 1236, 26923, + 32512, 20567, 7430, 2550, 875, 212, 51, -30, -43, -25, -6, 3, 5, 3, 1, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6960, -10485, -19740, 7864, 11223, 4449, 2279, + 11623, 20380, 9488, 4553, 2280, 1140, 29209, 31829, 16235, 4924, 1493, 452, 86, -7, -32, -20, -5, 2, 3, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -4739, -10974, -19831, 10240, 10190, 4169, 2114, 12524, 20649, 8531, 4226, 2114, 1057, 31495, 29672, 11916, 3168, 841, 121, + 17, -22, -18, -5, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -2333, -11641, -19288, 11765, 9175, 3923, 1971, 13889, 20646, 8007, 3942, 1971, + 985, 32512, 27426, 8446, 1949, 449, 45, -11, -16, -5, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, -12616, -17971, 12690, 8247, + 3693, 1846, 15662, 20271, 7658, 3692, 1846, 923, 32512, 25132, 6284, 1245, 246, -71, -78, -17, 8, 7, 1, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2232, -14001, -15234, 13198, 7447, 3478, 1736, 17409, 19411, 7332, 3472, 1736, 868, 32512, 22545, 4352, 731, + 18, -117, -40, 8, 9, 2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4197, -15836, -11480, 13408, 6791, 3281, 1639, 19224, 18074, 6978, + 3276, 1639, 819, 32512, 19657, 2706, 380, -148, -86, 2, 13, 3, -2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5863, -17878, -9460, + 13389, 6270, 3104, 1551, 20996, 16431, 6616, 3102, 1551, 776, 32512, 16633, 1921, 221, -95, -39, 5, 5, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7180, -20270, -6194, 13181, 5866, 2946, 1473, 22548, 14746, 6273, 2946, 1473, 737, 32512, 13621, + 1263, 116, -53, -15, 4, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8117, -21129, -2795, 12809, 5550, 2804, 1402, 23717, + 13326, 5962, 2804, 1402, 701, 32512, 10687, 776, -56, -56, 4, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8560, + -19953, 508, 12299, 5295, 2675, 1337, 25109, 12263, 5684, 2675, 1338, 669, 32512, 7905, 433, -36, -22, 3, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8488, -18731, 3672, 11679, 5080, 2558, 1279, 26855, 11480, 5434, 2557, 1279, 639, + 32512, 5357, 212, -95, 0, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7977, -24055, 6537, 10986, 4883, 2450, + 1225, 28611, 10918, 5206, 2450, 1225, 612, 32512, 3131, 83, -35, 2, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7088, -30584, 9054, 10265, 4696, 2351, 1176, 28707, 10494, 4996, 2351, 1175, 588, 32512, 1920, -155, -13, 4, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5952, -32627, 11249, 9564, 4519, 2260, 1130, 28678, 10113, 4803, 2260, + 1130, 565, 32512, 1059, -73, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4629, -32753, 13199, 8934, + 4351, 2175, 1088, 28446, 9775, 4623, 2175, 1087, 544, 32512, 434, -22, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3132, -32768, 15225, 8430, 4194, 2097, 1049, 30732, 9439, 4456, 2097, 1049, 524, 32512, 75, -6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, -32768, 16765, 8107, 4048, 2025, 1012, 32512, 9112, + 4302, 2025, 1012, 506, 32385, 392, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -706, -32768, + 17879, 8005, 3913, 1956, 978, 32512, 8843, 4157, 1957, 978, 489, 31184, 1671, 122, 10, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -3050, -32768, 18923, 8163, 3799, 1893, 946, 32512, 8613, 4022, 1893, 945, 473, 29903, + 3074, 316, 52, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5812, -32768, 19851, 8626, 3739, 1833, 917, + 32512, 7982, 3889, 1833, 916, 459, 28541, 4567, 731, 206, 66, 23, 8, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -9235, -32768, 20587, 9408, 3841, 1784, 889, 32512, 6486, 3688, 1776, 889, 447, 27099, 6112, 1379, 313, 135, 65, + 33, 17, 7, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, -12713, 1188, 1318, -1178, -4304, -26320, -14931, -1716, -1486, 2494, 3611, 22275, + 27450, -31839, -29668, -26258, -21608, -15880, -9560, -3211, 3138, 9369, 15281, 20717, 25571, 29774, 32512, 32512, 32512, 32512, 32512, + 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32748, 32600, 32750, 32566, 32659, 32730, 8886, 1762, 506, -1665, -12112, + -24641, -8513, -2224, 247, 3288, 9926, 25787, 28909, -31048, -27034, -20726, -12532, -3896, 4733, 13043, 20568, 27010, 32215, 32512, + 32512, 32512, 32512, 32512, 32512, 32512, 32762, 32696, 32647, 32512, 32665, 32512, 32587, 32638, 32669, 32681, 32679, 32667, 32648, + 32624, 32598, 6183, 2141, -630, -2674, -21856, -18306, -5711, -2161, 2207, 4247, 17616, 26475, 29719, -30017, -23596, -13741, -2819, + 8029, 18049, 26470, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32738, 32663, 32612, 32756, 32549, 32602, 32629, 32636, 32628, + 32610, 32588, 32564, 32542, 32524, 32510, 32500, 32494, 32492, 3604, 2248, -1495, -5612, -26800, -13545, -4745, -1390, 3443, 6973, + 23495, 27724, 30246, -28745, -19355, -6335, 6861, 19001, 28690, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32667, 32743, + 32757, 32730, 32681, 32624, 32572, 32529, 32500, 32482, 32476, 32477, 32482, 32489, 32497, 32504, 32509, 32513, 7977, 1975, -1861, + -9752, -25893, -10150, -4241, 86, 4190, 10643, 25235, 28481, 30618, -27231, -14398, 1096, 15982, 27872, 32512, 32512, 32512, 32512, + 32512, 32734, 32631, 32767, 32531, 32553, 32557, 32551, 32539, 32527, 32516, 32509, 32505, 32504, 32505, 32506, 32508, 32510, 32511, + 32512, 32512, 32512, 32511, 14529, 1389, -2028, -14813, -22765, -7845, -3774, 1986, 4706, 14562, 25541, 29019, 30894, -25476, -9294, + 8516, 23979, 32512, 32512, 32512, 32512, 32512, 32512, 32708, 32762, 32727, 32654, 32579, 32522, 32490, 32478, 32480, 32488, 32498, + 32507, 32512, 32515, 32515, 32514, 32513, 32512, 32510, 32510, 32510, 32510, 17663, 557, -2504, -19988, -19501, -6436, -3340, 4135, + 5461, 18788, 26016, 29448, 31107, -23481, -4160, 15347, 30045, 32512, 32512, 32512, 32512, 32512, 32674, 32700, 32654, 32586, 32531, + 32498, 32486, 32488, 32496, 32504, 32510, 32513, 32514, 32513, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 16286, + -402, -3522, -23951, -16641, -5631, -2983, 6251, 6837, 22781, 26712, 29788, 31277, -21244, 1108, 21806, 32512, 32512, 32512, 32512, + 32695, 32576, 32622, 32600, 32557, 32520, 32501, 32496, 32500, 32505, 32509, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 13436, -1351, -4793, -25948, -14224, -5151, -2702, 7687, 8805, 25705, 27348, 30064, 31415, + -18766, 5872, 26652, 32512, 32512, 32512, 32747, 32581, 32620, 32586, 32540, 32508, 32497, 32499, 32505, 32510, 32512, 32512, 32512, + 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10427, -2162, -7136, -26147, -12195, -4810, + -2474, 8723, 11098, 27251, 27832, 30293, 31530, -16047, 10877, 30990, 32512, 32512, 32512, 32512, 32584, 32571, 32536, 32511, 32502, + 32503, 32507, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 7797, -2748, -10188, -25174, -10519, -4515, -2281, 9397, 13473, 27937, 28213, 30487, 31627, -13087, 15816, 32512, 32512, 32512, + 32715, 32550, 32560, 32534, 32512, 32505, 32506, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 5840, -3084, -13327, -23617, -9177, -4231, -2116, 9892, 15843, 28292, 28538, + 30652, 31710, -9886, 20235, 32512, 32512, 32512, 32512, 32550, 32534, 32514, 32507, 32507, 32510, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4592, -3215, -15898, -21856, + -8141, -3958, -1972, 10401, 18229, 28612, 28824, 30796, 31781, -7103, 24037, 32512, 32512, 32745, 32535, 32534, 32517, 32508, 32508, + 32509, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 3964, -3262, -18721, -20087, -7368, -3705, -1847, 11014, 20634, 28996, 29075, 30920, 31843, -4732, 27243, 32512, + 32512, 32648, 32627, 32530, 32495, 32500, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 3858, -3404, -21965, -18398, -6801, -3479, -1738, 12009, 22960, + 29429, 29294, 31030, 31898, -2281, 30194, 32512, 32512, 32699, 32569, 32496, 32496, 32509, 32513, 32512, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 4177, -3869, + -24180, -16820, -6380, -3280, -1640, 13235, 25035, 29863, 29490, 31128, 31947, 251, 32758, 32512, 32749, 32652, 32508, 32490, 32507, + 32513, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 4837, -4913, -26436, -15364, -6056, -3103, -1553, 14759, 26704, 30256, 29664, 31215, 31991, 2863, + 32512, 32512, 32657, 32580, 32503, 32501, 32510, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 5755, -6290, -27702, -14036, -5788, -2947, -1474, + 16549, 27912, 30602, 29821, 31294, 32030, 5555, 32512, 32512, 32592, 32541, 32505, 32507, 32511, 32511, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 6898, -8911, -27788, -12841, -5550, -2805, -1403, 18509, 28687, 30906, 29963, 31364, 32066, 8328, 32512, 32512, 32623, 32511, 32502, + 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 8107, -11465, -27077, -11789, -5325, -2676, -1339, 19833, 29213, 31179, 30092, 31429, + 32098, 11181, 32512, 32512, 32561, 32508, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 9247, -13203, -25808, -10886, -5109, + -2559, -1280, 21060, 29636, 31428, 30209, 31488, 32127, 14114, 32512, 32681, 32529, 32502, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 10252, -16863, -24251, -10137, -4902, -2451, -1226, 21937, 30022, 31656, 30317, 31542, 32154, 17128, 32512, 32581, 32514, + 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11032, -22427, -22598, -9535, -4705, -2353, -1177, 20999, 30406, 31867, + 30415, 31591, 32179, 20222, 32512, 32591, 32501, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11539, -19778, -20962, + -9060, -4522, -2261, -1131, 19486, 30789, 32061, 30507, 31637, 32201, 23396, 32512, 32535, 32508, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 11803, -12759, -19353, -8690, -4353, -2177, -1089, 18499, 31165, 32240, 30591, 31678, 32222, 26651, 32512, + 32514, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11826, -7586, -17510, -8384, -4196, -2099, -1050, 26861, + 31521, 32406, 30669, 31718, 32241, 29986, 32585, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, + 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11599, + -2848, -15807, -8097, -4051, -2025, -1014, 30693, 31850, 32561, 30743, 31755, 32261, 32512, 32524, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 11037, -5302, -14051, -7770, -3913, -1958, -980, 28033, 32165, 32705, 30810, 31789, 32278, + 32512, 32729, 32536, 32513, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10114, -7837, -12293, -7348, -3782, -1894, + -948, 24926, 32473, 32512, 30873, 31819, 32294, 32512, 32512, 32580, 32527, 32515, 32512, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 8759, -10456, -10591, -6766, -3638, -1835, -917, 24058, 32600, 32512, 30934, 31850, 32309, 32512, 32512, 32729, 32591, 32537, + 32520, 32514, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, + 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6811, -13156, -9045, -5965, -3421, -1776, -890, 31582, 32246, 32512, 30988, + 31878, 32324, 32512, 32512, 32512, 32628, 32573, 32541, 32526, 32518, 32514, 32513, 32512, 32512, 32512, 32512, 32512, 32512, 32512, + 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 4835}; + +int8 waves[WAVES_SIZE]; +uint32 panning_left[256], panning_right[256]; + +void hvl_GenPanningTables(void) { + uint32 i; + float64 aa, ab; + + // Sine based panning table + aa = (3.14159265f * 2.0f) / 4.0f; // Quarter of the way through the sinewave == top peak + ab = 0.0f; // Start of the climb from zero + + for (i = 0; i < 256; i++) { + panning_left[i] = (uint32)(sin(aa) * 255.0f); + panning_right[i] = (uint32)(sin(ab) * 255.0f); + + aa += (3.14159265 * 2.0f / 4.0f) / 256.0f; + ab += (3.14159265 * 2.0f / 4.0f) / 256.0f; + } + panning_left[255] = 0; + panning_right[0] = 0; +} + +void hvl_GenSawtooth(int8 *buf, uint32 len) { + uint32 i; + int32 val, add; + + add = 256 / (len - 1); + val = -128; + + for (i = 0; i < len; i++, val += add) + *buf++ = (int8)val; +} + +void hvl_GenTriangle(int8 *buf, uint32 len) { + uint32 i; + int32 d2, d5, d1, d4; + int32 val; + int8 *buf2; + + d2 = len; + d5 = len >> 2; + d1 = 128 / d5; + d4 = -(d2 >> 1); + val = 0; + + for (i = 0; i < d5; i++) { + *buf++ = val; + val += d1; + } + *buf++ = 0x7f; + + if (d5 != 1) { + val = 128; + for (i = 0; i < d5 - 1; i++) { + val -= d1; + *buf++ = val; + } + } + + buf2 = buf + d4; + for (i = 0; i < d5 * 2; i++) { + int8 c; + + c = *buf2++; + if (c == 0x7f) + c = 0x80; + else + c = -c; + + *buf++ = c; + } +} + +void hvl_GenSquare(int8 *buf) { + uint32 i, j; + + for (i = 1; i <= 0x20; i++) { + for (j = 0; j < (0x40 - i) * 2; j++) + *buf++ = 0x80; + for (j = 0; j < i * 2; j++) + *buf++ = 0x7f; + } +} + +static inline int32 clipshifted8(int32 in) { + int16 top = (int16)(in >> 16); + if (top > 127) + in = 127 << 16; + else if (top < -128) + in = -(128 << 16); + return in; +} + +void hvl_GenFilterWaves(const int8 *buf, int8 *lowbuf, int8 *highbuf) { + + const int16 *mid_table = &filter_thing[0]; + const int16 *low_table = &filter_thing[1395]; + + int32 freq; + int32 i; + + for (i = 0, freq = 25; i < 31; i++, freq += 9) { + uint32 wv; + const int8 *a0 = buf; + + for (wv = 0; wv < 6 + 6 + 0x20 + 1; wv++) { + int32 in, fre, high, mid, low; + uint32 j; + + mid = *mid_table++ << 8; + low = *low_table++ << 8; + + for (j = 0; j <= lentab[wv]; j++) { + in = a0[j] << 16; + high = clipshifted8(in - mid - low); + fre = (high >> 8) * freq; + mid = clipshifted8(mid + fre); + fre = (mid >> 8) * freq; + low = clipshifted8(low + fre); + *highbuf++ = high >> 16; + *lowbuf++ = low >> 16; + } + a0 += lentab[wv] + 1; + } + } +} + +void hvl_GenWhiteNoise(int8 *buf, uint32 len) { + uint32 ays; + + ays = 0x41595321; + + do { + uint16 ax, bx; + int8 s; + + s = ays; + + if (ays & 0x100) { + s = 0x7f; + + if (ays & 0x8000) + s = 0x80; + } + + *buf++ = s; + len--; + + ays = (ays >> 5) | (ays << 27); + ays = (ays & 0xffffff00) | ((ays & 0xff) ^ 0x9a); + bx = ays; + ays = (ays << 2) | (ays >> 30); + ax = ays; + bx += ax; + ax ^= bx; + ays = (ays & 0xffff0000) | ax; + ays = (ays >> 3) | (ays << 29); + } while (len); +} + +void hvl_GenTables(void) { + hvl_GenPanningTables(); + hvl_GenSawtooth(&waves[WO_SAWTOOTH_04], 0x04); + hvl_GenSawtooth(&waves[WO_SAWTOOTH_08], 0x08); + hvl_GenSawtooth(&waves[WO_SAWTOOTH_10], 0x10); + hvl_GenSawtooth(&waves[WO_SAWTOOTH_20], 0x20); + hvl_GenSawtooth(&waves[WO_SAWTOOTH_40], 0x40); + hvl_GenSawtooth(&waves[WO_SAWTOOTH_80], 0x80); + hvl_GenTriangle(&waves[WO_TRIANGLE_04], 0x04); + hvl_GenTriangle(&waves[WO_TRIANGLE_08], 0x08); + hvl_GenTriangle(&waves[WO_TRIANGLE_10], 0x10); + hvl_GenTriangle(&waves[WO_TRIANGLE_20], 0x20); + hvl_GenTriangle(&waves[WO_TRIANGLE_40], 0x40); + hvl_GenTriangle(&waves[WO_TRIANGLE_80], 0x80); + hvl_GenSquare(&waves[WO_SQUARES]); + hvl_GenWhiteNoise(&waves[WO_WHITENOISE], WHITENOISELEN); + hvl_GenFilterWaves(&waves[WO_TRIANGLE_04], &waves[WO_LOWPASSES], &waves[WO_HIGHPASSES]); +} + +/* +** Waves +*/ + +void hvl_reset_some_stuff(struct hvl_tune *ht) { + uint32 i; + + for (i = 0; i < MAX_CHANNELS; i++) { + ht->ht_Voices[i].vc_Delta = 1; + ht->ht_Voices[i].vc_OverrideTranspose = 1000; // 1.5 + ht->ht_Voices[i].vc_SamplePos = ht->ht_Voices[i].vc_Track = ht->ht_Voices[i].vc_Transpose = ht->ht_Voices[i].vc_NextTrack = + ht->ht_Voices[i].vc_NextTranspose = 0; + ht->ht_Voices[i].vc_ADSRVolume = ht->ht_Voices[i].vc_InstrPeriod = ht->ht_Voices[i].vc_TrackPeriod = ht->ht_Voices[i].vc_VibratoPeriod = + ht->ht_Voices[i].vc_NoteMaxVolume = ht->ht_Voices[i].vc_PerfSubVolume = ht->ht_Voices[i].vc_TrackMasterVolume = 0; + ht->ht_Voices[i].vc_NewWaveform = ht->ht_Voices[i].vc_Waveform = ht->ht_Voices[i].vc_PlantSquare = ht->ht_Voices[i].vc_PlantPeriod = + ht->ht_Voices[i].vc_IgnoreSquare = 0; + ht->ht_Voices[i].vc_TrackOn = ht->ht_Voices[i].vc_FixedNote = ht->ht_Voices[i].vc_VolumeSlideUp = ht->ht_Voices[i].vc_VolumeSlideDown = + ht->ht_Voices[i].vc_HardCut = ht->ht_Voices[i].vc_HardCutRelease = ht->ht_Voices[i].vc_HardCutReleaseF = 0; + ht->ht_Voices[i].vc_PeriodSlideSpeed = ht->ht_Voices[i].vc_PeriodSlidePeriod = ht->ht_Voices[i].vc_PeriodSlideLimit = + ht->ht_Voices[i].vc_PeriodSlideOn = ht->ht_Voices[i].vc_PeriodSlideWithLimit = 0; + ht->ht_Voices[i].vc_PeriodPerfSlideSpeed = ht->ht_Voices[i].vc_PeriodPerfSlidePeriod = ht->ht_Voices[i].vc_PeriodPerfSlideOn = + ht->ht_Voices[i].vc_VibratoDelay = ht->ht_Voices[i].vc_VibratoCurrent = ht->ht_Voices[i].vc_VibratoDepth = ht->ht_Voices[i].vc_VibratoSpeed = 0; + ht->ht_Voices[i].vc_SquareOn = ht->ht_Voices[i].vc_SquareInit = ht->ht_Voices[i].vc_SquareLowerLimit = ht->ht_Voices[i].vc_SquareUpperLimit = + ht->ht_Voices[i].vc_SquarePos = ht->ht_Voices[i].vc_SquareSign = ht->ht_Voices[i].vc_SquareSlidingIn = ht->ht_Voices[i].vc_SquareReverse = 0; + ht->ht_Voices[i].vc_FilterOn = ht->ht_Voices[i].vc_FilterInit = ht->ht_Voices[i].vc_FilterLowerLimit = ht->ht_Voices[i].vc_FilterUpperLimit = + ht->ht_Voices[i].vc_FilterPos = ht->ht_Voices[i].vc_FilterSign = ht->ht_Voices[i].vc_FilterSpeed = ht->ht_Voices[i].vc_FilterSlidingIn = + ht->ht_Voices[i].vc_IgnoreFilter = 0; + ht->ht_Voices[i].vc_PerfCurrent = ht->ht_Voices[i].vc_PerfSpeed = ht->ht_Voices[i].vc_WaveLength = ht->ht_Voices[i].vc_NoteDelayOn = + ht->ht_Voices[i].vc_NoteCutOn = 0; + ht->ht_Voices[i].vc_AudioPeriod = ht->ht_Voices[i].vc_AudioVolume = ht->ht_Voices[i].vc_VoiceVolume = ht->ht_Voices[i].vc_VoicePeriod = + ht->ht_Voices[i].vc_VoiceNum = ht->ht_Voices[i].vc_WNRandom = 0; + ht->ht_Voices[i].vc_SquareWait = ht->ht_Voices[i].vc_FilterWait = ht->ht_Voices[i].vc_PerfWait = ht->ht_Voices[i].vc_NoteDelayWait = + ht->ht_Voices[i].vc_NoteCutWait = 0; + ht->ht_Voices[i].vc_PerfList = 0; + ht->ht_Voices[i].vc_RingSamplePos = ht->ht_Voices[i].vc_RingDelta = ht->ht_Voices[i].vc_RingPlantPeriod = ht->ht_Voices[i].vc_RingAudioPeriod = + ht->ht_Voices[i].vc_RingNewWaveform = ht->ht_Voices[i].vc_RingWaveform = ht->ht_Voices[i].vc_RingFixedPeriod = ht->ht_Voices[i].vc_RingBasePeriod = + 0; + + ht->ht_Voices[i].vc_RingMixSource = NULL; + ht->ht_Voices[i].vc_RingAudioSource = NULL; + + memset(&ht->ht_Voices[i].vc_SquareTempBuffer, 0, 0x80); + memset(&ht->ht_Voices[i].vc_ADSR, 0, sizeof(struct hvl_envelope)); + memset(&ht->ht_Voices[i].vc_VoiceBuffer, 0, 0x281); + memset(&ht->ht_Voices[i].vc_RingVoiceBuffer, 0, 0x281); + } + + for (i = 0; i < MAX_CHANNELS; i++) { + ht->ht_Voices[i].vc_WNRandom = 0x280; + ht->ht_Voices[i].vc_VoiceNum = i; + ht->ht_Voices[i].vc_TrackMasterVolume = 0x40; + ht->ht_Voices[i].vc_TrackOn = 1; + ht->ht_Voices[i].vc_MixSource = ht->ht_Voices[i].vc_VoiceBuffer; + } +} + +BOOL hvl_InitSubsong(struct hvl_tune *ht, uint32 nr) { + uint32 PosNr, i; + + if (nr > ht->ht_SubsongNr) + return FALSE; + + ht->ht_SongNum = nr; + + PosNr = 0; + if (nr) + PosNr = ht->ht_Subsongs[nr - 1]; + + ht->ht_PosNr = PosNr; + ht->ht_PosJump = 0; + ht->ht_PatternBreak = 0; + ht->ht_NoteNr = 0; + ht->ht_PosJumpNote = 0; + ht->ht_Tempo = 6; + ht->ht_StepWaitFrames = 0; + ht->ht_GetNewPosition = 1; + ht->ht_SongEndReached = 0; + ht->ht_PlayingTime = 0; + + for (i = 0; i < MAX_CHANNELS; i += 4) { + ht->ht_Voices[i + 0].vc_Pan = ht->ht_defpanleft; + ht->ht_Voices[i + 0].vc_SetPan = ht->ht_defpanleft; // 1.4 + ht->ht_Voices[i + 0].vc_PanMultLeft = panning_left[ht->ht_defpanleft]; + ht->ht_Voices[i + 0].vc_PanMultRight = panning_right[ht->ht_defpanleft]; + ht->ht_Voices[i + 1].vc_Pan = ht->ht_defpanright; + ht->ht_Voices[i + 1].vc_SetPan = ht->ht_defpanright; // 1.4 + ht->ht_Voices[i + 1].vc_PanMultLeft = panning_left[ht->ht_defpanright]; + ht->ht_Voices[i + 1].vc_PanMultRight = panning_right[ht->ht_defpanright]; + ht->ht_Voices[i + 2].vc_Pan = ht->ht_defpanright; + ht->ht_Voices[i + 2].vc_SetPan = ht->ht_defpanright; // 1.4 + ht->ht_Voices[i + 2].vc_PanMultLeft = panning_left[ht->ht_defpanright]; + ht->ht_Voices[i + 2].vc_PanMultRight = panning_right[ht->ht_defpanright]; + ht->ht_Voices[i + 3].vc_Pan = ht->ht_defpanleft; + ht->ht_Voices[i + 3].vc_SetPan = ht->ht_defpanleft; // 1.4 + ht->ht_Voices[i + 3].vc_PanMultLeft = panning_left[ht->ht_defpanleft]; + ht->ht_Voices[i + 3].vc_PanMultRight = panning_right[ht->ht_defpanleft]; + } + + hvl_reset_some_stuff(ht); + + return TRUE; +} + +void hvl_InitReplayer(void) { hvl_GenTables(); } + +struct hvl_tune *hvl_load_ahx(const uint8 *buf, uint32 buflen, uint32 defstereo, uint32 freq) { + const uint8 *bptr; + const TEXT *nptr; + uint32 i, j, k, l, posn, insn, ssn, hs, trkn, trkl; + struct hvl_tune *ht; + struct hvl_plsentry *ple; + const int32 defgain[] = {71, 72, 76, 85, 100}; + + posn = ((buf[6] & 0x0f) << 8) | buf[7]; + insn = buf[12]; + ssn = buf[13]; + trkl = buf[10]; + trkn = buf[11]; + + hs = sizeof(struct hvl_tune); + hs += sizeof(struct hvl_position) * posn; + hs += sizeof(struct hvl_instrument) * (insn + 1); + hs += sizeof(uint16) * ssn; + + // Calculate the size of all instrument PList buffers + bptr = &buf[14]; + bptr += ssn * 2; // Skip past the subsong list + bptr += posn * 4 * 2; // Skip past the positions + bptr += trkn * trkl * 3; + if ((buf[6] & 0x80) == 0) + bptr += trkl * 3; + + // *NOW* we can finally calculate PList space + for (i = 1; i <= insn; i++) { + hs += bptr[21] * sizeof(struct hvl_plsentry); + bptr += 22 + bptr[21] * 4; + } + + ht = malloc(hs); + if (!ht) { + // printf("Out of memory!\n"); + return NULL; + } + + ht->ht_Frequency = freq; + ht->ht_FreqF = (float64)freq; + + ht->ht_Positions = (struct hvl_position *)(&ht[1]); + ht->ht_Instruments = (struct hvl_instrument *)(&ht->ht_Positions[posn]); + ht->ht_Subsongs = (uint16 *)(&ht->ht_Instruments[(insn + 1)]); + ple = (struct hvl_plsentry *)(&ht->ht_Subsongs[ssn]); + + ht->ht_WaveformTab[0] = &waves[WO_TRIANGLE_04]; + ht->ht_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; + ht->ht_WaveformTab[3] = &waves[WO_WHITENOISE]; + + ht->ht_Channels = 4; + ht->ht_PositionNr = posn; + ht->ht_Restart = (buf[8] << 8) | buf[9]; + ht->ht_SpeedMultiplier = ((buf[6] >> 5) & 3) + 1; + ht->ht_TrackLength = trkl; + ht->ht_TrackNr = trkn; + ht->ht_InstrumentNr = insn; + ht->ht_SubsongNr = ssn; + ht->ht_defstereo = defstereo; + ht->ht_defpanleft = stereopan_left[ht->ht_defstereo]; + ht->ht_defpanright = stereopan_right[ht->ht_defstereo]; + ht->ht_mixgain = (defgain[ht->ht_defstereo] * 256) / 100; + + if (ht->ht_Restart >= ht->ht_PositionNr) + ht->ht_Restart = ht->ht_PositionNr - 1; + + // Do some validation + if ((ht->ht_PositionNr > 1000) || (ht->ht_TrackLength > 64) || (ht->ht_InstrumentNr > 64)) { + // printf("%d,%d,%d\n", ht->ht_PositionNr, ht->ht_TrackLength, ht->ht_InstrumentNr); + free(ht); + // printf("Invalid file.\n"); + return NULL; + } + + strncpy(ht->ht_Name, (TEXT *)&buf[(buf[4] << 8) | buf[5]], 128); + nptr = (TEXT *)&buf[((buf[4] << 8) | buf[5]) + strlen(ht->ht_Name) + 1]; + + bptr = &buf[14]; + + // Subsongs + for (i = 0; i < ht->ht_SubsongNr; i++) { + ht->ht_Subsongs[i] = (bptr[0] << 8) | bptr[1]; + if (ht->ht_Subsongs[i] >= ht->ht_PositionNr) + ht->ht_Subsongs[i] = 0; + bptr += 2; + } + + // Position list + for (i = 0; i < ht->ht_PositionNr; i++) { + for (j = 0; j < 4; j++) { + ht->ht_Positions[i].pos_Track[j] = *bptr++; + ht->ht_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; + } + } + + // Tracks + for (i = 0; i <= ht->ht_TrackNr; i++) { + if (((buf[6] & 0x80) == 0x80) && (i == 0)) { + for (j = 0; j < ht->ht_TrackLength; j++) { + ht->ht_Tracks[i][j].stp_Note = 0; + ht->ht_Tracks[i][j].stp_Instrument = 0; + ht->ht_Tracks[i][j].stp_FX = 0; + ht->ht_Tracks[i][j].stp_FXParam = 0; + ht->ht_Tracks[i][j].stp_FXb = 0; + ht->ht_Tracks[i][j].stp_FXbParam = 0; + } + continue; + } + + for (j = 0; j < ht->ht_TrackLength; j++) { + ht->ht_Tracks[i][j].stp_Note = (bptr[0] >> 2) & 0x3f; + ht->ht_Tracks[i][j].stp_Instrument = ((bptr[0] & 0x3) << 4) | (bptr[1] >> 4); + ht->ht_Tracks[i][j].stp_FX = bptr[1] & 0xf; + ht->ht_Tracks[i][j].stp_FXParam = bptr[2]; + ht->ht_Tracks[i][j].stp_FXb = 0; + ht->ht_Tracks[i][j].stp_FXbParam = 0; + bptr += 3; + } + } + + // Instruments + for (i = 1; i <= ht->ht_InstrumentNr; i++) { + if (nptr < (TEXT *)(buf + buflen)) { + strncpy(ht->ht_Instruments[i].ins_Name, nptr, 128); + nptr += strlen(nptr) + 1; + } else { + ht->ht_Instruments[i].ins_Name[0] = 0; + } + + ht->ht_Instruments[i].ins_Volume = bptr[0]; + ht->ht_Instruments[i].ins_FilterSpeed = ((bptr[1] >> 3) & 0x1f) | ((bptr[12] >> 2) & 0x20); + ht->ht_Instruments[i].ins_WaveLength = bptr[1] & 0x07; + + ht->ht_Instruments[i].ins_Envelope.aFrames = bptr[2]; + ht->ht_Instruments[i].ins_Envelope.aVolume = bptr[3]; + ht->ht_Instruments[i].ins_Envelope.dFrames = bptr[4]; + ht->ht_Instruments[i].ins_Envelope.dVolume = bptr[5]; + ht->ht_Instruments[i].ins_Envelope.sFrames = bptr[6]; + ht->ht_Instruments[i].ins_Envelope.rFrames = bptr[7]; + ht->ht_Instruments[i].ins_Envelope.rVolume = bptr[8]; + + ht->ht_Instruments[i].ins_FilterLowerLimit = bptr[12] & 0x7f; + ht->ht_Instruments[i].ins_VibratoDelay = bptr[13]; + ht->ht_Instruments[i].ins_HardCutReleaseFrames = (bptr[14] >> 4) & 0x07; + ht->ht_Instruments[i].ins_HardCutRelease = bptr[14] & 0x80 ? 1 : 0; + ht->ht_Instruments[i].ins_VibratoDepth = bptr[14] & 0x0f; + ht->ht_Instruments[i].ins_VibratoSpeed = bptr[15]; + ht->ht_Instruments[i].ins_SquareLowerLimit = bptr[16]; + ht->ht_Instruments[i].ins_SquareUpperLimit = bptr[17]; + ht->ht_Instruments[i].ins_SquareSpeed = bptr[18]; + ht->ht_Instruments[i].ins_FilterUpperLimit = bptr[19] & 0x3f; + ht->ht_Instruments[i].ins_PList.pls_Speed = bptr[20]; + ht->ht_Instruments[i].ins_PList.pls_Length = bptr[21]; + + ht->ht_Instruments[i].ins_PList.pls_Entries = ple; + ple += bptr[21]; + + bptr += 22; + for (j = 0; j < ht->ht_Instruments[i].ins_PList.pls_Length; j++) { + k = (bptr[0] >> 5) & 7; + if (k == 6) + k = 12; + if (k == 7) + k = 15; + l = (bptr[0] >> 2) & 7; + if (l == 6) + l = 12; + if (l == 7) + l = 15; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = k; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = l; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = ((bptr[0] << 1) & 6) | (bptr[1] >> 7); + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[1] >> 6) & 1; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[1] & 0x3f; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[2]; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[3]; + + // 1.6: Strip "toggle filter" commands if the module is + // version 0 (pre-filters). This is what AHX also does. + if ((buf[3] == 0) && (l == 4) && ((bptr[2] & 0xf0) != 0)) + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] &= 0x0f; + if ((buf[3] == 0) && (k == 4) && ((bptr[3] & 0xf0) != 0)) + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] &= 0x0f; // 1.8 + + bptr += 4; + } + } + + hvl_InitSubsong(ht, 0); + return ht; +} + +struct hvl_tune *hvl_load_hvl(const uint8 *buf, uint32 buflen, uint32 defstereo, uint32 freq) { + const uint8 *bptr; + const TEXT *nptr; + uint32 i, j, posn, insn, ssn, chnn, hs, trkl, trkn; + struct hvl_tune *ht; + struct hvl_plsentry *ple; + + posn = ((buf[6] & 0x0f) << 8) | buf[7]; + insn = buf[12]; + ssn = buf[13]; + chnn = (buf[8] >> 2) + 4; + trkl = buf[10]; + trkn = buf[11]; + + hs = sizeof(struct hvl_tune); + hs += sizeof(struct hvl_position) * posn; + hs += sizeof(struct hvl_instrument) * (insn + 1); + hs += sizeof(uint16) * ssn; + + // Calculate the size of all instrument PList buffers + bptr = &buf[16]; + bptr += ssn * 2; // Skip past the subsong list + bptr += posn * chnn * 2; // Skip past the positions + + // Skip past the tracks + // 1.4: Fixed two really stupid bugs that cancelled each other + // out if the module had a blank first track (which is how + // come they were missed. + for (i = ((buf[6] & 0x80) == 0x80) ? 1 : 0; i <= trkn; i++) + for (j = 0; j < trkl; j++) { + if (bptr[0] == 0x3f) { + bptr++; + continue; + } + bptr += 5; + } + + // *NOW* we can finally calculate PList space + for (i = 1; i <= insn; i++) { + hs += bptr[21] * sizeof(struct hvl_plsentry); + bptr += 22 + bptr[21] * 5; + } + + ht = malloc(hs); + if (!ht) { + // printf("Out of memory!\n"); + return NULL; + } + + ht->ht_Version = buf[3]; // 1.5 + ht->ht_Frequency = freq; + ht->ht_FreqF = (float64)freq; + + ht->ht_Positions = (struct hvl_position *)(&ht[1]); + ht->ht_Instruments = (struct hvl_instrument *)(&ht->ht_Positions[posn]); + ht->ht_Subsongs = (uint16 *)(&ht->ht_Instruments[(insn + 1)]); + ple = (struct hvl_plsentry *)(&ht->ht_Subsongs[ssn]); + + ht->ht_WaveformTab[0] = &waves[WO_TRIANGLE_04]; + ht->ht_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; + ht->ht_WaveformTab[3] = &waves[WO_WHITENOISE]; + + ht->ht_PositionNr = posn; + ht->ht_Channels = (buf[8] >> 2) + 4; + ht->ht_Restart = ((buf[8] & 3) << 8) | buf[9]; + ht->ht_SpeedMultiplier = ((buf[6] >> 5) & 3) + 1; + ht->ht_TrackLength = buf[10]; + ht->ht_TrackNr = buf[11]; + ht->ht_InstrumentNr = insn; + ht->ht_SubsongNr = ssn; + ht->ht_mixgain = (buf[14] << 8) / 100; + ht->ht_defstereo = buf[15]; + ht->ht_defpanleft = stereopan_left[ht->ht_defstereo]; + ht->ht_defpanright = stereopan_right[ht->ht_defstereo]; + + if (ht->ht_Restart >= ht->ht_PositionNr) + ht->ht_Restart = ht->ht_PositionNr - 1; + + // Do some validation + if ((ht->ht_PositionNr > 1000) || (ht->ht_TrackLength > 64) || (ht->ht_InstrumentNr > 64)) { + // printf("%d,%d,%d\n", ht->ht_PositionNr, ht->ht_TrackLength, ht->ht_InstrumentNr); + free(ht); + // printf("Invalid file.\n"); + return NULL; + } + + strncpy(ht->ht_Name, (TEXT *)&buf[(buf[4] << 8) | buf[5]], 128); + nptr = (TEXT *)&buf[((buf[4] << 8) | buf[5]) + strlen(ht->ht_Name) + 1]; + + bptr = &buf[16]; + + // Subsongs + for (i = 0; i < ht->ht_SubsongNr; i++) { + ht->ht_Subsongs[i] = (bptr[0] << 8) | bptr[1]; + bptr += 2; + } + + // Position list + for (i = 0; i < ht->ht_PositionNr; i++) { + for (j = 0; j < ht->ht_Channels; j++) { + ht->ht_Positions[i].pos_Track[j] = *bptr++; + ht->ht_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; + } + } + + // Tracks + for (i = 0; i <= ht->ht_TrackNr; i++) { + if (((buf[6] & 0x80) == 0x80) && (i == 0)) { + for (j = 0; j < ht->ht_TrackLength; j++) { + ht->ht_Tracks[i][j].stp_Note = 0; + ht->ht_Tracks[i][j].stp_Instrument = 0; + ht->ht_Tracks[i][j].stp_FX = 0; + ht->ht_Tracks[i][j].stp_FXParam = 0; + ht->ht_Tracks[i][j].stp_FXb = 0; + ht->ht_Tracks[i][j].stp_FXbParam = 0; + } + continue; + } + + for (j = 0; j < ht->ht_TrackLength; j++) { + if (bptr[0] == 0x3f) { + ht->ht_Tracks[i][j].stp_Note = 0; + ht->ht_Tracks[i][j].stp_Instrument = 0; + ht->ht_Tracks[i][j].stp_FX = 0; + ht->ht_Tracks[i][j].stp_FXParam = 0; + ht->ht_Tracks[i][j].stp_FXb = 0; + ht->ht_Tracks[i][j].stp_FXbParam = 0; + bptr++; + continue; + } + + ht->ht_Tracks[i][j].stp_Note = bptr[0]; + ht->ht_Tracks[i][j].stp_Instrument = bptr[1]; + ht->ht_Tracks[i][j].stp_FX = bptr[2] >> 4; + ht->ht_Tracks[i][j].stp_FXParam = bptr[3]; + ht->ht_Tracks[i][j].stp_FXb = bptr[2] & 0xf; + ht->ht_Tracks[i][j].stp_FXbParam = bptr[4]; + bptr += 5; + } + } + + // Instruments + for (i = 1; i <= ht->ht_InstrumentNr; i++) { + if (nptr < (TEXT *)(buf + buflen)) { + strncpy(ht->ht_Instruments[i].ins_Name, nptr, 128); + nptr += strlen(nptr) + 1; + } else { + ht->ht_Instruments[i].ins_Name[0] = 0; + } + + ht->ht_Instruments[i].ins_Volume = bptr[0]; + ht->ht_Instruments[i].ins_FilterSpeed = ((bptr[1] >> 3) & 0x1f) | ((bptr[12] >> 2) & 0x20); + ht->ht_Instruments[i].ins_WaveLength = bptr[1] & 0x07; + + ht->ht_Instruments[i].ins_Envelope.aFrames = bptr[2]; + ht->ht_Instruments[i].ins_Envelope.aVolume = bptr[3]; + ht->ht_Instruments[i].ins_Envelope.dFrames = bptr[4]; + ht->ht_Instruments[i].ins_Envelope.dVolume = bptr[5]; + ht->ht_Instruments[i].ins_Envelope.sFrames = bptr[6]; + ht->ht_Instruments[i].ins_Envelope.rFrames = bptr[7]; + ht->ht_Instruments[i].ins_Envelope.rVolume = bptr[8]; + + ht->ht_Instruments[i].ins_FilterLowerLimit = bptr[12] & 0x7f; + ht->ht_Instruments[i].ins_VibratoDelay = bptr[13]; + ht->ht_Instruments[i].ins_HardCutReleaseFrames = (bptr[14] >> 4) & 0x07; + ht->ht_Instruments[i].ins_HardCutRelease = bptr[14] & 0x80 ? 1 : 0; + ht->ht_Instruments[i].ins_VibratoDepth = bptr[14] & 0x0f; + ht->ht_Instruments[i].ins_VibratoSpeed = bptr[15]; + ht->ht_Instruments[i].ins_SquareLowerLimit = bptr[16]; + ht->ht_Instruments[i].ins_SquareUpperLimit = bptr[17]; + ht->ht_Instruments[i].ins_SquareSpeed = bptr[18]; + ht->ht_Instruments[i].ins_FilterUpperLimit = bptr[19] & 0x3f; + ht->ht_Instruments[i].ins_PList.pls_Speed = bptr[20]; + ht->ht_Instruments[i].ins_PList.pls_Length = bptr[21]; + + ht->ht_Instruments[i].ins_PList.pls_Entries = ple; + ple += bptr[21]; + + bptr += 22; + for (j = 0; j < ht->ht_Instruments[i].ins_PList.pls_Length; j++) { + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = bptr[0] & 0xf; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = (bptr[1] >> 3) & 0xf; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = bptr[1] & 7; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[2] >> 6) & 1; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[2] & 0x3f; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[3]; + ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[4]; + bptr += 5; + } + } + + hvl_InitSubsong(ht, 0); + return ht; +} + +struct hvl_tune *hvl_ParseTune(const uint8 *buf, uint32 buflen, uint32 freq, uint32 defstereo) { + struct hvl_tune *ht = NULL; + if ((buf[0] == 'T') && (buf[1] == 'H') && (buf[2] == 'X') && (buf[3] < 3)) { + ht = hvl_load_ahx(buf, buflen, defstereo, freq); + } + + else if ((buf[0] == 'H') && (buf[1] == 'V') && (buf[2] == 'L') && (buf[3] < 2)) { + ht = hvl_load_hvl(buf, buflen, defstereo, freq); + } else { + // printf("Invalid file.\n"); + } + return ht; +} + +struct hvl_tune *hvl_LoadTune(const TEXT *name, uint32 freq, uint32 defstereo) { + struct hvl_tune *ht = NULL; + uint8 *buf; + uint32 buflen; + FILE *fh; + + fh = fopen(name, "rb"); + if (!fh) { + // printf("Can't open file\n"); + return NULL; + } + + fseek(fh, 0, SEEK_END); + buflen = ftell(fh); + fseek(fh, 0, SEEK_SET); + + buf = malloc(buflen); + if (!buf) { + fclose(fh); + // printf("Out of memory!\n"); + return NULL; + } + + if (fread(buf, 1, buflen, fh) != buflen) { + fclose(fh); + free(buf); + // printf("Unable to read from file!\n"); + return NULL; + } + fclose(fh); + + ht = hvl_ParseTune(buf, buflen, freq, defstereo); + free(buf); + return ht; +} + +void hvl_FreeTune(struct hvl_tune *ht) { + if (!ht) + return; + free(ht); +} + +void hvl_process_stepfx_1(struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam) { + switch (FX) { + case 0x0: // Position Jump HI + if (((FXParam & 0x0f) > 0) && ((FXParam & 0x0f) <= 9)) + ht->ht_PosJump = FXParam & 0xf; + break; + + case 0x5: // Volume Slide + Tone Portamento + case 0xa: // Volume Slide + voice->vc_VolumeSlideDown = FXParam & 0x0f; + voice->vc_VolumeSlideUp = FXParam >> 4; + break; + + case 0x7: // Panning + if (FXParam > 127) + FXParam -= 256; + voice->vc_Pan = (FXParam + 128); + voice->vc_SetPan = (FXParam + 128); // 1.4 + voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; + voice->vc_PanMultRight = panning_right[voice->vc_Pan]; + break; + + case 0xb: // Position jump + ht->ht_PosJump = ht->ht_PosJump * 100 + (FXParam & 0x0f) + (FXParam >> 4) * 10; + ht->ht_PatternBreak = 1; + if (ht->ht_PosJump <= ht->ht_PosNr) + ht->ht_SongEndReached = 1; + break; + + case 0xd: // Pattern break + ht->ht_PosJump = ht->ht_PosNr + 1; + ht->ht_PosJumpNote = (FXParam & 0x0f) + (FXParam >> 4) * 10; + ht->ht_PatternBreak = 1; + if (ht->ht_PosJumpNote > ht->ht_TrackLength) + ht->ht_PosJumpNote = 0; + break; + + case 0xe: // Extended commands + switch (FXParam >> 4) { + case 0xc: // Note cut + if ((FXParam & 0x0f) < ht->ht_Tempo) { + voice->vc_NoteCutWait = FXParam & 0x0f; + if (voice->vc_NoteCutWait) { + voice->vc_NoteCutOn = 1; + voice->vc_HardCutRelease = 0; + } + } + break; + + // 1.6: 0xd case removed + } + break; + + case 0xf: // Speed + ht->ht_Tempo = FXParam; + if (FXParam == 0) + ht->ht_SongEndReached = 1; + break; + } +} + +void hvl_process_stepfx_2(const struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam, int32 *Note) { + switch (FX) { + case 0x9: // Set squarewave offset + voice->vc_SquarePos = FXParam >> (5 - voice->vc_WaveLength); + // voice->vc_PlantSquare = 1; + voice->vc_IgnoreSquare = 1; + break; + + case 0x3: // Tone portamento + if (FXParam != 0) + voice->vc_PeriodSlideSpeed = FXParam; + case 0x5: // Tone portamento + volume slide + + if (*Note) { + int32 new, diff; + + new = period_tab[*Note]; + diff = period_tab[voice->vc_TrackPeriod]; + diff -= new; + new = diff + voice->vc_PeriodSlidePeriod; + + if (new) + voice->vc_PeriodSlideLimit = -diff; + } + voice->vc_PeriodSlideOn = 1; + voice->vc_PeriodSlideWithLimit = 1; + *Note = 0; + break; + } +} + +void hvl_process_stepfx_3(struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam) { + int32 i; + + switch (FX) { + case 0x01: // Portamento up (period slide down) + voice->vc_PeriodSlideSpeed = -FXParam; + voice->vc_PeriodSlideOn = 1; + voice->vc_PeriodSlideWithLimit = 0; + break; + case 0x02: // Portamento down + voice->vc_PeriodSlideSpeed = FXParam; + voice->vc_PeriodSlideOn = 1; + voice->vc_PeriodSlideWithLimit = 0; + break; + case 0x04: // Filter override + if ((FXParam == 0) || (FXParam == 0x40)) + break; + if (FXParam < 0x40) { + voice->vc_IgnoreFilter = FXParam; + break; + } + if (FXParam > 0x7f) + break; + voice->vc_FilterPos = FXParam - 0x40; + break; + case 0x0c: // Volume + FXParam &= 0xff; + if (FXParam <= 0x40) { + voice->vc_NoteMaxVolume = FXParam; + break; + } + + if ((FXParam -= 0x50) < 0) + break; // 1.6 + + if (FXParam <= 0x40) { + for (i = 0; i < ht->ht_Channels; i++) + ht->ht_Voices[i].vc_TrackMasterVolume = FXParam; + break; + } + + if ((FXParam -= 0xa0 - 0x50) < 0) + break; // 1.6 + + if (FXParam <= 0x40) + voice->vc_TrackMasterVolume = FXParam; + break; + + case 0xe: // Extended commands; + switch (FXParam >> 4) { + case 0x1: // Fineslide up + voice->vc_PeriodSlidePeriod -= (FXParam & 0x0f); // 1.8 + voice->vc_PlantPeriod = 1; + break; + + case 0x2: // Fineslide down + voice->vc_PeriodSlidePeriod += (FXParam & 0x0f); // 1.8 + voice->vc_PlantPeriod = 1; + break; + + case 0x4: // Vibrato control + voice->vc_VibratoDepth = FXParam & 0x0f; + break; + + case 0x0a: // Fine volume up + voice->vc_NoteMaxVolume += FXParam & 0x0f; + + if (voice->vc_NoteMaxVolume > 0x40) + voice->vc_NoteMaxVolume = 0x40; + break; + + case 0x0b: // Fine volume down + voice->vc_NoteMaxVolume -= FXParam & 0x0f; + + if (voice->vc_NoteMaxVolume < 0) + voice->vc_NoteMaxVolume = 0; + break; + + case 0x0f: // Misc flags (1.5) + if (ht->ht_Version < 1) + break; + switch (FXParam & 0xf) { + case 1: + voice->vc_OverrideTranspose = voice->vc_Transpose; + break; + } + break; + } + break; + } +} + +void hvl_process_step(struct hvl_tune *ht, struct hvl_voice *voice) { + int32 Note, Instr, donenotedel; + const struct hvl_step *Step; + + if (voice->vc_TrackOn == 0) + return; + + voice->vc_VolumeSlideUp = voice->vc_VolumeSlideDown = 0; + + Step = &ht->ht_Tracks[ht->ht_Positions[ht->ht_PosNr].pos_Track[voice->vc_VoiceNum]][ht->ht_NoteNr]; + + Note = Step->stp_Note; + Instr = Step->stp_Instrument; + + // --------- 1.6: from here -------------- + + donenotedel = 0; + + // Do notedelay here + if (((Step->stp_FX & 0xf) == 0xe) && ((Step->stp_FXParam & 0xf0) == 0xd0)) { + if (voice->vc_NoteDelayOn) { + voice->vc_NoteDelayOn = 0; + donenotedel = 1; + } else { + if ((Step->stp_FXParam & 0x0f) < ht->ht_Tempo) { + voice->vc_NoteDelayWait = Step->stp_FXParam & 0x0f; + if (voice->vc_NoteDelayWait) { + voice->vc_NoteDelayOn = 1; + return; + } + } + } + } + + if ((donenotedel == 0) && ((Step->stp_FXb & 0xf) == 0xe) && ((Step->stp_FXbParam & 0xf0) == 0xd0)) { + if (voice->vc_NoteDelayOn) { + voice->vc_NoteDelayOn = 0; + } else { + if ((Step->stp_FXbParam & 0x0f) < ht->ht_Tempo) { + voice->vc_NoteDelayWait = Step->stp_FXbParam & 0x0f; + if (voice->vc_NoteDelayWait) { + voice->vc_NoteDelayOn = 1; + return; + } + } + } + } + + // --------- 1.6: to here -------------- + + if (Note) + voice->vc_OverrideTranspose = 1000; // 1.5 + + hvl_process_stepfx_1(ht, voice, Step->stp_FX & 0xf, Step->stp_FXParam); + hvl_process_stepfx_1(ht, voice, Step->stp_FXb & 0xf, Step->stp_FXbParam); + + if ((Instr) && (Instr <= ht->ht_InstrumentNr)) { + struct hvl_instrument *Ins; + int16 SquareLower, SquareUpper, d6, d3, d4; + + /* 1.4: Reset panning to last set position */ + voice->vc_Pan = voice->vc_SetPan; + voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; + voice->vc_PanMultRight = panning_right[voice->vc_Pan]; + + voice->vc_PeriodSlideSpeed = voice->vc_PeriodSlidePeriod = voice->vc_PeriodSlideLimit = 0; + + voice->vc_PerfSubVolume = 0x40; + voice->vc_ADSRVolume = 0; + voice->vc_Instrument = Ins = &ht->ht_Instruments[Instr]; + voice->vc_SamplePos = 0; + + voice->vc_ADSR.aFrames = Ins->ins_Envelope.aFrames; + voice->vc_ADSR.aVolume = voice->vc_ADSR.aFrames ? Ins->ins_Envelope.aVolume * 256 / voice->vc_ADSR.aFrames : Ins->ins_Envelope.aVolume * 256; // XXX + voice->vc_ADSR.dFrames = Ins->ins_Envelope.dFrames; + voice->vc_ADSR.dVolume = voice->vc_ADSR.dFrames ? (Ins->ins_Envelope.dVolume - Ins->ins_Envelope.aVolume) * 256 / voice->vc_ADSR.dFrames + : Ins->ins_Envelope.dVolume * 256; // XXX + voice->vc_ADSR.sFrames = Ins->ins_Envelope.sFrames; + voice->vc_ADSR.rFrames = Ins->ins_Envelope.rFrames; + voice->vc_ADSR.rVolume = voice->vc_ADSR.rFrames ? (Ins->ins_Envelope.rVolume - Ins->ins_Envelope.dVolume) * 256 / voice->vc_ADSR.rFrames + : Ins->ins_Envelope.rVolume * 256; // XXX + + voice->vc_WaveLength = Ins->ins_WaveLength; + voice->vc_NoteMaxVolume = Ins->ins_Volume; + + voice->vc_VibratoCurrent = 0; + voice->vc_VibratoDelay = Ins->ins_VibratoDelay; + voice->vc_VibratoDepth = Ins->ins_VibratoDepth; + voice->vc_VibratoSpeed = Ins->ins_VibratoSpeed; + voice->vc_VibratoPeriod = 0; + + voice->vc_HardCutRelease = Ins->ins_HardCutRelease; + voice->vc_HardCut = Ins->ins_HardCutReleaseFrames; + + voice->vc_IgnoreSquare = voice->vc_SquareSlidingIn = 0; + voice->vc_SquareWait = voice->vc_SquareOn = 0; + + SquareLower = Ins->ins_SquareLowerLimit >> (5 - voice->vc_WaveLength); + SquareUpper = Ins->ins_SquareUpperLimit >> (5 - voice->vc_WaveLength); + + if (SquareUpper < SquareLower) { + int16 t = SquareUpper; + SquareUpper = SquareLower; + SquareLower = t; + } + + voice->vc_SquareUpperLimit = SquareUpper; + voice->vc_SquareLowerLimit = SquareLower; + + voice->vc_IgnoreFilter = voice->vc_FilterWait = voice->vc_FilterOn = 0; + voice->vc_FilterSlidingIn = 0; + + d6 = Ins->ins_FilterSpeed; + d3 = Ins->ins_FilterLowerLimit; + d4 = Ins->ins_FilterUpperLimit; + + if (d3 & 0x80) + d6 |= 0x20; + if (d4 & 0x80) + d6 |= 0x40; + + voice->vc_FilterSpeed = d6; + d3 &= ~0x80; + d4 &= ~0x80; + + if (d3 > d4) { + int16 t = d3; + d3 = d4; + d4 = t; + } + + voice->vc_FilterUpperLimit = d4; + voice->vc_FilterLowerLimit = d3; + voice->vc_FilterPos = 32; + + voice->vc_PerfWait = voice->vc_PerfCurrent = 0; + voice->vc_PerfSpeed = Ins->ins_PList.pls_Speed; + voice->vc_PerfList = &voice->vc_Instrument->ins_PList; + + voice->vc_RingMixSource = NULL; // No ring modulation + voice->vc_RingSamplePos = 0; + voice->vc_RingPlantPeriod = 0; + voice->vc_RingNewWaveform = 0; + } + + voice->vc_PeriodSlideOn = 0; + + hvl_process_stepfx_2(ht, voice, Step->stp_FX & 0xf, Step->stp_FXParam, &Note); + hvl_process_stepfx_2(ht, voice, Step->stp_FXb & 0xf, Step->stp_FXbParam, &Note); + + if (Note) { + voice->vc_TrackPeriod = Note; + voice->vc_PlantPeriod = 1; + } + + hvl_process_stepfx_3(ht, voice, Step->stp_FX & 0xf, Step->stp_FXParam); + hvl_process_stepfx_3(ht, voice, Step->stp_FXb & 0xf, Step->stp_FXbParam); +} + +void hvl_plist_command_parse(const struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam) { + switch (FX) { + case 0: + if ((FXParam > 0) && (FXParam < 0x40)) { + if (voice->vc_IgnoreFilter) { + voice->vc_FilterPos = voice->vc_IgnoreFilter; + voice->vc_IgnoreFilter = 0; + } else { + voice->vc_FilterPos = FXParam; + } + voice->vc_NewWaveform = 1; + } + break; + + case 1: + voice->vc_PeriodPerfSlideSpeed = FXParam; + voice->vc_PeriodPerfSlideOn = 1; + break; + + case 2: + voice->vc_PeriodPerfSlideSpeed = -FXParam; + voice->vc_PeriodPerfSlideOn = 1; + break; + + case 3: + if (voice->vc_IgnoreSquare == 0) + voice->vc_SquarePos = FXParam >> (5 - voice->vc_WaveLength); + else + voice->vc_IgnoreSquare = 0; + break; + + case 4: + if (FXParam == 0) { + voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); + voice->vc_SquareSign = 1; + } else { + + if (FXParam & 0x0f) { + voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); + voice->vc_SquareSign = 1; + if ((FXParam & 0x0f) == 0x0f) + voice->vc_SquareSign = -1; + } + + if (FXParam & 0xf0) { + voice->vc_FilterInit = (voice->vc_FilterOn ^= 1); + voice->vc_FilterSign = 1; + if ((FXParam & 0xf0) == 0xf0) + voice->vc_FilterSign = -1; + } + } + break; + + case 5: + voice->vc_PerfCurrent = FXParam; + break; + + case 7: + // Ring modulate with triangle + if ((FXParam >= 1) && (FXParam <= 0x3C)) { + voice->vc_RingBasePeriod = FXParam; + voice->vc_RingFixedPeriod = 1; + } else if ((FXParam >= 0x81) && (FXParam <= 0xBC)) { + voice->vc_RingBasePeriod = FXParam - 0x80; + voice->vc_RingFixedPeriod = 0; + } else { + voice->vc_RingBasePeriod = 0; + voice->vc_RingFixedPeriod = 0; + voice->vc_RingNewWaveform = 0; + voice->vc_RingAudioSource = NULL; // turn it off + voice->vc_RingMixSource = NULL; + break; + } + voice->vc_RingWaveform = 0; + voice->vc_RingNewWaveform = 1; + voice->vc_RingPlantPeriod = 1; + break; + + case 8: // Ring modulate with sawtooth + if ((FXParam >= 1) && (FXParam <= 0x3C)) { + voice->vc_RingBasePeriod = FXParam; + voice->vc_RingFixedPeriod = 1; + } else if ((FXParam >= 0x81) && (FXParam <= 0xBC)) { + voice->vc_RingBasePeriod = FXParam - 0x80; + voice->vc_RingFixedPeriod = 0; + } else { + voice->vc_RingBasePeriod = 0; + voice->vc_RingFixedPeriod = 0; + voice->vc_RingNewWaveform = 0; + voice->vc_RingAudioSource = NULL; + voice->vc_RingMixSource = NULL; + break; + } + + voice->vc_RingWaveform = 1; + voice->vc_RingNewWaveform = 1; + voice->vc_RingPlantPeriod = 1; + break; + + /* New in HivelyTracker 1.4 */ + case 9: + if (FXParam > 127) + FXParam -= 256; + voice->vc_Pan = (FXParam + 128); + voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; + voice->vc_PanMultRight = panning_right[voice->vc_Pan]; + break; + + case 12: + if (FXParam <= 0x40) { + voice->vc_NoteMaxVolume = FXParam; + break; + } + + if ((FXParam -= 0x50) < 0) + break; + + if (FXParam <= 0x40) { + voice->vc_PerfSubVolume = FXParam; + break; + } + + if ((FXParam -= 0xa0 - 0x50) < 0) + break; + + if (FXParam <= 0x40) + voice->vc_TrackMasterVolume = FXParam; + break; + + case 15: + voice->vc_PerfSpeed = voice->vc_PerfWait = FXParam; + break; + } +} + +void hvl_process_frame(struct hvl_tune *ht, struct hvl_voice *voice) { + static const uint8 Offsets[] = {0x00, 0x04, 0x04 + 0x08, 0x04 + 0x08 + 0x10, 0x04 + 0x08 + 0x10 + 0x20, 0x04 + 0x08 + 0x10 + 0x20 + 0x40}; + + if (voice->vc_TrackOn == 0) + return; + + if (voice->vc_NoteDelayOn) { + if (voice->vc_NoteDelayWait <= 0) + hvl_process_step(ht, voice); + else + voice->vc_NoteDelayWait--; + } + + if (voice->vc_HardCut) { + int32 nextinst; + + if (ht->ht_NoteNr + 1 < ht->ht_TrackLength) + nextinst = ht->ht_Tracks[voice->vc_Track][ht->ht_NoteNr + 1].stp_Instrument; + else + nextinst = ht->ht_Tracks[voice->vc_NextTrack][0].stp_Instrument; + + if (nextinst) { + int32 d1; + + d1 = ht->ht_Tempo - voice->vc_HardCut; + + if (d1 < 0) + d1 = 0; + + if (!voice->vc_NoteCutOn) { + voice->vc_NoteCutOn = 1; + voice->vc_NoteCutWait = d1; + voice->vc_HardCutReleaseF = -(d1 - ht->ht_Tempo); + } else { + voice->vc_HardCut = 0; + } + } + } + + if (voice->vc_NoteCutOn) { + if (voice->vc_NoteCutWait <= 0) { + voice->vc_NoteCutOn = 0; + + if (voice->vc_HardCutRelease) { + voice->vc_ADSR.rFrames = voice->vc_HardCutReleaseF; + voice->vc_ADSR.rVolume = 0; + if (voice->vc_ADSR.rFrames > 0) + voice->vc_ADSR.rVolume = -(voice->vc_ADSRVolume - (voice->vc_Instrument->ins_Envelope.rVolume << 8)) / voice->vc_ADSR.rFrames; + voice->vc_ADSR.aFrames = voice->vc_ADSR.dFrames = voice->vc_ADSR.sFrames = 0; + } else { + voice->vc_NoteMaxVolume = 0; + } + } else { + voice->vc_NoteCutWait--; + } + } + + // ADSR envelope + if (voice->vc_ADSR.aFrames) { + voice->vc_ADSRVolume += voice->vc_ADSR.aVolume; + + if (--voice->vc_ADSR.aFrames <= 0) + voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.aVolume << 8; + } else if (voice->vc_ADSR.dFrames) { + + voice->vc_ADSRVolume += voice->vc_ADSR.dVolume; + + if (--voice->vc_ADSR.dFrames <= 0) + voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.dVolume << 8; + } else if (voice->vc_ADSR.sFrames) { + + voice->vc_ADSR.sFrames--; + } else if (voice->vc_ADSR.rFrames) { + + voice->vc_ADSRVolume += voice->vc_ADSR.rVolume; + + if (--voice->vc_ADSR.rFrames <= 0) + voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.rVolume << 8; + } + + // VolumeSlide + voice->vc_NoteMaxVolume = voice->vc_NoteMaxVolume + voice->vc_VolumeSlideUp - voice->vc_VolumeSlideDown; + + if (voice->vc_NoteMaxVolume < 0) + voice->vc_NoteMaxVolume = 0; + else if (voice->vc_NoteMaxVolume > 0x40) + voice->vc_NoteMaxVolume = 0x40; + + // Portamento + if (voice->vc_PeriodSlideOn) { + if (voice->vc_PeriodSlideWithLimit) { + int32 d0, d2; + + d0 = voice->vc_PeriodSlidePeriod - voice->vc_PeriodSlideLimit; + d2 = voice->vc_PeriodSlideSpeed; + + if (d0 > 0) + d2 = -d2; + + if (d0) { + int32 d3; + + d3 = (d0 + d2) ^ d0; + + if (d3 >= 0) + d0 = voice->vc_PeriodSlidePeriod + d2; + else + d0 = voice->vc_PeriodSlideLimit; + + voice->vc_PeriodSlidePeriod = d0; + voice->vc_PlantPeriod = 1; + } + } else { + voice->vc_PeriodSlidePeriod += voice->vc_PeriodSlideSpeed; + voice->vc_PlantPeriod = 1; + } + } + + // Vibrato + if (voice->vc_VibratoDepth) { + if (voice->vc_VibratoDelay <= 0) { + voice->vc_VibratoPeriod = (vib_tab[voice->vc_VibratoCurrent] * voice->vc_VibratoDepth) >> 7; + voice->vc_PlantPeriod = 1; + voice->vc_VibratoCurrent = (voice->vc_VibratoCurrent + voice->vc_VibratoSpeed) & 0x3f; + } else { + voice->vc_VibratoDelay--; + } + } + + // PList + if (voice->vc_PerfList != 0) { + if (voice->vc_Instrument && voice->vc_PerfCurrent < voice->vc_Instrument->ins_PList.pls_Length) { + int signedOverflow = (voice->vc_PerfWait == 128); + + voice->vc_PerfWait--; + if (signedOverflow || (int8)voice->vc_PerfWait <= 0) { + uint32 i; + int32 cur; + + cur = voice->vc_PerfCurrent++; + voice->vc_PerfWait = voice->vc_PerfSpeed; + + if (voice->vc_PerfList->pls_Entries[cur].ple_Waveform) { + voice->vc_Waveform = voice->vc_PerfList->pls_Entries[cur].ple_Waveform - 1; + voice->vc_NewWaveform = 1; + voice->vc_PeriodPerfSlideSpeed = voice->vc_PeriodPerfSlidePeriod = 0; + } + + // Holdwave + voice->vc_PeriodPerfSlideOn = 0; + + for (i = 0; i < 2; i++) + hvl_plist_command_parse(ht, voice, voice->vc_PerfList->pls_Entries[cur].ple_FX[i] & 0xff, + voice->vc_PerfList->pls_Entries[cur].ple_FXParam[i] & 0xff); + + // GetNote + if (voice->vc_PerfList->pls_Entries[cur].ple_Note) { + voice->vc_InstrPeriod = voice->vc_PerfList->pls_Entries[cur].ple_Note; + voice->vc_PlantPeriod = 1; + voice->vc_FixedNote = voice->vc_PerfList->pls_Entries[cur].ple_Fixed; + } + } + } else { + if (voice->vc_PerfWait) + voice->vc_PerfWait--; + else + voice->vc_PeriodPerfSlideSpeed = 0; + } + } + + // PerfPortamento + if (voice->vc_PeriodPerfSlideOn) { + voice->vc_PeriodPerfSlidePeriod -= voice->vc_PeriodPerfSlideSpeed; + + if (voice->vc_PeriodPerfSlidePeriod) + voice->vc_PlantPeriod = 1; + } + + if (voice->vc_Waveform == 3 - 1 && voice->vc_SquareOn) { + if (--voice->vc_SquareWait <= 0) { + int32 d1, d2, d3; + + d1 = voice->vc_SquareLowerLimit; + d2 = voice->vc_SquareUpperLimit; + d3 = voice->vc_SquarePos; + + if (voice->vc_SquareInit) { + voice->vc_SquareInit = 0; + + if (d3 <= d1) { + voice->vc_SquareSlidingIn = 1; + voice->vc_SquareSign = 1; + } else if (d3 >= d2) { + voice->vc_SquareSlidingIn = 1; + voice->vc_SquareSign = -1; + } + } + + // NoSquareInit + if (d1 == d3 || d2 == d3) { + if (voice->vc_SquareSlidingIn) + voice->vc_SquareSlidingIn = 0; + else + voice->vc_SquareSign = -voice->vc_SquareSign; + } + + d3 += voice->vc_SquareSign; + voice->vc_SquarePos = d3; + voice->vc_PlantSquare = 1; + voice->vc_SquareWait = voice->vc_Instrument->ins_SquareSpeed; + } + } + + if (voice->vc_FilterOn && --voice->vc_FilterWait <= 0) { + uint32 i, FMax; + int32 d1, d2, d3; + + d1 = voice->vc_FilterLowerLimit; + d2 = voice->vc_FilterUpperLimit; + d3 = voice->vc_FilterPos; + + if (voice->vc_FilterInit) { + voice->vc_FilterInit = 0; + if (d3 <= d1) { + voice->vc_FilterSlidingIn = 1; + voice->vc_FilterSign = 1; + } else if (d3 >= d2) { + voice->vc_FilterSlidingIn = 1; + voice->vc_FilterSign = -1; + } + } + + // NoFilterInit + FMax = (voice->vc_FilterSpeed < 4) ? (5 - voice->vc_FilterSpeed) : 1; + + for (i = 0; i < FMax; i++) { + if ((d1 == d3) || (d2 == d3)) { + if (voice->vc_FilterSlidingIn) + voice->vc_FilterSlidingIn = 0; + else + voice->vc_FilterSign = -voice->vc_FilterSign; + } + d3 += voice->vc_FilterSign; + } + + if (d3 < 1) + d3 = 1; + if (d3 > 63) + d3 = 63; + voice->vc_FilterPos = d3; + voice->vc_NewWaveform = 1; + voice->vc_FilterWait = voice->vc_FilterSpeed - 3; + + if (voice->vc_FilterWait < 1) + voice->vc_FilterWait = 1; + } + + if (voice->vc_Waveform == 3 - 1 || voice->vc_PlantSquare) { + // CalcSquare + uint32 i; + int32 Delta; + const int8 *SquarePtr; + int32 X; + + SquarePtr = &waves[WO_SQUARES + (voice->vc_FilterPos - 0x20) * (0xfc + 0xfc + 0x80 * 0x1f + 0x80 + 0x280 * 3)]; + X = voice->vc_SquarePos << (5 - voice->vc_WaveLength); + + if (X > 0x20) { + X = 0x40 - X; + voice->vc_SquareReverse = 1; + } + + // OkDownSquare + if (X > 0) + SquarePtr += (X - 1) << 7; + + Delta = 32 >> voice->vc_WaveLength; + ht->ht_WaveformTab[2] = voice->vc_SquareTempBuffer; + + for (i = 0; i < (1 << voice->vc_WaveLength) * 4; i++) { + voice->vc_SquareTempBuffer[i] = *SquarePtr; + SquarePtr += Delta; + } + + voice->vc_NewWaveform = 1; + voice->vc_Waveform = 3 - 1; + voice->vc_PlantSquare = 0; + } + + if (voice->vc_Waveform == 4 - 1) + voice->vc_NewWaveform = 1; + + if (voice->vc_RingNewWaveform) { + const int8 *rasrc; + + if (voice->vc_RingWaveform > 1) + voice->vc_RingWaveform = 1; + + rasrc = ht->ht_WaveformTab[voice->vc_RingWaveform]; + rasrc += Offsets[voice->vc_WaveLength]; + + voice->vc_RingAudioSource = rasrc; + } + + if (voice->vc_NewWaveform) { + const int8 *AudioSource; + + AudioSource = ht->ht_WaveformTab[voice->vc_Waveform]; + + if (voice->vc_Waveform != 3 - 1) + AudioSource += (voice->vc_FilterPos - 0x20) * (0xfc + 0xfc + 0x80 * 0x1f + 0x80 + 0x280 * 3); + + if (voice->vc_Waveform < 3 - 1) { + // GetWLWaveformlor2 + AudioSource += Offsets[voice->vc_WaveLength]; + } + + if (voice->vc_Waveform == 4 - 1) { + // AddRandomMoving + AudioSource += (voice->vc_WNRandom & (2 * 0x280 - 1)) & ~1; + // GoOnRandom + voice->vc_WNRandom += 2239384; + voice->vc_WNRandom = ((((voice->vc_WNRandom >> 8) | (voice->vc_WNRandom << 24)) + 782323) ^ 75) - 6735; + } + + voice->vc_AudioSource = AudioSource; + } + + // Ring modulation period calculation + if (voice->vc_RingAudioSource) { + voice->vc_RingAudioPeriod = voice->vc_RingBasePeriod; + + if (!(voice->vc_RingFixedPeriod)) { + if (voice->vc_OverrideTranspose != 1000) // 1.5 + voice->vc_RingAudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; + else + voice->vc_RingAudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; + } + + if (voice->vc_RingAudioPeriod > 5 * 12) + voice->vc_RingAudioPeriod = 5 * 12; + + if (voice->vc_RingAudioPeriod < 0) + voice->vc_RingAudioPeriod = 0; + + voice->vc_RingAudioPeriod = period_tab[voice->vc_RingAudioPeriod]; + + if (!(voice->vc_RingFixedPeriod)) + voice->vc_RingAudioPeriod += voice->vc_PeriodSlidePeriod; + + voice->vc_RingAudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; + + if (voice->vc_RingAudioPeriod > 0x0d60) + voice->vc_RingAudioPeriod = 0x0d60; + + if (voice->vc_RingAudioPeriod < 0x0071) + voice->vc_RingAudioPeriod = 0x0071; + } + + // Normal period calculation + voice->vc_AudioPeriod = voice->vc_InstrPeriod; + + if (!(voice->vc_FixedNote)) { + if (voice->vc_OverrideTranspose != 1000) // 1.5 + voice->vc_AudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; + else + voice->vc_AudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; + } + + if (voice->vc_AudioPeriod > 5 * 12) + voice->vc_AudioPeriod = 5 * 12; + + if (voice->vc_AudioPeriod < 0) + voice->vc_AudioPeriod = 0; + + voice->vc_AudioPeriod = period_tab[voice->vc_AudioPeriod]; + + if (!(voice->vc_FixedNote)) + voice->vc_AudioPeriod += voice->vc_PeriodSlidePeriod; + + voice->vc_AudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; + + if (voice->vc_AudioPeriod > 0x0d60) + voice->vc_AudioPeriod = 0x0d60; + + if (voice->vc_AudioPeriod < 0x0071) + voice->vc_AudioPeriod = 0x0071; + + voice->vc_AudioVolume = + (((((((voice->vc_ADSRVolume >> 8) * voice->vc_NoteMaxVolume) >> 6) * voice->vc_PerfSubVolume) >> 6) * voice->vc_TrackMasterVolume) >> 6); +} + +void hvl_set_audio(struct hvl_voice *voice, float64 freqf) { + if (voice->vc_TrackOn == 0) { + voice->vc_VoiceVolume = 0; + return; + } + + voice->vc_VoiceVolume = voice->vc_AudioVolume; + + if (voice->vc_PlantPeriod) { + float64 freq2; + uint32 delta; + + voice->vc_PlantPeriod = 0; + voice->vc_VoicePeriod = voice->vc_AudioPeriod; + + freq2 = Period2Freq(voice->vc_AudioPeriod); + delta = (uint32)(freq2 / freqf); + + if (delta > (0x280 << 16)) + delta -= (0x280 << 16); + if (delta == 0) + delta = 1; + voice->vc_Delta = delta; + } + + if (voice->vc_NewWaveform) { + const int8 *src; + + src = voice->vc_AudioSource; + + if (voice->vc_Waveform == 4 - 1) { + memcpy(&voice->vc_VoiceBuffer[0], src, 0x280); + } else { + uint32 i, WaveLoops; + + WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; + + for (i = 0; i < WaveLoops; i++) + memcpy(&voice->vc_VoiceBuffer[i * 4 * (1 << voice->vc_WaveLength)], src, 4 * (1 << voice->vc_WaveLength)); + } + + voice->vc_VoiceBuffer[0x280] = voice->vc_VoiceBuffer[0]; + voice->vc_MixSource = voice->vc_VoiceBuffer; + } + + /* Ring Modulation */ + if (voice->vc_RingPlantPeriod) { + float64 freq2; + uint32 delta; + + voice->vc_RingPlantPeriod = 0; + freq2 = Period2Freq(voice->vc_RingAudioPeriod); + delta = (uint32)(freq2 / freqf); + + if (delta > (0x280 << 16)) + delta -= (0x280 << 16); + if (delta == 0) + delta = 1; + voice->vc_RingDelta = delta; + } + + if (voice->vc_RingNewWaveform) { + const int8 *src; + uint32 i, WaveLoops; + + src = voice->vc_RingAudioSource; + + WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; + + for (i = 0; i < WaveLoops; i++) + memcpy(&voice->vc_RingVoiceBuffer[i * 4 * (1 << voice->vc_WaveLength)], src, 4 * (1 << voice->vc_WaveLength)); + + voice->vc_RingVoiceBuffer[0x280] = voice->vc_RingVoiceBuffer[0]; + voice->vc_RingMixSource = voice->vc_RingVoiceBuffer; + } +} + +void hvl_play_irq(struct hvl_tune *ht) { + uint32 i; + + if (ht->ht_StepWaitFrames == 0) { + if (ht->ht_GetNewPosition) { + int32 nextpos = (ht->ht_PosNr + 1 == ht->ht_PositionNr) ? 0 : (ht->ht_PosNr + 1); + + for (i = 0; i < ht->ht_Channels; i++) { + ht->ht_Voices[i].vc_Track = ht->ht_Positions[ht->ht_PosNr].pos_Track[i]; + ht->ht_Voices[i].vc_Transpose = ht->ht_Positions[ht->ht_PosNr].pos_Transpose[i]; + ht->ht_Voices[i].vc_NextTrack = ht->ht_Positions[nextpos].pos_Track[i]; + ht->ht_Voices[i].vc_NextTranspose = ht->ht_Positions[nextpos].pos_Transpose[i]; + } + ht->ht_GetNewPosition = 0; + } + + for (i = 0; i < ht->ht_Channels; i++) + hvl_process_step(ht, &ht->ht_Voices[i]); + + ht->ht_StepWaitFrames = ht->ht_Tempo; + } + + for (i = 0; i < ht->ht_Channels; i++) + hvl_process_frame(ht, &ht->ht_Voices[i]); + + ht->ht_PlayingTime++; + if (--ht->ht_StepWaitFrames == 0) { + if (!ht->ht_PatternBreak) { + ht->ht_NoteNr++; + if (ht->ht_NoteNr >= ht->ht_TrackLength) { + ht->ht_PosJump = ht->ht_PosNr + 1; + ht->ht_PosJumpNote = 0; + ht->ht_PatternBreak = 1; + } + } + + if (ht->ht_PatternBreak) { + ht->ht_PatternBreak = 0; + ht->ht_PosNr = ht->ht_PosJump; + ht->ht_NoteNr = ht->ht_PosJumpNote; + if (ht->ht_PosNr == ht->ht_PositionNr) { + ht->ht_SongEndReached = 1; + ht->ht_PosNr = ht->ht_Restart; + } + ht->ht_PosJumpNote = 0; + ht->ht_PosJump = 0; + + ht->ht_GetNewPosition = 1; + } + } + + for (i = 0; i < ht->ht_Channels; i++) + hvl_set_audio(&ht->ht_Voices[i], ht->ht_Frequency); +} + +void hvl_mixchunk(struct hvl_tune *ht, uint32 samples, int8 *buf1, int8 *buf2, int32 bufmod) { + const int8 *src[MAX_CHANNELS]; + const int8 *rsrc[MAX_CHANNELS]; + uint32 delta[MAX_CHANNELS]; + uint32 rdelta[MAX_CHANNELS]; + int32 vol[MAX_CHANNELS]; + uint32 pos[MAX_CHANNELS]; + uint32 rpos[MAX_CHANNELS]; + uint32 cnt; + int32 panl[MAX_CHANNELS]; + int32 panr[MAX_CHANNELS]; + // uint32 vu[MAX_CHANNELS]; + int32 a = 0, b = 0, j; + uint32 i, chans, loops; + + chans = ht->ht_Channels; + for (i = 0; i < chans; i++) { + delta[i] = ht->ht_Voices[i].vc_Delta; + vol[i] = ht->ht_Voices[i].vc_VoiceVolume; + pos[i] = ht->ht_Voices[i].vc_SamplePos; + src[i] = ht->ht_Voices[i].vc_MixSource; + panl[i] = ht->ht_Voices[i].vc_PanMultLeft; + panr[i] = ht->ht_Voices[i].vc_PanMultRight; + + /* Ring Modulation */ + rdelta[i] = ht->ht_Voices[i].vc_RingDelta; + rpos[i] = ht->ht_Voices[i].vc_RingSamplePos; + rsrc[i] = ht->ht_Voices[i].vc_RingMixSource; + + // vu[i] = 0; + } + + do { + loops = samples; + for (i = 0; i < chans; i++) { + if (pos[i] >= (0x280 << 16)) + pos[i] -= 0x280 << 16; + cnt = ((0x280 << 16) - pos[i] - 1) / delta[i] + 1; + if (cnt < loops) + loops = cnt; + + if (rsrc[i]) { + if (rpos[i] >= (0x280 << 16)) + rpos[i] -= 0x280 << 16; + cnt = ((0x280 << 16) - rpos[i] - 1) / rdelta[i] + 1; + if (cnt < loops) + loops = cnt; + } + } + + samples -= loops; + + // Inner loop + do { + a = 0; + b = 0; + for (i = 0; i < chans; i++) { + if (rsrc[i]) { + /* Ring Modulation */ + j = ((src[i][pos[i] >> 16] * rsrc[i][rpos[i] >> 16]) >> 7) * vol[i]; + rpos[i] += rdelta[i]; + } else { + j = src[i][pos[i] >> 16] * vol[i]; + } + + // if( abs( j ) > vu[i] ) vu[i] = abs( j ); + + a += (j * panl[i]) >> 7; + b += (j * panr[i]) >> 7; + pos[i] += delta[i]; + } + + a = (a * ht->ht_mixgain) >> 8; + b = (b * ht->ht_mixgain) >> 8; + + if (a < -0x8000) + a = -0x8000; + if (a > 0x7fff) + a = 0x7fff; + if (b < -0x8000) + b = -0x8000; + if (b > 0x7fff) + b = 0x7fff; + + *(int16 *)buf1 = a; + *(int16 *)buf2 = b; + + loops--; + + buf1 += bufmod; + buf2 += bufmod; + } while (loops > 0); + } while (samples > 0); + + for (i = 0; i < chans; i++) { + ht->ht_Voices[i].vc_SamplePos = pos[i]; + ht->ht_Voices[i].vc_RingSamplePos = rpos[i]; + // ht->ht_Voices[i].vc_VUMeter = vu[i]; + } +} + +void hvl_DecodeFrame(struct hvl_tune *ht, int8 *buf1, int8 *buf2, int32 bufmod) { + uint32 samples, loops; + + samples = ht->ht_Frequency / 50 / ht->ht_SpeedMultiplier; + loops = ht->ht_SpeedMultiplier; + + do { + hvl_play_irq(ht); + hvl_mixchunk(ht, samples, buf1, buf2, bufmod); + buf1 += samples * bufmod; + buf2 += samples * bufmod; + loops--; + } while (loops); +} diff --git a/internal/c/parts/audio/extras/hivelytracker/hvl_replay.h b/internal/c/parts/audio/extras/hivelytracker/hvl_replay.h new file mode 100644 index 000000000..da518c11e --- /dev/null +++ b/internal/c/parts/audio/extras/hivelytracker/hvl_replay.h @@ -0,0 +1,259 @@ +//---------------------------------------------------------------------------------------------------- +// ___ ___ __ _ _ ___ ___ _ _ _ ___ _ +// / _ \| _ ) / /| | || _ \ __| /_\ _ _ __| (_)___ | __|_ _ __ _(_)_ _ ___ +// | (_) | _ \/ _ \_ _| _/ _| / _ \ || / _` | / _ \ | _|| ' \/ _` | | ' \/ -_) +// \__\_\___/\___/ |_||_| |___| /_/ \_\_,_\__,_|_\___/ |___|_||_\__, |_|_||_\___| +// |___/ +// +// QB64-PE Audio Engine powered by miniaudio (https://miniaud.io/) +// +// hvl_replay.c + hvl_replay.h is a modified AHX & HVL replayer from +// https://github.com/pete-gordon/hivelytracker (BSD 3-Clause License) +// +// Some modifications were required to make this suitable for QB64-PE. The points below lists the +// changes that were made. We may need to redo these if we need to reconstruct these files from +// newer versions of the HivelyTracker replayer code. +// +// 1. Use the replayer code in 'Replayer_Windows' +// 2. Comment out all printfs from hvl_replay.c and hvl_tables.c +// 3. hvl_tables.c and hvl_replay.c can be amalgamated like in 'hvl2wav' +// 4. hvl_tables.h and hvl_replay.h can be amalgamated iike in 'hvl2wav' +// 5. Add 'extern "C"' block around the function declarations in hvl_replay.h +// 6. Change 'typedef char int8' to 'typedef signed char int8' in hvl_replay.h +// +//----------------------------------------------------------------------------------------------------- + +#pragma once + +typedef signed char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef double float64; +typedef char TEXT; +typedef int BOOL; + +#define TRUE 1 +#define FALSE 0 + +// Woohoo! +#define MAX_CHANNELS 16 + +// Some handy constants. Thanks eightbitbubsy. +#define AMIGA_PAL_XTAL 28375160 +#define AMIGA_NTSC_XTAL 28636360 +#define AMIGA_CPU_PAL_CLK ((AMIGA_PAL_XTAL / 4)) +#define AMIGA_CPU_NTSC_CLK ((AMIGA_NTSC_XTAL / 4)) +#define AMIGA_CIA_PAL_CLK ((AMIGA_CPU_PAL_CLK / 10)) +#define AMIGA_CIA_NTSC_CLK ((AMIGA_CPU_NTSC_CLK / 10)) +#define AMIGA_PAULA_PAL_CLK ((AMIGA_CPU_PAL_CLK / 2)) +#define AMIGA_PAULA_NTSC_CLK ((AMIGA_CPU_NTSC_CLK / 2)) + +#define Period2Freq(period) ((AMIGA_PAULA_PAL_CLK * 65536.f) / (period)) + +#ifdef __cplusplus +extern "C" { +#endif + +struct hvl_envelope { + int16 aFrames, aVolume; + int16 dFrames, dVolume; + int16 sFrames; + int16 rFrames, rVolume; + int16 pad; +}; + +struct hvl_plsentry { + uint8 ple_Note; + uint8 ple_Waveform; + int16 ple_Fixed; + int8 ple_FX[2]; + int8 ple_FXParam[2]; +}; + +struct hvl_plist { + int16 pls_Speed; + int16 pls_Length; + struct hvl_plsentry *pls_Entries; +}; + +struct hvl_instrument { + TEXT ins_Name[128]; + uint8 ins_Volume; + uint8 ins_WaveLength; + uint8 ins_FilterLowerLimit; + uint8 ins_FilterUpperLimit; + uint8 ins_FilterSpeed; + uint8 ins_SquareLowerLimit; + uint8 ins_SquareUpperLimit; + uint8 ins_SquareSpeed; + uint8 ins_VibratoDelay; + uint8 ins_VibratoSpeed; + uint8 ins_VibratoDepth; + uint8 ins_HardCutRelease; + uint8 ins_HardCutReleaseFrames; + struct hvl_envelope ins_Envelope; + struct hvl_plist ins_PList; +}; + +struct hvl_position { + uint8 pos_Track[MAX_CHANNELS]; + int8 pos_Transpose[MAX_CHANNELS]; +}; + +struct hvl_step { + uint8 stp_Note; + uint8 stp_Instrument; + uint8 stp_FX; + uint8 stp_FXParam; + uint8 stp_FXb; + uint8 stp_FXbParam; +}; + +struct hvl_voice { + int16 vc_Track; + int16 vc_NextTrack; + int16 vc_Transpose; + int16 vc_NextTranspose; + int16 vc_OverrideTranspose; // 1.5 + int32 vc_ADSRVolume; + struct hvl_envelope vc_ADSR; + struct hvl_instrument *vc_Instrument; + uint32 vc_SamplePos; + uint32 vc_Delta; + uint16 vc_InstrPeriod; + uint16 vc_TrackPeriod; + uint16 vc_VibratoPeriod; + uint16 vc_WaveLength; + int16 vc_NoteMaxVolume; + uint16 vc_PerfSubVolume; + uint8 vc_NewWaveform; + uint8 vc_Waveform; + uint8 vc_PlantPeriod; + uint8 vc_VoiceVolume; + uint8 vc_PlantSquare; + uint8 vc_IgnoreSquare; + uint8 vc_FixedNote; + int16 vc_VolumeSlideUp; + int16 vc_VolumeSlideDown; + int16 vc_HardCut; + uint8 vc_HardCutRelease; + int16 vc_HardCutReleaseF; + uint8 vc_PeriodSlideOn; + int16 vc_PeriodSlideSpeed; + int16 vc_PeriodSlidePeriod; + int16 vc_PeriodSlideLimit; + int16 vc_PeriodSlideWithLimit; + int16 vc_PeriodPerfSlideSpeed; + int16 vc_PeriodPerfSlidePeriod; + uint8 vc_PeriodPerfSlideOn; + int16 vc_VibratoDelay; + int16 vc_VibratoSpeed; + int16 vc_VibratoCurrent; + int16 vc_VibratoDepth; + int16 vc_SquareOn; + int16 vc_SquareInit; + int16 vc_SquareWait; + int16 vc_SquareLowerLimit; + int16 vc_SquareUpperLimit; + int16 vc_SquarePos; + int16 vc_SquareSign; + int16 vc_SquareSlidingIn; + int16 vc_SquareReverse; + uint8 vc_FilterOn; + uint8 vc_FilterInit; + int16 vc_FilterWait; + int16 vc_FilterSpeed; + int16 vc_FilterUpperLimit; + int16 vc_FilterLowerLimit; + int16 vc_FilterPos; + int16 vc_FilterSign; + int16 vc_FilterSlidingIn; + int16 vc_IgnoreFilter; + int16 vc_PerfCurrent; + int16 vc_PerfSpeed; + int16 vc_PerfWait; + struct hvl_plist *vc_PerfList; + const int8 *vc_AudioPointer; + const int8 *vc_AudioSource; + uint8 vc_NoteDelayOn; + uint8 vc_NoteCutOn; + int16 vc_NoteDelayWait; + int16 vc_NoteCutWait; + int16 vc_AudioPeriod; + int16 vc_AudioVolume; + int32 vc_WNRandom; + const int8 *vc_MixSource; + int8 vc_SquareTempBuffer[0x80]; + int8 vc_VoiceBuffer[0x282 * 4]; + uint8 vc_VoiceNum; + uint8 vc_TrackMasterVolume; + uint8 vc_TrackOn; + int16 vc_VoicePeriod; + uint32 vc_Pan; + uint32 vc_SetPan; // New for 1.4 + uint32 vc_PanMultLeft; + uint32 vc_PanMultRight; + uint32 vc_RingSamplePos; + uint32 vc_RingDelta; + const int8 *vc_RingMixSource; + uint8 vc_RingPlantPeriod; + int16 vc_RingInstrPeriod; + int16 vc_RingBasePeriod; + int16 vc_RingAudioPeriod; + const int8 *vc_RingAudioSource; + uint8 vc_RingNewWaveform; + uint8 vc_RingWaveform; + uint8 vc_RingFixedPeriod; + int8 vc_RingVoiceBuffer[0x282 * 4]; +}; + +struct hvl_tune { + TEXT ht_Name[128]; + uint16 ht_SongNum; + uint32 ht_Frequency; + float64 ht_FreqF; + const int8 *ht_WaveformTab[MAX_CHANNELS]; + uint16 ht_Restart; + uint16 ht_PositionNr; + uint8 ht_SpeedMultiplier; + uint8 ht_TrackLength; + uint8 ht_TrackNr; + uint8 ht_InstrumentNr; + uint8 ht_SubsongNr; + uint16 ht_PosJump; + uint32 ht_PlayingTime; + int16 ht_Tempo; + int16 ht_PosNr; + uint16 ht_StepWaitFrames; + int16 ht_NoteNr; + uint16 ht_PosJumpNote; + uint8 ht_GetNewPosition; + uint8 ht_PatternBreak; + uint8 ht_SongEndReached; + uint8 ht_Stereo; + uint16 *ht_Subsongs; + uint16 ht_Channels; + struct hvl_position *ht_Positions; + struct hvl_step ht_Tracks[256][64]; + struct hvl_instrument *ht_Instruments; + struct hvl_voice ht_Voices[MAX_CHANNELS]; + int32 ht_defstereo; + int32 ht_defpanleft; + int32 ht_defpanright; + int32 ht_mixgain; + uint8 ht_Version; +}; + +void hvl_DecodeFrame(struct hvl_tune *ht, int8 *buf1, int8 *buf2, int32 bufmod); +void hvl_InitReplayer(void); +BOOL hvl_InitSubsong(struct hvl_tune *ht, uint32 nr); +struct hvl_tune *hvl_LoadTune(const TEXT *name, uint32 freq, uint32 defstereo); +struct hvl_tune *hvl_ParseTune(const uint8 *buf, uint32 buflen, uint32 freq, uint32 defstereo); +void hvl_FreeTune(struct hvl_tune *ht); + +#ifdef __cplusplus +} +#endif diff --git a/internal/c/parts/audio/extras/vtables.h b/internal/c/parts/audio/extras/vtables.h index 55c4befa8..efa5e25da 100644 --- a/internal/c/parts/audio/extras/vtables.h +++ b/internal/c/parts/audio/extras/vtables.h @@ -6,5 +6,6 @@ extern ma_decoding_backend_vtable ma_vtable_midi; extern ma_decoding_backend_vtable ma_vtable_modplay; extern ma_decoding_backend_vtable ma_vtable_radv2; +extern ma_decoding_backend_vtable ma_vtable_hively; #endif diff --git a/internal/c/parts/audio/miniaudio.h b/internal/c/parts/audio/miniaudio.h index f774f0d5f..ad3651503 100644 --- a/internal/c/parts/audio/miniaudio.h +++ b/internal/c/parts/audio/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.9 - 2022-04-20 +miniaudio - v0.11.11 - 2022-11-04 David Reid - mackron@gmail.com @@ -386,7 +386,7 @@ Sounds should be uninitialized with `ma_sound_uninit()`. Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting +`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound the be started and/or stopped at a specific time. This can be done with the following functions: @@ -627,10 +627,29 @@ You cannot use `-std=c*` compiler flags, nor `-ansi`. | | and `ma_device` APIs. This is useful if you only want to use | | | miniaudio's data conversion and/or decoding APIs. | +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | + | | also disable the following functions: | + | | | + | | ``` | + | | ma_sound_init_from_file() | + | | ma_sound_init_from_file_w() | + | | ma_sound_init_copy() | + | | ma_engine_play_sound_ex() | + | | ma_engine_play_sound() | + | | ``` | + | | | + | | The only way to initialize a `ma_sound` object is to initialize it | + | | from a data source. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | + | | because it depends on the node graph. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_ENGINE | Disables the engine API. | + +----------------------------------+--------------------------------------------------------------------+ | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | | | `ma_event` APIs. This option is useful if you only need to use | | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIsrequire threading which means the following | + | | families of APIs require threading which means the following | | | options must also be set: | | | | | | ``` | @@ -1254,6 +1273,14 @@ When streaming sounds, 2 seconds worth of audio data is stored in memory. Althou fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music tracks in games. +When loading a sound from a file path, the engine will reference count the file to prevent it from +being loaded if it's already in memory. When you uninitialize a sound, the reference count will be +decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting +system is not used for streams. The engine will use a 64-bit hash of the file name when comparing +file paths which means there's a small chance you might encounter a name collision. If this is an +issue, you'll need to use a different name for one of the colliding file paths, or just not load +from files and instead load from a data source. + When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. If you would instead rather leave the sound unattached by default, you can can specify the @@ -1429,7 +1456,7 @@ source, mainly for convenience: Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do not have any notion of a data source, anything relating to a data source is unavailable. -Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports +Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports file formats that have built-in support in miniaudio. You can extend this to support any kind of file format through the use of custom decoders. To do this you'll need to use a self-managed resource manager and configure it appropriately. See the "Resource Management" section below for @@ -1444,7 +1471,7 @@ streaming. This is supported by miniaudio via the `ma_resource_manager` API. The resource manager is mainly responsible for the following: * Loading of sound files into memory with reference counting. - * Streaming of sound data + * Streaming of sound data. When loading a sound file, the resource manager will give you back a `ma_data_source` compatible object called `ma_resource_manager_data_source`. This object can be passed into any @@ -1539,7 +1566,7 @@ need to retrieve a job using `ma_resource_manager_next_job()` and then process i ma_job job; ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); if (result != MA_SUCCESS) { - if (result == MA_NOT_DATA_AVAILABLE) { + if (result == MA_NO_DATA_AVAILABLE) { // No jobs are available. Keep going. Will only get this if the resource manager was initialized // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. continue; @@ -1578,7 +1605,7 @@ default. This can be done by setting `pVFS` member of the resource manager's con This is particularly useful in programs like games where you want to read straight from an archive rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. This is default. +use the operating system's normal file operations. To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When loading a sound you need to specify the file path and options for how the sounds should be loaded. @@ -1851,9 +1878,11 @@ A binary search tree (BST) is used for storing data buffers as it has good balan efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If -this is an issue, you should normalize your file names to upper- or lower-case before initializing -your data sources. +due to the random nature of the hash. The disadvantages are that file names are case-sensitive and +there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize +your file names to upper- or lower-case before initializing your data sources. If name collisions +become an issue, you'll need to change the name of one of the colliding names or just not use the +resource manager. When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is excluded, the file will be decoded synchronously by the calling thread. There are two @@ -1933,7 +1962,7 @@ miniaudio's routing infrastructure follows a node graph paradigm. The idea is th node whose outputs are attached to inputs of another node, thereby creating a graph. There are different types of nodes, with each node in the graph processing input data to produce output, which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs, but +the start of the graph will usually be one or more data source nodes which have no inputs and instead pull their data from a data source. At the end of the graph is an endpoint which represents the end of the chain and is where the final output is ultimately extracted from. @@ -1959,7 +1988,7 @@ splitter node. It's at this point that the two data sources are mixed. After mix performs it's processing routine and produces two outputs which is simply a duplication of the input stream. One output is attached to a low pass filter, whereas the other output is attached to a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input but, they'll be mixed. +since they're both connected to the same input bus, they'll be mixed. Each input bus must be configured to accept the same number of channels, but the number of channels used by input buses can be different to the number of channels for output buses in which case @@ -2074,7 +2103,7 @@ pointer to the processing function and the number of input and output buses. Exa static ma_node_vtable my_custom_node_vtable = { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. 2, // 2 input buses. 1, // 1 output bus. @@ -2204,7 +2233,7 @@ called `ma_splitter_node`. This takes has 1 input bus and splits the stream into You can use it like this: ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut); + ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); ma_splitter_node splitterNode; result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); @@ -3514,7 +3543,12 @@ producer thread. 15. Backends ============ -The following backends are supported by miniaudio. +The following backends are supported by miniaudio. These are listed in order of default priority. +When no backend is specified when initializing a context or device, miniaudio will attempt to use +each of these backends in the order listed in the table below. + +Note that backends that are not usable by the build target will not be included in the build. For +example, ALSA, which is specific to Linux, will not be included in the Windows build. +-------------+-----------------------+--------------------------------------------------------+ | Name | Enum Name | Supported Operating Systems | @@ -3523,12 +3557,12 @@ The following backends are supported by miniaudio. | DirectSound | ma_backend_dsound | Windows XP+ | | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | | sndio | ma_backend_sndio | OpenBSD | | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | | OSS | ma_backend_oss | FreeBSD | + | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | ALSA | ma_backend_alsa | Linux | + | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | | AAudio | ma_backend_aaudio | Android 8+ | | OpenSL ES | ma_backend_opensl | Android (API level 16+) | | Web Audio | ma_backend_webaudio | Web (via Emscripten) | @@ -3640,7 +3674,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 9 +#define MA_VERSION_REVISION 11 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3737,7 +3771,7 @@ typedef ma_uint16 wchar_t; /* Platform/backend detection. */ #ifdef _WIN32 #define MA_WIN32 - #if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)) + #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) #define MA_WIN32_UWP #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) #define MA_WIN32_GDK @@ -3890,7 +3924,7 @@ implications. Where supported by the compiler, alignment will be used, but other architecture does not require it, it will simply leave it unaligned. This is the case with old versions of Visual Studio, which I've confirmed with at least VC6. */ -#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) #include #define MA_ATOMIC(alignment, type) alignas(alignment) type #else @@ -4123,7 +4157,7 @@ typedef enum { ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */ + ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular } ma_channel_mix_mode; @@ -4748,7 +4782,7 @@ typedef struct { ma_delay_config config; ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */ + ma_uint32 bufferSizeInFrames; float* pBuffer; } ma_delay; @@ -5264,6 +5298,7 @@ typedef struct const ma_channel* pChannelMapIn; const ma_channel* pChannelMapOut; ma_channel_mix_mode mixingMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ } ma_channel_converter_config; @@ -5316,6 +5351,7 @@ typedef struct ma_channel* pChannelMapOut; ma_dither_mode ditherMode; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ ma_bool32 allowDynamicSampleRate; ma_resampler_config resampling; @@ -5496,6 +5532,28 @@ The channel map buffer must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); +/* +Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The +index of the channel is output to `pChannelIndex`. + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); + +/* +Generates a string representing the given channel map. + +This is for printing and debugging purposes, not serialization/deserialization. + +Returns the length of the string, not including the null terminator. +*/ +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); + +/* +Retrieves a human readable version of a channel position. +*/ +MA_API const char* ma_channel_position_to_string(ma_channel channel); + /************************************************************************************************************************************************************ @@ -6103,10 +6161,6 @@ This section contains the APIs for device playback and capture. Here is where yo #define MA_SUPPORT_PULSEAUDIO #define MA_SUPPORT_JACK #endif - #if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL - #endif #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ #endif @@ -6117,6 +6171,10 @@ This section contains the APIs for device playback and capture. Here is where yo #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ #endif #endif +#if defined(MA_ANDROID) + #define MA_SUPPORT_AAUDIO + #define MA_SUPPORT_OPENSL +#endif #if defined(MA_APPLE) #define MA_SUPPORT_COREAUDIO #endif @@ -6412,7 +6470,7 @@ typedef enum /* iOS/tvOS/watchOS session categories. */ typedef enum { - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */ + ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ ma_ios_session_category_none, /* Leave the session category unchanged. */ ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ @@ -6457,36 +6515,44 @@ typedef enum ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ } ma_opensl_recording_preset; +/* WASAPI audio thread priority characteristics. */ +typedef enum +{ + ma_wasapi_usage_default = 0, + ma_wasapi_usage_games, + ma_wasapi_usage_pro_audio, +} ma_wasapi_usage; + /* AAudio usage types. */ typedef enum { ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ + ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ + ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ + ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ + ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ + ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ + ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ + ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ + ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ + ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ } ma_aaudio_usage; /* AAudio content types. */ typedef enum { ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */ - ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */ + ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ } ma_aaudio_content_type; /* AAudio input presets. */ @@ -6495,9 +6561,9 @@ typedef enum ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ + ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ } ma_aaudio_input_preset; @@ -6584,6 +6650,7 @@ struct ma_device_config ma_uint32 channels; ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ ma_share_mode shareMode; } playback; struct @@ -6593,15 +6660,19 @@ struct ma_device_config ma_uint32 channels; ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ ma_share_mode shareMode; } capture; struct { - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ + ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ + ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ } wasapi; struct { @@ -6714,7 +6785,7 @@ sample rate. For the channel map, the default should be used when `ma_channel_ma `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format` +sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses @@ -6844,6 +6915,11 @@ struct ma_context ma_uint32 commandIndex; ma_uint32 commandCount; ma_context_command__wasapi commands[4]; + ma_handle hAvrt; + ma_proc AvSetMmThreadCharacteristicsW; + ma_proc AvRevertMmThreadcharacteristics; + ma_handle hMMDevapi; + ma_proc ActivateAudioInterfaceAsync; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -7278,6 +7354,7 @@ struct ma_device ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; ma_data_converter converter; void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ ma_uint32 intermediaryBufferCap; @@ -7303,6 +7380,7 @@ struct ma_device ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; ma_data_converter converter; void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ ma_uint32 intermediaryBufferCap; @@ -7338,6 +7416,8 @@ struct ma_device ma_uint32 mappedBufferPlaybackLen; MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_uint32 loopbackProcessID; + ma_bool8 loopbackProcessExclude; ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noHardwareOffloading; @@ -7345,6 +7425,8 @@ struct ma_device ma_bool8 allowPlaybackAutoStreamRouting; ma_bool8 isDetachedPlayback; ma_bool8 isDetachedCapture; + ma_wasapi_usage usage; + void *hAvrtHandle; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -8146,9 +8228,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame - count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the - backend requests, which could be anything. + Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a + consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with + whatever the backend requests, which could be anything. dataCallback The callback to fire whenever data is ready to be delivered to or from the device. @@ -10441,6 +10523,7 @@ typedef struct { ma_node_config nodeConfig; ma_uint32 channels; + ma_uint32 outputBusCount; } ma_splitter_node_config; MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); @@ -10665,6 +10748,7 @@ MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); #endif /* MA_NO_NODE_GRAPH */ +/* SECTION: miniaudio_engine.h */ /************************************************************************************************************************************************************ Engine @@ -10706,6 +10790,7 @@ typedef struct ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_mono_expansion_mode monoExpansionMode; ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ @@ -10720,6 +10805,7 @@ typedef struct ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_mono_expansion_mode monoExpansionMode; ma_fader fader; ma_linear_resampler resampler; /* For pitch shift. */ ma_spatializer spatializer; @@ -10753,6 +10839,7 @@ typedef struct ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ ma_uint64 rangeBegInPCMFrames; @@ -10763,7 +10850,8 @@ typedef struct ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } ma_sound_config; -MA_API ma_sound_config ma_sound_config_init(void); +MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ struct ma_sound { @@ -10795,8 +10883,8 @@ struct ma_sound_inlined typedef ma_sound_config ma_sound_group_config; typedef ma_sound ma_sound_group; -MA_API ma_sound_group_config ma_sound_group_config_init(void); - +MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ typedef struct { @@ -10807,6 +10895,7 @@ typedef struct ma_context* pContext; ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device_notification_proc notificationCallback; #endif ma_log* pLog; /* When set to NULL, will use the context's log. */ ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ @@ -11016,6 +11105,7 @@ MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); #endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.h */ #ifdef __cplusplus } @@ -11801,12 +11891,19 @@ Standard Library Stuff #endif #endif -#ifndef MA_ZERO_MEMORY +static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) +{ #ifdef MA_WIN32 -#define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz)) + ZeroMemory(p, sz); #else -#define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) + if (sz > 0) { + memset(p, 0, sz); + } #endif +} + +#ifndef MA_ZERO_MEMORY +#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) #endif #ifndef MA_COPY_MEMORY @@ -12227,12 +12324,15 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { + size_t sz; + char* dst; + if (src == NULL) { return NULL; } - size_t sz = strlen(src)+1; - char* dst = (char*)ma_malloc(sz, pAllocationCallbacks); + sz = strlen(src)+1; + dst = (char*)ma_malloc(sz, pAllocationCallbacks); if (dst == NULL) { return NULL; } @@ -13187,7 +13287,7 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat return MA_INVALID_ARGS; } - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) { ma_result result; int length; @@ -17052,6 +17152,10 @@ DEVICE I/O #include /* For mach_absolute_time() */ #endif +#ifdef MA_ANDROID + #include +#endif + #ifdef MA_POSIX #include #include @@ -17184,13 +17288,43 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) #endif case ma_backend_aaudio: #if defined(MA_HAS_AAUDIO) - return MA_TRUE; + #if defined(MA_ANDROID) + { + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + if (atoi(sdkVersion) >= 26) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + } + #else + return MA_FALSE; + #endif #else return MA_FALSE; #endif case ma_backend_opensl: #if defined(MA_HAS_OPENSL) - return MA_TRUE; + #if defined(MA_ANDROID) + { + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + if (atoi(sdkVersion) >= 9) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + } + #else + return MA_TRUE; + #endif #else return MA_FALSE; #endif @@ -17584,17 +17718,18 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); #ifdef _WIN32 -#ifdef MA_WIN32_DESKTOP - handle = (ma_handle)LoadLibraryA(filename); -#else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } -#endif + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(WINAPI_FAMILY) || (defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif #else handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif @@ -17772,6 +17907,11 @@ static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* { MA_ASSERT(pDevice != NULL); + /* Don't read more data from the client if we're in the process of stopping. */ + if (ma_device_get_state(pDevice) == ma_device_state_stopping) { + return; + } + if (pDevice->noFixedSizedCallback) { /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); @@ -18059,6 +18199,9 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; totalClientFramesProcessed += clientFramesProcessedThisIteration; + /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ + (void)totalClientFramesProcessed; + if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { break; /* We're done. */ } @@ -19064,11 +19207,9 @@ static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap /* Converts a Win32-style channel mask to a miniaudio channel map. */ static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) { - if (channels == 1 && dwChannelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && dwChannelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; + /* If the channel mask is set to 0, just assume a default Win32 channel map. */ + if (dwChannelMask == 0) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); } else { if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { pChannelMap[0] = MA_CHANNEL_MONO; @@ -19116,7 +19257,7 @@ static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) } if (pWFEX->Samples.wValidBitsPerSample == 24) { if (pWFEX->Format.wBitsPerSample == 32) { - /*return ma_format_s24_32;*/ + return ma_format_s32; } if (pWFEX->Format.wBitsPerSample == 24) { return ma_format_s24; @@ -19693,8 +19834,16 @@ static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } +#if defined(MA_WIN32_UWP) +/* mmdevapi Functions */ +typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(LPCWSTR deviceInterfacePath, const IID* riid, PROPVARIANT *activationParams, ma_IActivateAudioInterfaceCompletionHandler *completionHandler, ma_IActivateAudioInterfaceAsyncOperation **activationOperation); +#endif + +/* Avrt Functions */ +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsW)(LPCWSTR TaskName, LPDWORD TaskIndex); +typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); + #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -#include typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; typedef struct @@ -20045,6 +20194,18 @@ static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { }; #endif /* MA_WIN32_DESKTOP */ +static LPCWSTR ma_to_usage_string__wasapi(ma_wasapi_usage usage) +{ + switch (usage) { + case ma_wasapi_usage_default: return NULL; + case ma_wasapi_usage_games: return L"Games"; + case ma_wasapi_usage_pro_audio: return L"Pro Audio"; + default: break; + } + + return NULL; +} + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) typedef ma_IMMDevice ma_WASAPIDeviceInterface; #else @@ -20369,6 +20530,10 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); } } + #else + { + (void)pMMDevice; /* Unused. */ + } #endif return MA_SUCCESS; @@ -20473,7 +20638,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); } @@ -20485,7 +20650,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); return ma_result_from_HRESULT(hr); } @@ -20594,7 +20759,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte if (SUCCEEDED(hr)) { hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); result = ma_result_from_HRESULT(hr); goto done; } @@ -20634,7 +20799,7 @@ done: return result; } -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) +static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) { ma_result result; HRESULT hr; @@ -20648,7 +20813,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex return result; } - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient); + hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); if (FAILED(hr)) { return ma_result_from_HRESULT(hr); } @@ -20656,7 +20821,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex return MA_SUCCESS; } #else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) +static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) { ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; ma_completion_handler_uwp completionHandler; @@ -20671,45 +20836,43 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m MA_ASSERT(ppAudioClient != NULL); if (pDeviceID != NULL) { - MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid)); + iidStr = (LPOLESTR)pDeviceID->wasapi; } else { - if (deviceType == ma_device_type_playback) { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } else { + if (deviceType == ma_device_type_capture) { iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; + } else { + iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; } - } -#if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); -#else - hr = StringFromIID(&iid, &iidStr); -#endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory."); - return ma_result_from_HRESULT(hr); + #if defined(__cplusplus) + hr = StringFromIID(iid, &iidStr); + #else + hr = StringFromIID(&iid, &iidStr); + #endif + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); + return ma_result_from_HRESULT(hr); + } } result = ma_completion_handler_uwp_init(&completionHandler); if (result != MA_SUCCESS) { ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync()."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); return result; } -#if defined(__cplusplus) - hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#else - hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#endif + hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); if (FAILED(hr)) { ma_completion_handler_uwp_uninit(&completionHandler); ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); return ma_result_from_HRESULT(hr); } - ma_CoTaskMemFree(pContext, iidStr); + if (pDeviceID == NULL) { + ma_CoTaskMemFree(pContext, iidStr); + } /* Wait for the async operation for finish. */ ma_completion_handler_uwp_wait(&completionHandler); @@ -20719,14 +20882,14 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); } /* Here is where we grab the IAudioClient interface. */ hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); return ma_result_from_HRESULT(hr); } @@ -20740,13 +20903,106 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m } #endif -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ +typedef enum { -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); -#else - return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); + MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, + MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK +} MA_AUDIOCLIENT_ACTIVATION_TYPE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ +typedef enum +{ + MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, + MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE +} MA_PROCESS_LOOPBACK_MODE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ +typedef struct +{ + DWORD TargetProcessId; + MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; +} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif #endif +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ +typedef struct +{ + MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; + union + { + MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; + }; +} MA_AUDIOCLIENT_ACTIVATION_PARAMS; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" + +static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) +{ + ma_result result; + ma_bool32 usingProcessLoopback = MA_FALSE; + MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; + PROPVARIANT activationParams; + PROPVARIANT* pActivationParams = NULL; + ma_device_id virtualDeviceID; + + /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ + if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { + usingProcessLoopback = MA_TRUE; + } + + if (usingProcessLoopback) { + MA_ZERO_OBJECT(&audioclientActivationParams); + audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; + audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; + audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; + + ma_PropVariantInit(&activationParams); + activationParams.vt = VT_BLOB; + activationParams.blob.cbSize = sizeof(audioclientActivationParams); + activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; + pActivationParams = &activationParams; + + /* When requesting a specific device ID we need to use a special device ID. */ + MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ + pDeviceID = &virtualDeviceID; + } else { + pActivationParams = NULL; /* No activation parameters required. */ + } + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#else + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#endif + + /* + If loopback mode was requested with a process ID and initialization failed, it could be because it's + trying to run on an older version of Windows where it's not supported. We need to let the caller + know about this with a log message. + */ + if (result != MA_SUCCESS) { + if (usingProcessLoopback) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); + } + } + + return result; } @@ -20839,7 +21095,7 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL); + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); if (result != MA_SUCCESS) { return result; } @@ -20918,6 +21174,8 @@ typedef struct ma_bool32 noAutoConvertSRC; ma_bool32 noDefaultQualitySRC; ma_bool32 noHardwareOffloading; + ma_uint32 loopbackProcessID; + ma_bool32 loopbackProcessExclude; /* Output. */ ma_IAudioClient* pAudioClient; @@ -20971,7 +21229,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; } - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface); + result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); if (result != MA_SUCCESS) { goto done; } @@ -21352,7 +21610,7 @@ done: } if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s", errorMsg); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); } return result; @@ -21429,6 +21687,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; + data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); if (result != MA_SUCCESS) { return result; @@ -21492,9 +21752,12 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + pDevice->wasapi.usage = pConfig->wasapi.usage; + pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; /* Exclusive mode is not allowed with loopback. */ if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { @@ -21515,6 +21778,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); if (result != MA_SUCCESS) { @@ -21579,6 +21844,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); if (result != MA_SUCCESS) { @@ -21785,6 +22052,14 @@ static ma_result ma_device_start__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); + if (pDevice->pContext->wasapi.hAvrt) { + LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + if (pTaskName) { + DWORD idx = 0; + pDevice->wasapi.hAvrtHandle = ((MA_PFN_AvSetMmThreadCharacteristicsW)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsW)(pTaskName, &idx); + } + } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { @@ -21815,6 +22090,11 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); + if (pDevice->wasapi.hAvrtHandle) { + ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); + pDevice->wasapi.hAvrtHandle = NULL; + } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { @@ -21873,7 +22153,7 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) } prevFramesAvaialablePlayback = framesAvailablePlayback; - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); + WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime * 1000); ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ } } @@ -21953,50 +22233,100 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui } else { /* We don't have any cached data pointer, so grab another one. */ HRESULT hr; - DWORD flags; + DWORD flags = 0; /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == S_OK) { /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + + /* + There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every + call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially + work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution + would be to figure out why the flag is always getting reported. + */ + #if defined(MA_DEBUG_OUTPUT) + { + if (flags != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); + + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); + } + } + } + #endif /* Overrun detection. */ if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { /* Glitched. Probably due to an overrun. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); /* - If we got an overrun it probably means we're straddling the end of the buffer. In order to prevent - a never-ending sequence of glitches we're going to recover by completely clearing out the capture - buffer. + If we got an overrun it probably means we're straddling the end of the buffer. In normal capture + mode this is the fault of the client application because they're responsible for ensuring data is + processed fast enough. In duplex mode, however, the processing of audio is tied to the playback + device, so this can possibly be the result of a timing de-sync. + + In capture mode we're not going to do any kind of recovery because the real fix is for the client + application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers + to prevent a never-ending sequence of glitches due to straddling the end of the buffer. */ - { - ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */ + if (pDevice->type == ma_device_type_duplex) { + /* + Experiment: + + If we empty out the *entire* buffer we may end up putting ourselves into an underrun position + which isn't really any better than the overrun we're probably in right now. Instead we'll just + empty out about half. + */ ma_uint32 i; + ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); + ma_uint32 iterationCount = periodCount / 2; + if ((periodCount % 2) > 0) { + iterationCount += 1; + } for (i = 0; i < iterationCount; i += 1) { hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %d.\n", hr); break; } + flags = 0; hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { + /* + The buffer has been completely emptied or an error occurred. In this case we'll need + to reset the state of the mapped buffer which will trigger the next iteration to get + a fresh buffer from WASAPI. + */ + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); + } + } + + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %d.\n", hr); + } + break; } } - } - /* We should not have a valid buffer at this point so make sure everything is empty. */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } else { - /* The data is clean. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); + /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + } } } @@ -22009,9 +22339,16 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui microphone isn't delivering data for whatever reason. In this case we'll just abort the read and return whatever we were able to get. The other situations is loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. + through the speakers. */ - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + + /* Experiment: Use a shorter timeout for loopback mode. */ + DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; + if (pDevice->type == ma_device_type_loopback) { + timeoutInMilliseconds = 10; + } + + if (WaitForSingleObject(pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { if (pDevice->type == ma_device_type_loopback) { continue; /* Keep waiting in loopback mode. */ } else { @@ -22169,6 +22506,20 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext) ma_context_post_command__wasapi(pContext, &cmd); ma_thread_wait(&pContext->wasapi.commandThread); + if (pContext->wasapi.hAvrt) { + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + + #if defined(MA_WIN32_UWP) + { + if (pContext->wasapi.hMMDevapi) { + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + pContext->wasapi.hMMDevapi = NULL; + } + } + #endif + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); @@ -22274,6 +22625,41 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_mutex_uninit(&pContext->wasapi.commandLock); return result; } + + #if defined(MA_WIN32_UWP) + { + /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ + pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll"); + if (pContext->wasapi.hMMDevapi) { + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ + } + } else { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ + } + } + #endif + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); + if (pContext->wasapi.hAvrt) { + pContext->wasapi.AvSetMmThreadCharacteristicsW = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsW"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContext->wasapi.AvSetMmThreadCharacteristicsW || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsW = NULL; + pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + } } @@ -26462,7 +26848,11 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ { - ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + ma_snd_pcm_chmap_t* pChmap = NULL; + if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { + pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + } + if (pChmap != NULL) { ma_uint32 iChannel; @@ -28169,6 +28559,14 @@ static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_ return; } + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + pInfoOut = (ma_pa_sink_info*)pUserData; MA_ASSERT(pInfoOut != NULL); @@ -28185,6 +28583,14 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m return; } + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + pInfoOut = (ma_pa_source_info*)pUserData; MA_ASSERT(pInfoOut != NULL); @@ -28912,6 +29318,11 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; + /* Use the requested sample rate if one was specified. */ + if (pDescriptorCapture->sampleRate != 0) { + ss.rate = pDescriptorCapture->sampleRate; + } + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; @@ -29048,6 +29459,11 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; + /* Use the requested sample rate if one was specified. */ + if (pDescriptorPlayback->sampleRate != 0) { + ss.rate = pDescriptorPlayback->sampleRate; + } + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; @@ -30401,7 +30817,7 @@ structure with three variables and is used to identify which property you are ge which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different. +kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property @@ -30705,6 +31121,14 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* return MA_SUCCESS; } +#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ + (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain +#else +/* kAudioObjectPropertyElementMaster is deprecated. */ +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster +#endif + static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ { AudioObjectPropertyAddress propAddressDevices; @@ -30722,7 +31146,7 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 propAddressDevices.mSelector = kAudioHardwarePropertyDevices; propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = kAudioObjectPropertyElementMaster; + propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); if (status != noErr) { @@ -30756,7 +31180,7 @@ static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, Aud propAddress.mSelector = kAudioDevicePropertyDeviceUID; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(*pUID); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); @@ -30798,7 +31222,7 @@ static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID obj propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(deviceName); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); @@ -30827,7 +31251,7 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; propAddress.mScope = scope; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -30882,7 +31306,7 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au */ propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -30920,7 +31344,7 @@ static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioOb propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -31010,7 +31434,7 @@ static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObje propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -31132,7 +31556,7 @@ static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pC propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(bufferSizeRange); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); @@ -31170,7 +31594,7 @@ static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); @@ -31199,7 +31623,7 @@ static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_t *pDeviceObjectID = 0; propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster; + propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; if (deviceType == ma_device_type_playback) { propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; } else { @@ -32272,7 +32696,7 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); @@ -32302,7 +32726,7 @@ static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pCont if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); @@ -32753,7 +33177,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); if (status != noErr) { @@ -35974,22 +36398,22 @@ static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) { switch (usage) { - case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; + case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; + case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; + case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; + case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; + case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; + case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; + case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; + case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; + case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; + case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; + case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; + case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; + case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; + case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; + case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; + case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; default: break; } @@ -35999,10 +36423,10 @@ static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) { switch (contentType) { - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; + case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; + case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; + case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; default: break; } @@ -36014,9 +36438,9 @@ static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_prese switch (inputPreset) { case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; + case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; default: break; } @@ -38376,7 +38800,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d } /* Send data to the client from our intermediary buffer. */ - ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); + _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); totalFramesProcessed += framesToProcess; } @@ -38422,7 +38846,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d } /* Read data from the client into our intermediary buffer. */ - ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); + _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ if (outputSilence) { @@ -38566,8 +38990,17 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_webaudio); - /* Nothing needs to be done here. */ - (void)pContext; + (void)pContext; /* Unused. */ + + /* Remove the global miniaudio object from window if there are no more references to it. */ + EM_ASM({ + if (typeof(window.miniaudio) !== 'undefined') { + window.miniaudio.referenceCount--; + if (window.miniaudio.referenceCount === 0) { + delete window.miniaudio; + } + } + }); return MA_SUCCESS; } @@ -38582,12 +39015,14 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex /* Here is where our global JavaScript object is initialized. */ resultFromJS = EM_ASM_INT({ - if ((window.AudioContext || window.webkitAudioContext) === undefined) { + if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { return 0; /* Web Audio not supported. */ } if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = {}; + window.miniaudio = { + referenceCount: 0 + }; miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ miniaudio.track_device = function(device) { @@ -38651,6 +39086,8 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex }); } + window.miniaudio.referenceCount++; + return 1; }, 0); /* Must pass in a dummy argument for C99 compatibility. */ @@ -38761,20 +39198,21 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { /* Converting from internal device format to client format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + converterConfig.formatIn = pDevice->capture.internalFormat; + converterConfig.channelsIn = pDevice->capture.internalChannels; + converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; + converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; + converterConfig.formatOut = pDevice->capture.format; + converterConfig.channelsOut = pDevice->capture.channels; + converterConfig.sampleRateOut = pDevice->sampleRate; + converterConfig.pChannelMapOut = pDevice->capture.channelMap; + converterConfig.channelMixMode = pDevice->capture.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { @@ -38790,20 +39228,21 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* Converting from client format to device format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + converterConfig.formatIn = pDevice->playback.format; + converterConfig.channelsIn = pDevice->playback.channels; + converterConfig.sampleRateIn = pDevice->sampleRate; + converterConfig.pChannelMapIn = pDevice->playback.channelMap; + converterConfig.formatOut = pDevice->playback.internalFormat; + converterConfig.channelsOut = pDevice->playback.internalChannels; + converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; + converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; + converterConfig.channelMixMode = pDevice->playback.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { @@ -39111,6 +39550,8 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); +#else + (void)pContext; /* Unused. */ #endif ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); @@ -39502,13 +39943,17 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC #ifdef MA_HAS_AAUDIO case ma_backend_aaudio: { - pContext->callbacks.onContextInit = ma_context_init__aaudio; + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__aaudio; + } } break; #endif #ifdef MA_HAS_OPENSL case ma_backend_opensl: { - pContext->callbacks.onContextInit = ma_context_init__opensl; + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__opensl; + } } break; #endif #ifdef MA_HAS_WEBAUDIO @@ -39717,7 +40162,12 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p /* Capture devices. */ if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */ + *ppCaptureDeviceInfos = pContext->pDeviceInfos; + /* Capture devices come after playback devices. */ + if (pContext->playbackDeviceInfoCount > 0) { + /* Conditional, because NULL+0 is undefined behavior. */ + *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; + } } if (pCaptureDeviceCount != NULL) { *pCaptureDeviceCount = pContext->captureDeviceInfoCount; @@ -39867,13 +40317,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC pDevice->capture.channels = pConfig->capture.channels; ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; + pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; pDevice->playback.shareMode = pConfig->playback.shareMode; pDevice->playback.format = pConfig->playback.format; pDevice->playback.channels = pConfig->playback.channels; ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - + pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; result = ma_mutex_init(&pDevice->startStopLock); if (result != MA_SUCCESS) { @@ -40135,9 +40586,9 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC /* Log device information. */ { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); + ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); @@ -40150,6 +40601,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; @@ -40166,6 +40625,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } } } @@ -40216,6 +40683,33 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen result = MA_NO_BACKEND; for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + /* + This is a hack for iOS. If the context config is null, there's a good chance the + `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this + case, set the session category based on the device type. + */ + #if defined(MA_APPLE_MOBILE) + ma_context_config contextConfig; + + if (pContextConfig == NULL) { + contextConfig = ma_context_config_init(); + switch (pConfig->deviceType) { + case ma_device_type_duplex: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; + } break; + case ma_device_type_capture: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; + } break; + case ma_device_type_playback: + default: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; + } break; + } + + pContextConfig = &contextConfig; + } + #endif + result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); if (result == MA_SUCCESS) { result = ma_device_init(pContext, pConfig, pDevice); @@ -40491,6 +40985,15 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_event_wait(&pDevice->stopEvent); result = MA_SUCCESS; } + + /* + This is a safety measure to ensure the internal buffer has been cleared so any leftover + does not get played the next time the device starts. Ideally this should be drained by + the backend first. + */ + pDevice->playback.intermediaryBufferLen = 0; + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; } ma_mutex_unlock(&pDevice->startStopLock); @@ -49158,7 +49661,7 @@ static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_c } /* LPF */ - pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes; + pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); { ma_result result; size_t lpfHeapSizeInBytes; @@ -50084,6 +50587,7 @@ MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_ result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); return result; } @@ -50388,6 +50892,23 @@ static ma_int32 ma_channel_converter_float_to_fixed(float x) return (ma_int32)(x * (1< 0); + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { + spatialChannelCount++; + } + } + + return spatialChannelCount; +} + static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) { int i; @@ -51117,6 +51638,26 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert /* We now need to fill out our weights table. This is determined by the mixing mode. */ + + /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (channelPosIn == channelPosOut) { + float weight = 1; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + switch (pConverter->mixingMode) { case ma_channel_mix_mode_custom_weights: @@ -51140,19 +51681,10 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert case ma_channel_mix_mode_simple: { - /* In simple mode, excess channels need to be silenced or dropped. */ - ma_uint32 iChannel; - for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannel][iChannel] == 0) { - pConverter->weights.f32[iChannel][iChannel] = 1; - } - } else { - if (pConverter->weights.s16[iChannel][iChannel] == 0) { - pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1); - } - } - } + /* + In simple mode, only set weights for channels that have exactly matching types, leave the rest at + zero. The 1:1 mappings have already been covered before this switch statement. + */ } break; case ma_channel_mix_mode_rectangular: @@ -51160,12 +51692,12 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert { /* Unmapped input channels. */ for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (ma_is_spatial_channel_position(channelPosOut)) { float weight = 0; @@ -51191,12 +51723,12 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert /* Unmapped output channels. */ for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (ma_is_spatial_channel_position(channelPosOut)) { if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { float weight = 0; @@ -51219,6 +51751,32 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert } } } + + /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ + if (pConfig->calculateLFEFromSpatialChannels) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { + ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); + ma_uint32 iChannelOutLFE; + + if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { + const float weightForLFE = 1.0f / spatialChannelCount; + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + if (ma_is_spatial_channel_position(channelPosIn)) { + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); + } + } + } + } + } + } + } } break; } } @@ -51720,6 +52278,7 @@ static ma_channel_converter_config ma_channel_converter_config_init_from_data_co channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); channelConverterConfig.ppWeights = pConfig->ppChannelWeights; + channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; return channelConverterConfig; } @@ -53643,18 +54202,128 @@ MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint3 } MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) +{ + return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); +} + +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) { ma_uint32 iChannel; + if (pChannelIndex != NULL) { + *pChannelIndex = (ma_uint32)-1; + } + for (iChannel = 0; iChannel < channels; ++iChannel) { if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { + if (pChannelIndex != NULL) { + *pChannelIndex = iChannel; + } + return MA_TRUE; } } + /* Getting here means the channel position was not found. */ return MA_FALSE; } +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) +{ + size_t len; + ma_uint32 iChannel; + + len = 0; + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); + size_t channelStrLen = strlen(pChannelStr); + + /* Append the string if necessary. */ + if (pBufferOut != NULL && bufferCap > len + channelStrLen) { + MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); + } + len += channelStrLen; + + /* Append a space if it's not the last item. */ + if (iChannel+1 < channels) { + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = ' '; + } + len += 1; + } + } + + /* Null terminate. Don't increment the length here. */ + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = '\0'; + } + + return len; +} + +MA_API const char* ma_channel_position_to_string(ma_channel channel) +{ + switch (channel) + { + case MA_CHANNEL_NONE : return "CHANNEL_NONE"; + case MA_CHANNEL_MONO : return "CHANNEL_MONO"; + case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; + case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; + case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; + case MA_CHANNEL_LFE : return "CHANNEL_LFE"; + case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; + case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; + case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; + case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; + case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; + case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; + case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; + case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; + case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; + case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; + case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; + case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; + case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; + case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; + case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; + case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; + case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; + case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; + case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; + case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; + case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; + case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; + case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; + case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; + case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; + case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; + case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; + case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; + case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; + case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; + case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; + case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; + case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; + case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; + case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; + case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; + case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; + case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; + case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; + case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; + case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; + case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; + case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; + case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; + case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; + default: break; + } + + return "UNKNOWN"; +} + /************************************************************************************************************************************************************** @@ -55067,7 +55736,8 @@ MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSourc return result; } - *pCursor = cursorInPCMFrames / (float)sampleRate; + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; return MA_SUCCESS; } @@ -55094,7 +55764,8 @@ MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSourc return result; } - *pLength = lengthInPCMFrames / (float)sampleRate; + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; return MA_SUCCESS; } @@ -57199,7 +57870,7 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 6 +#define DRWAV_VERSION_REVISION 7 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include typedef signed char drwav_int8; @@ -57734,7 +58405,7 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 38 +#define DRFLAC_VERSION_REVISION 39 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; @@ -57863,14 +58534,12 @@ typedef enum drflac_seek_origin_start, drflac_seek_origin_current } drflac_seek_origin; -#pragma pack(2) typedef struct { drflac_uint64 firstPCMFrame; drflac_uint64 flacFrameOffset; drflac_uint16 pcmFrameCount; } drflac_seekpoint; -#pragma pack() typedef struct { drflac_uint16 minBlockSizeInPCMFrames; @@ -58057,14 +58726,12 @@ typedef struct drflac_uint32 countRemaining; const char* pRunningData; } drflac_cuesheet_track_iterator; -#pragma pack(4) typedef struct { drflac_uint64 offset; drflac_uint8 index; drflac_uint8 reserved[3]; } drflac_cuesheet_track_index; -#pragma pack() typedef struct { drflac_uint64 offset; @@ -58095,7 +58762,7 @@ extern "C" { #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 33 +#define DRMP3_VERSION_REVISION 34 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include typedef signed char drmp3_int8; @@ -58277,7 +58944,6 @@ typedef struct typedef struct { drmp3dec decoder; - drmp3dec_frame_info frameInfo; drmp3_uint32 channels; drmp3_uint32 sampleRate; drmp3_read_proc onRead; @@ -60095,6 +60761,7 @@ static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_back mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } @@ -64078,10 +64745,15 @@ static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) { + ma_uint32 block; + + /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ + MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); + if (ma_is_little_endian()) { - return blocks[i]; + return block; } else { - return ma_swap_endian_uint32(blocks[i]); + return ma_swap_endian_uint32(block); } } @@ -68425,11 +69097,15 @@ static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* p static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) { + MA_ASSERT(pInputBus != NULL); + ma_spinlock_lock(&pInputBus->lock); } static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) { + MA_ASSERT(pInputBus != NULL); + ma_spinlock_unlock(&pInputBus->lock); } @@ -69980,8 +70656,9 @@ MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) ma_splitter_node_config config; MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + config.outputBusCount = 2; return config; } @@ -70012,9 +70689,9 @@ static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** pp static ma_node_vtable g_ma_splitter_node_vtable = { ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 2, /* 2 output buses. */ + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ 0 }; @@ -70023,7 +70700,8 @@ MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_split ma_result result; ma_node_config baseConfig; ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[2]; + ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; + ma_uint32 iOutputBus; if (pSplitterNode == NULL) { return MA_INVALID_ARGS; @@ -70035,15 +70713,21 @@ MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_split return MA_INVALID_ARGS; } + if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; /* Too many output buses. */ + } + /* Splitters require the same number of channels between inputs and outputs. */ pInputChannels[0] = pConfig->channels; - pOutputChannels[0] = pConfig->channels; - pOutputChannels[1] = pConfig->channels; + for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { + pOutputChannels[iOutputBus] = pConfig->channels; + } baseConfig = pConfig->nodeConfig; baseConfig.vtable = &g_ma_splitter_node_vtable; baseConfig.pInputChannels = pInputChannels; baseConfig.pOutputChannels = pOutputChannels; + baseConfig.outputBusCount = pConfig->outputBusCount; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); if (result != MA_SUCCESS) { @@ -70938,6 +71622,7 @@ MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) #endif /* MA_NO_NODE_GRAPH */ +/* SECTION: miniaudio_engine.c */ #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) /************************************************************************************************************************************************************** @@ -70955,6 +71640,7 @@ MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_e config.type = type; config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; + config.monoExpansionMode = pEngine->monoExpansionMode; return config; } @@ -71147,7 +71833,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut); } else { /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->pEngine->monoExpansionMode); + ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); } } @@ -71496,6 +72182,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->pEngine = pConfig->pEngine; pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; pEngineNode->pitch = 1; pEngineNode->oldPitch = 1; pEngineNode->oldDopplerPitch = 1; @@ -71629,10 +72316,22 @@ MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocati MA_API ma_sound_config ma_sound_config_init(void) +{ + return ma_sound_config_init_2(NULL); +} + +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) { ma_sound_config config; MA_ZERO_OBJECT(&config); + + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + config.rangeEndInPCMFrames = ~((ma_uint64)0); config.loopPointEndInPCMFrames = ~((ma_uint64)0); @@ -71640,11 +72339,22 @@ MA_API ma_sound_config ma_sound_config_init(void) } MA_API ma_sound_group_config ma_sound_group_config_init(void) +{ + return ma_sound_group_config_init_2(NULL); +} + +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) { ma_sound_group_config config; MA_ZERO_OBJECT(&config); + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + return config; } @@ -71746,6 +72456,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng deviceConfig.sampleRate = engineConfig.sampleRate; deviceConfig.dataCallback = ma_engine_data_callback_internal; deviceConfig.pUserData = pEngine; + deviceConfig.notificationCallback = engineConfig.notificationCallback; deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ @@ -72512,8 +73223,9 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con source that provides this information upfront. */ engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ if (pConfig->pDataSource != NULL) { @@ -72540,7 +73252,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con /* If no attachment is specified, attach the sound straight to the endpoint. */ if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that do not. */ + /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); } @@ -72643,7 +73355,7 @@ done: MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init(); + ma_sound_config config = ma_sound_config_init_2(pEngine); config.pFilePath = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; @@ -72653,7 +73365,7 @@ MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePa MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init(); + ma_sound_config config = ma_sound_config_init_2(pEngine); config.pFilePathW = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; @@ -72695,10 +73407,11 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin return result; } - config = ma_sound_config_init(); + config = ma_sound_config_init_2(pEngine); config.pDataSource = pSound->pResourceManagerDataSource; config.flags = flags; config.pInitialAttachment = pGroup; + config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); if (result != MA_SUCCESS) { @@ -72714,7 +73427,7 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init(); + ma_sound_config config = ma_sound_config_init_2(pEngine); config.pDataSource = pDataSource; config.flags = flags; config.pInitialAttachment = pGroup; @@ -73458,7 +74171,7 @@ MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) { - ma_sound_group_config config = ma_sound_group_config_init(); + ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); config.flags = flags; config.pInitialAttachment = pParentGroup; return ma_sound_group_init_ex(pEngine, &config, pGroup); @@ -73760,6 +74473,7 @@ MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGr return ma_sound_get_time_in_pcm_frames(pGroup); } #endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.c */ @@ -73778,13 +74492,18 @@ code below please report the bug to the respective repository for the relevant p /* dr_wav_c begin */ #ifndef dr_wav_c #define dr_wav_c +#ifdef __MRC__ +#pragma options opt off +#endif #include #include #include #ifndef DR_WAV_NO_STDIO #include +#ifndef DR_WAV_NO_WCHAR #include #endif +#endif #ifndef DRWAV_ASSERT #include #define DRWAV_ASSERT(expression) assert(expression) @@ -76491,6 +77210,7 @@ DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, con #define DRWAV_HAS_WFOPEN #endif #endif +#ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { @@ -76515,6 +77235,10 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, (void)pAllocationCallbacks; } #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -76547,12 +77271,14 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, *ppFile = fopen(pFilePathMB, pOpenModeMB); drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRWAV_ERROR; } #endif return DRWAV_SUCCESS; } +#endif DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); @@ -76593,6 +77319,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drw } return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); @@ -76605,6 +77332,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename } return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } +#endif DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -76613,6 +77341,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* fi } return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -76621,6 +77350,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_ } return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } +#endif DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav_bool32 result; @@ -76644,6 +77374,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const ch } return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -76652,6 +77383,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const } return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } +#endif DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); @@ -76667,6 +77399,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, } return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); @@ -76683,6 +77416,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #endif +#endif DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { drwav* pWav = (drwav*)pUserData; @@ -78701,6 +79435,7 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filen } return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -78753,6 +79488,7 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif +#endif DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -78866,6 +79602,9 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) a[2] == b[2] && a[3] == b[3]; } +#ifdef __MRC__ +#pragma options opt reset +#endif #endif /* dr_wav_c end */ #endif /* DRWAV_IMPLEMENTATION */ @@ -79022,9 +79761,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { #if defined(DRFLAC_SUPPORT_SSE41) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__) + #if defined(__SSE4_1__) || defined(__AVX__) return DRFLAC_TRUE; #else #if defined(DRFLAC_NO_CPUID) @@ -79086,18 +79823,21 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ - parm [ax] \ - modify [ax]; + parm [ax] \ + value [ax] \ + modify nomemory; #pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - modify [eax]; + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; #pragma aux _watcom_bswap64 = \ "bswap eax" \ "bswap edx" \ "xchg eax,edx" \ parm [eax edx] \ - modify [eax edx]; + value [eax edx] \ + modify nomemory; #endif #ifndef DRFLAC_ASSERT #include @@ -79189,6 +79929,9 @@ typedef drflac_int32 drflac_result; #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) { @@ -79962,6 +80705,10 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) #if defined(__WATCOMC__) && defined(__386__) #define DRFLAC_IMPLEMENT_CLZ_WATCOM #endif +#ifdef __MRC__ +#include +#define DRFLAC_IMPLEMENT_CLZ_MRC +#endif static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) { drflac_uint32 n; @@ -79996,6 +80743,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) { #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) return DRFLAC_TRUE; +#elif defined(__MRC__) + return DRFLAC_TRUE; #else #ifdef DRFLAC_HAS_LZCNT_INTRINSIC return drflac__gIsLZCNTSupported; @@ -80076,6 +80825,13 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) #endif #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux drflac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else #pragma aux drflac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ @@ -80083,6 +80839,7 @@ static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); value [eax] \ modify exact [eax] nomemory; #endif +#endif static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT @@ -80093,8 +80850,12 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC return drflac__clz_msvc(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return drflac__clz_watcom_lzcnt(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); #else return drflac__clz_software(x); #endif @@ -81261,7 +82022,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ int32x2_t shift64; uint32x4_t one128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); @@ -81401,8 +82162,11 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ int32x4_t riceParam128; int64x1_t shift64; uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); @@ -81458,9 +82222,6 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { - int64x2_t prediction128; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || @@ -82751,7 +83512,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks) +static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) { drflac_uint64 runningFilePos = 42; drflac_uint64 seektablePos = 0; @@ -82798,26 +83559,28 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { + drflac_uint32 seekpointCount; drflac_uint32 iSeekpoint; void* pRawData; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; - for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } @@ -82882,8 +83645,10 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d void* pRawData; const char* pRunningData; const char* pRunningDataEnd; + size_t bufferSize; drflac_uint8 iTrack; drflac_uint8 iIndex; + void* pTrackData; pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; @@ -82900,29 +83665,61 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = pRunningData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - drflac_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < 36) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; pRunningData += 1; - indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index); - if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData; - pRunningData += sizeof(drflac_cuesheet_track_index); - pTrack->offset = drflac__be2host_64(pTrack->offset); + metadata.data.cuesheet.pTrackData = NULL; + { + const char* pRunningDataSaved = pRunningData; + bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + drflac_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningData += 35; + indexCount = pRunningData[0]; + pRunningData += 1; + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); + indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningData += indexPointSize; } + pRunningData = pRunningDataSaved; + } + { + char* pRunningTrackData; + pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningTrackData = (char*)pTrackData; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(drflac_cuesheet_track_index); + pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + } + } + metadata.data.cuesheet.pTrackData = pTrackData; } - onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + onMeta(pUserDataMD, &metadata); + drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; } } break; case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: @@ -82952,13 +83749,13 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; @@ -83020,9 +83817,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d break; } } - *pSeektablePos = seektablePos; - *pSeektableSize = seektableSize; - *pFirstFramePos = runningFilePos; + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) @@ -83773,11 +84570,11 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac drflac_uint32 wholeSIMDVectorCountPerChannel; drflac_uint32 decodedSamplesAllocationSize; #ifndef DR_FLAC_NO_OGG - drflac_oggbs oggbs; + drflac_oggbs* pOggbs = NULL; #endif drflac_uint64 firstFramePos; drflac_uint64 seektablePos; - drflac_uint32 seektableSize; + drflac_uint32 seekpointCount; drflac_allocation_callbacks allocationCallbacks; drflac* pFlac; drflac__init_cpu_caps(); @@ -83807,22 +84604,24 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { allocationSize += sizeof(drflac_oggbs); - } - DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs)); - if (init.container == drflac_container_ogg) { - oggbs.onRead = onRead; - oggbs.onSeek = onSeek; - oggbs.pUserData = pUserData; - oggbs.currentBytePos = init.oggFirstBytePos; - oggbs.firstBytePos = init.oggFirstBytePos; - oggbs.serialNumber = init.oggSerial; - oggbs.bosPageHeader = init.oggBosHeader; - oggbs.bytesRemainingInPage = 0; + pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; + } + DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; } #endif - firstFramePos = 42; - seektablePos = 0; - seektableSize = 0; + firstFramePos = 42; + seektablePos = 0; + seekpointCount = 0; if (init.hasMetadataBlocks) { drflac_read_proc onReadOverride = onRead; drflac_seek_proc onSeekOverride = onSeek; @@ -83831,16 +84630,22 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac if (init.container == drflac_container_ogg) { onReadOverride = drflac__on_read_ogg; onSeekOverride = drflac__on_seek_ogg; - pUserDataOverride = (void*)&oggbs; + pUserDataOverride = (void*)pOggbs; } #endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) { + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif return NULL; } - allocationSize += seektableSize; + allocationSize += seekpointCount * sizeof(drflac_seekpoint); } pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif return NULL; } drflac__init_from_info(pFlac, &init); @@ -83848,8 +84653,10 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { - drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); - DRFLAC_COPY_MEMORY(pInternalOggbs, &oggbs, sizeof(oggbs)); + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); + DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; @@ -83867,21 +84674,22 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #endif { if (seektablePos != 0) { - pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); + pFlac->seekpointCount = seekpointCount; pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); DRFLAC_ASSERT(pFlac->bs.onRead != NULL); if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { - drflac_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + drflac_uint32 iSeekpoint; + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; } if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); @@ -83917,7 +84725,9 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } #ifndef DR_FLAC_NO_STDIO #include +#ifndef DR_FLAC_NO_WCHAR #include +#endif #include static drflac_result drflac_result_from_errno(int e) { @@ -84361,6 +85171,7 @@ static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const ch #define DRFLAC_HAS_WFOPEN #endif #endif +#ifndef DR_FLAC_NO_WCHAR static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { @@ -84385,6 +85196,10 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons (void)pAllocationCallbacks; } #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -84417,12 +85232,14 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons *ppFile = fopen(pFilePathMB, pOpenModeMB); drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRFLAC_ERROR; } #endif return DRFLAC_SUCCESS; } +#endif static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); @@ -84446,6 +85263,7 @@ DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocati } return pFlac; } +#ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -84460,6 +85278,7 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all } return pFlac; } +#endif DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -84474,6 +85293,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_ } return pFlac; } +#ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -84489,6 +85309,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr return pFlac; } #endif +#endif static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; @@ -87201,7 +88022,7 @@ DRMP3_API const char* drmp3_version_string(void) #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) #define DR_MP3_ONLY_SIMD #endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) #if defined(_MSC_VER) #include #endif @@ -87847,7 +88668,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) @@ -88363,7 +89184,7 @@ static void drmp3d_DCT_II(float *grbuf, int n) #if DRMP3_HAVE_SSE #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else -#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { @@ -88379,7 +89200,7 @@ static void drmp3d_DCT_II(float *grbuf, int n) DRMP3_VSAVE2(3, t[3][7]); } else { -#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v) +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); @@ -88879,7 +89700,7 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num #endif #define DRMP3_MIN_DATA_CHUNK_SIZE 16384 #ifndef DRMP3_DATA_CHUNK_SIZE -#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4 +#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) #endif #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) @@ -89711,6 +90532,10 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const (void)pAllocationCallbacks; } #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -89743,6 +90568,7 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const *ppFile = fopen(pFilePathMB, pOpenModeMB); drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRMP3_ERROR; } diff --git a/internal/c/parts/audio/miniaudio_impl.cpp b/internal/c/parts/audio/miniaudio_impl.cpp index d94f6111f..515bdf136 100644 --- a/internal/c/parts/audio/miniaudio_impl.cpp +++ b/internal/c/parts/audio/miniaudio_impl.cpp @@ -12,51 +12,41 @@ // //----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// HEADER FILES -//----------------------------------------------------------------------------------------------------- // Enable Ogg Vorbis decoding #define STB_VORBIS_HEADER_ONLY #include "extras/stb_vorbis.c" -// PulseAudio has serious stuttering issues in ChromeOS Linux (Crostini) and possibly others -// This may be due to this - https://github.com/mackron/miniaudio/issues/427 -// And https://wiki.archlinux.org/title/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling -// We'll have to look at this closely later. If this is fixed, then remove this define from here & audio.cpp -#define MA_NO_PULSEAUDIO // The main miniaudio header #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" // The stb_vorbis implementation must come after the implementation of miniaudio #undef STB_VORBIS_HEADER_ONLY #include "extras/stb_vorbis.c" - #include "extras/vtables.h" -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// GLOBAL VARIABLES -//----------------------------------------------------------------------------------------------------- // Add custom backend (format) vtables here // The order in the array defines the order of priority // The vtables will be passed in to the resource manager config +// ma_vtable_modplay should be the last one because libxmp supports 15-channel MODs which does not have any signatures +// This can lead to incorrect detection static ma_decoding_backend_vtable *maCustomBackendVTables[] = { &ma_vtable_radv2, + &ma_vtable_hively, &ma_vtable_midi, &ma_vtable_modplay, }; -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- -// FUNCTIONS -//----------------------------------------------------------------------------------------------------- -/// -/// This simply attaches the format decode VTables array to ma_resource_manager_config -/// -/// Pointer to a miniaudio resource manager config object. This cannot be NULL +/// @brief This simply attaches the format decode VTables array to ma_resource_manager_config +/// @param maDecoderConfig Pointer to a miniaudio resource manager config object. This cannot be NULL void AudioEngineAttachCustomBackendVTables(ma_resource_manager_config *maResourceManagerConfig) { // Attach the VTable maResourceManagerConfig->ppCustomDecodingBackendVTables = maCustomBackendVTables; maResourceManagerConfig->customDecodingBackendCount = ma_countof(maCustomBackendVTables); } -//----------------------------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------------------------- + +/// @brief This simply attaches the format decode VTables array to ma_decoder_config +/// @param maDecoderConfig Pointer to a miniaudio decoder config object. This cannot be NULL +void AudioEngineAttachCustomBackendVTables(ma_decoder_config *maDecoderConfig) { + // Attach the VTable + maDecoderConfig->ppCustomBackendVTables = maCustomBackendVTables; + maDecoderConfig->customBackendCount = ma_countof(maCustomBackendVTables); +} diff --git a/licenses/license_hivelytracker.txt b/licenses/license_hivelytracker.txt new file mode 100644 index 000000000..f2b6dc426 --- /dev/null +++ b/licenses/license_hivelytracker.txt @@ -0,0 +1,32 @@ +-------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/source/subs_functions/subs_functions.bas b/source/subs_functions/subs_functions.bas index 783208be4..d8d05767e 100644 --- a/source/subs_functions/subs_functions.bas +++ b/source/subs_functions/subs_functions.bas @@ -2058,6 +2058,19 @@ id.arg = MKL$(ULONGTYPE - ISPOINTER) id.hr_syntax = "_SNDCLOSE handle&" regid +' a740g: Feature request #28 +clearid +id.n = qb64prefix$ + "SndNew" ' Name in CaMeL case +id.Dependency = DEPENDENCY_AUDIO_OUT ' QB64-PE library dependency +id.subfunc = 1 ' 1 = function, 2 = sub +id.callname = "func__sndnew" ' C/C++ function name +id.args = 3 ' number of arguments "passed" +id.arg = MKL$(LONGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) + MKL$(LONGTYPE - ISPOINTER) ' arguments & types +id.ret = LONGTYPE - ISPOINTER ' return type for functions +id.hr_syntax = "_SNDNEW(frames&, channels&, bits&)" ' syntax help +regid +' a740g: Feature request #28 + clearid id.n = "Input" id.musthave = "$" diff --git a/source/subs_functions/syntax_highlighter_list.bas b/source/subs_functions/syntax_highlighter_list.bas index 91af1031c..0a7754603 100644 --- a/source/subs_functions/syntax_highlighter_list.bas +++ b/source/subs_functions/syntax_highlighter_list.bas @@ -6,5 +6,5 @@ listOfKeywords$ = listOfKeywords$ + "_GLPOPATTRIB@_GLPOPCLIENTATTRIB@_GLPOPMATRI listOfKeywords$ = listOfKeywords$ + "_SOFTWARE@_SQUAREPIXELS@_STRETCH@_ALLOWFULLSCREEN@_ALL@_ECHO@_INSTRREV@_TRIM$@_ACCEPTFILEDROP@_FINISHDROP@_TOTALDROPPEDFILES@_DROPPEDFILE@_DROPPEDFILE$@_SHR@_SHL@_ROR@_ROL@" ' a740g: added ROR & ROL listOfKeywords$ = listOfKeywords$ + "_DEFLATE$@_INFLATE$@_READBIT@_RESETBIT@_SETBIT@_TOGGLEBIT@$ASSERTS@_ASSERT@_CAPSLOCK@_NUMLOCK@_SCROLLLOCK@_TOGGLE@_CONSOLEFONT@_CONSOLECURSOR@_CONSOLEINPUT@_CINP@$NOPREFIX@$COLOR@$DEBUG@_ENVIRONCOUNT@$UNSTABLE@$MIDISOUNDFONT@" listOfKeywords$ = listOfKeywords$ + "_NOTIFYPOPUP@_MESSAGEBOX@_INPUTBOX$@_SELECTFOLDERDIALOG$@_COLORCHOOSERDIALOG@_OPENFILEDIALOG$@_SAVEFILEDIALOG$@" ' a740g: added common dialog keywords -listOfKeywords$ = listOfKeywords$ + "_STATUSCODE@" ' a740g: added common dialog keywords +listOfKeywords$ = listOfKeywords$ + "_STATUSCODE@_SNDNEW@" diff --git a/tests/compile_tests/audio_mem_test/newsound_test.bas b/tests/compile_tests/audio_mem_test/newsound_test.bas new file mode 100644 index 000000000..5da0a2744 --- /dev/null +++ b/tests/compile_tests/audio_mem_test/newsound_test.bas @@ -0,0 +1,76 @@ +$Console:Only +Option _Explicit +Option _ExplicitArray + +Dim h As Long: h = _SndNew(1024, 2, 32) +Print "Handle ="; h +Dim m As _MEM: m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +h = _SndNew(0, 2, 32) +Print "Handle ="; h +m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +h = _SndNew(1024, 0, 32) +Print "Handle ="; h +m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +h = _SndNew(1024, 1, 0) +Print "Handle ="; h +m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +h = _SndNew(1024, -10, 16) +Print "Handle ="; h +m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +h = _SndNew(1024, 1, -32) +Print "Handle ="; h +m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +h = _SndNew(-1024, 1, 16) +Print "Handle ="; h +m = _MemSound(h, 0) +Print "Size ="; m.SIZE +Print "Type ="; m.TYPE +Print "Element Size ="; m.ELEMENTSIZE +Print "Sound ="; m.SOUND +Print +_SndClose h + +System + diff --git a/tests/compile_tests/audio_mem_test/newsound_test.output b/tests/compile_tests/audio_mem_test/newsound_test.output new file mode 100644 index 000000000..d2a1475cd --- /dev/null +++ b/tests/compile_tests/audio_mem_test/newsound_test.output @@ -0,0 +1,42 @@ +Handle = 1 +Size = 8192 +Type = 260 +Element Size = 8 +Sound = 1 + +Handle = 0 +Size = 0 +Type = 0 +Element Size = 0 +Sound = 0 + +Handle = 0 +Size = 0 +Type = 0 +Element Size = 0 +Sound = 0 + +Handle = 0 +Size = 0 +Type = 0 +Element Size = 0 +Sound = 0 + +Handle = 0 +Size = 0 +Type = 0 +Element Size = 0 +Sound = 0 + +Handle = 0 +Size = 0 +Type = 0 +Element Size = 0 +Sound = 0 + +Handle = 0 +Size = 0 +Type = 0 +Element Size = 0 +Sound = 0 + diff --git a/tests/compile_tests/audio_mem_test/sndopen_mem_test.bas b/tests/compile_tests/audio_mem_test/sndopen_mem_test.bas new file mode 100644 index 000000000..45bf12c3d --- /dev/null +++ b/tests/compile_tests/audio_mem_test/sndopen_mem_test.bas @@ -0,0 +1,1311 @@ +$Console:Only +Option _Explicit +Option _ExplicitArray + +Dim buffer As String: buffer = LoadTestData +Print "Size ="; Len(buffer) + + +Dim h As Long: h = _SndOpen(buffer, "memory") +Print "Handle ="; h +Print "Length ="; _SndLen(h) + +System + +' Modified RhoSigma MakeDATA loader +Function LoadTestData$ + '--- option _explicit requirements --- + Dim numL&, numB&, rawdata$, stroffs&, i&, dat& + '--- write DATAs --- + Restore Test + Read numL&, numB& + rawdata$ = Space$((numL& * 4) + numB&) + stroffs& = 1 + For i& = 1 To numL& + Read dat& + Mid$(rawdata$, stroffs&, 4) = MKL$(dat&) + stroffs& = stroffs& + 4 + Next i& + If numB& > 0 Then + For i& = 1 To numB& + Read dat& + Mid$(rawdata$, stroffs&, 1) = Chr$(dat&) + stroffs& = stroffs& + 1 + Next i& + End If + + '--- set result --- + LoadTestData$ = _Inflate$(rawdata$) + Exit Function + + '--- DATAs representing the contents of file test.mp3 + '--------------------------------------------------------------------- + Test: + Data 10104,29 + Data &H9C2C9C78,&HFFD53C77,&H15AFC717,&HEF7B5D59,&H5590854D,&H196C9BD9,&H0BB25995,&HECAD7199 + Data &H9BD957BD,&H45C42B28,&H23DEF656,&HB9B23D91,&HB9FEFBDC,&H1FF7BFBE,&HF0F171F7,&HCF8F5E78 + Data &H7AF39F79,&HF379F79D,&H985605B9,&H0810215B,&H0BF5E1A6,&HB20E7C04,&H66B3F799,&HDF130486 + Data &HFC4A27BA,&HD0910A01,&H80BAAB5A,&H30E004A5,&H229F65FC,&H3B6887BC,&HC9C7F15C,&H8702CC3A + Data &H897F7281,&H9E1FA131,&HF8FE58BC,&H66FB6C03,&HFFF9E74B,&H1127C6A3,&HEE73D4A4,&HE336F8B3 + Data &HC521FF00,&H13BEBF84,&HDE1932B9,&H82DD5595,&HF9DC98E6,&H218C53ED,&H1DB10D4B,&H674E24EA + Data &HED3EB2A5,&H2D283694,&HCAC9CDB6,&H341B0873,&H69482E51,&H75BD60BA,&HFB9C337B,&H60EEE144 + Data &HE5CB13F5,&H66BFF207,&H7A083F9E,&H0F714B44,&HD3DFC25C,&H49B773D9,&HA61D07C8,&H8149ED9C + Data &H67B227A3,&H163555D6,&HAE5D23F2,&H3CFAB4B3,&H2361F824,&H8E68F904,&H3ADCDF1B,&H95463C0B + Data &H2600BE13,&H7A6751AD,&HCDA50FEC,&H59246869,&H62E77EEB,&H947F5059,&H64197D63,&H12CE3A15 + Data &H80700502,&H717E575E,&HB8747239,&H5A5BF430,&H3E3E00DE,&H43760A3F,&H871F6B0F,&HDFBE3DBA + Data &H8D7E7171,&H03A11391,&H9E5AD35B,&H39AF56F8,&H264E45FE,&H39A2CDC6,&HB1D0E42C,&H0243EB3B + Data &H39795090,&H52BF7BF5,&H5A6A0DFF,&H2D8AD2F9,&HC89DF7DF,&H7DF80AEE,&HA7E2C9E5,&HB121762F + Data &H8CE4F6C8,&H77D26A80,&HEA53DC80,&H32D04F2A,&HA72655B8,&H80C49905,&HB373BF7D,&H59B258D9 + Data &H2F8670B4,&HEB85C63D,&H950FAAB9,&HA97497DF,&HCA23A2D7,&H8DF72DA0,&H3814CFBC,&HA0F87345 + Data &H73B6305E,&H61BAA0B1,&H8021E8A1,&H1E82291C,&H390408D7,&H4CBD6239,&HD601361C,&H700638F4 + Data &H4B7DCC90,&H4D1BE76B,&H4EA170AD,&H97E540F5,&H7D475B13,&H112424C6,&H2F3DD528,&H38175042 + Data &HF0E4EF46,&H89F09D14,&H3FDD53BE,&H592ACB0E,&HD935C3EA,&HFD27C243,&H3EB146CC,&H57E007C7 + Data &HC4F239A0,&HCD0A80CB,&HC20863E1,&H96D8F82B,&HDE98EE73,&HFDF3936C,&HE7FD57D7,&HA674C34C + Data &H2CA63BEF,&H4A03DDA6,&HDE2ABB98,&H5D97255A,&H3DDC4CC6,&HAE783BB1,&H4C2A03DD,&H61C5AB0F + Data &H4366CC27,&HC39C2D6D,&H9A18D4FE,&HE3780D93,&H188C19FE,&HD69321FC,&H51F5F6B4,&H2F7F9CDE + Data &HF85DAB1D,&HA84B1CD1,&H481EADE0,&H84DBC75F,&HA8525664,&HDA990755,&H362004F3,&HF5F55BA6 + Data &H8A531799,&HAAA3F866,&H2DA5A364,&H52ABFAFC,&HD478889A,&H90A3003E,&HE11D55A2,&H167960F9 + Data &H463C4B55,&H88BEE096,&H8553A7B7,&HF7BBEB6D,&HBF761D9F,&HEC65CAE9,&HDD4BACCE,&HE0F3BDD7 + Data &H8D6DBE78,&HE9992C97,&HC73463C5,&HE0FBF487,&H69630C77,&HADD47EC8,&H88CE24E0,&HD7643DB9 + Data &HD47FCEC7,&HE2C1E05E,&H5A6DF621,&H288E688F,&HB3B1B19C,&HCD0E25D7,&H5250B443,&HD5DC24C2 + Data &HC3863FAE,&H52BA4E64,&HB06EDB0B,&HD5C85915,&HFC5658F7,&H4B14A661,&HCA66196C,&H0338F8EB + Data &H63A43CFF,&HD5E9CD09,&H6D5CBFE4,&HD143922A,&HC096289C,&HA9A6F33F,&H23709062,&HF72784D3 + Data &HDF2308E4,&HF03F1E7B,&H60486A19,&H8E013178,&HEF1FD446,&H93835659,&H5FEBA2ED,&H44EC6007 + Data &HCE70A9A4,&HAB66A2BD,&H42B1CBDB,&H737AA35C,&H2105EC7A,&HF47AFC77,&HAD5510AD,&H720C1565 + Data &H3F478963,&HE899257B,&HAF7A21D9,&H12EFA4AC,&H3BC9DDA4,&H282C7346,&H2EF9029A,&H6D30C5DA + Data &H25640954,&H8444D4A7,&H2A9B27B8,&HBA805DA7,&HA0D5E9B8,&HEC02B6CF,&HA1EE58E4,&H89160002 + Data &H60FB49B2,&H67A456BE,&H7F2C9FEC,&H2315B943,&HCEE7FCCE,&H8BA6BB72,&HBF0B4142,&HDD0EA514 + Data &HF2F335AE,&HAFBB84A4,&HBE5587A6,&H63F4EC06,&HFCBD1F43,&HA8C91CC3,&HBCE687A2,&H9185A526 + Data &HD7CB8B9D,&H49DBC37D,&HA5FF1914,&HFF4BFE5B,&H92CA2D49,&H0904F44E,&HE2DD83A5,&H00185DDA + Data &H2012BAC0,&H686D71B5,&H30451DE1,&HF5EAAA80,&H2F9690E8,&H651AD52E,&H67AD55F9,&H5F23B2F8 + Data &H21E8A7EC,&H515C8324,&HC7E6A447,&HDDC2389B,&H97AF5B24,&H544B95D1,&H7DF07465,&HC73460F6 + Data &H60D59483,&HA3ABF21E,&HA548CC06,&H9C4DCC0E,&H8148BA5A,&H700E78FD,&HA405C660,&HB1B316D3 + Data &H74A8E079,&HB234827E,&HC44A4944,&HBF27F19F,&H5F270D24,&H509C89A4,&H18C9065C,&H863634B6 + Data &H79940130,&H351AE9B8,&HEBE15C82,&HE224B812,&H263D8180,&H1225C21E,&HEE60D47E,&HE04B39A0 + Data &H4E139A16,&H87E88392,&H0CD4768C,&HCDEFD44F,&H9F43CB47,&H90AA395A,&HF363CB07,&HF4D48B2E + Data &H55DF13D4,&HFCEA3133,&HD8D629A1,&H86D98410,&HCFD37F7F,&H53DE5ADF,&H2A09A951,&H2C7C9FC4 + Data &HE281ECFA,&H6696A8D3,&HAA78B341,&HACE20D3F,&H7EAC4F15,&H85F07EFA,&H3A14F23D,&H0F8153D1 + Data &H3516A7F5,&HA0533060,&H9D499F39,&H3285C0BB,&H55FB6637,&HF0C0C7A6,&H0B9221D2,&H1F8D4316 + Data &HFE732ED8,&HAB049445,&H5239665D,&H88DF26EA,&HB651B075,&H4430F3FF,&H28B4D348,&H327A1610 + Data &H841261A8,&HC7C39013,&H442728E1,&H1A71383D,&H1E1894D6,&H194DFC05,&H5357C897,&H03C5913B + Data &H0D4D3649,&HAE3A2471,&H254E6819,&HAADAD1E3,&H84168C69,&HCF0579EF,&HAD79B48B,&HE6781E37 + Data &HF205F445,&H72C44D42,&H780A2111,&HD0D69801,&H31B80AC7,&H61584C8C,&H6D62ADD0,&H7E37DA0A + Data &HFAAE7839,&HA4B171F4,&HD8DC4B0E,&H821D939F,&H1EAD5F5F,&H3C2A1505,&H761CD16D,&H3B778842 + Data &HDF154A51,&H66B41559,&H2471A706,&H682BA652,&H65F5344E,&H2745D708,&H4F46A637,&HA7A3E50A + Data &H8C8EB297,&HFED11D01,&H63ADA3AA,&HD6BCB958,&H49AC5B49,&H4B8271B9,&HA58AACDC,&H38FAFF98 + Data &HDB68A544,&HFE88861E,&H7B61D028,&H0012D1C3,&H4075484E,&H5BA3B3C9,&H2FE6E7FC,&H823FDD5F + Data &HF58FF981,&HD5D42760,&HD72B496C,&HCEACD914,&HB46B329D,&H847CE683,&HA1F2E036,&H939DE5A8 + Data &H702607DC,&HBF48869E,&H4896F885,&H904A3FF3,&H8BF28A9E,&H32E26B8A,&H7E3116E5,&H8FB3056F + Data &HDD6739D4,&H1C8423DD,&H49061FAE,&H7D6508A1,&HB310E7CD,&H4A5901C5,&HF6BB3600,&H957E0D2E + Data &HA333C568,&HA16B1C9F,&H4CB8A3FB,&HFC7BCFDD,&H6D289BD4,&H2FD82BB7,&HF9CD1732,&HA703AA44 + Data &H35074943,&HFEF07986,&H13F2C492,&H5711089D,&H583B8679,&HAD93F4A0,&H3B177210,&H1FE0499E + Data &H57488C75,&H77590C7D,&H3DD85916,&H74E0C4A0,&H23970A91,&H8D62F787,&H8139F40F,&H183E4B73 + Data &H8E8C1550,&HDA16096E,&H06A8D947,&H2053E7EE,&H21C8B6B3,&H566EA812,&H0974953B,&HE7346299 + Data &HEFA9390B,&H4772B9FA,&HF3C1384D,&H4326DB35,&HF1C91B65,&HE507BD28,&H2439759B,&H5773A88A + Data &HA729D339,&HE401AC24,&H95C8F9CF,&H1818608D,&HC9CBBDE9,&HF5CC68E3,&HC5D22D25,&H86B61C74 + Data &HED50CAA9,&H2FFE3B94,&HFB2DA33F,&H1E19A127,&H2A8E761E,&H308D261F,&HCCB1F38E,&H25065BCF + Data &HA80D32EF,&HE4EF9CD1,&H10ADBDB2,&HB3EF8ECF,&HA3CFFC12,&HE6363026,&HCE1E5955,&H3D8BA79E + Data &H99934AE2,&H561D5E34,&HAD1D17BB,&H22F54569,&HE1CCEB21,&H9B72E924,&HB8427C1C,&H53854C37 + Data &HC8BAFEAC,&HFE42A7B0,&H2C46ABF8,&HB86E5FB5,&H36626C02,&H6CBB5F93,&HCC35A7AA,&H6168D0FC + Data &H66090BC7,&H9DBFD1D8,&HE6825C6E,&HFAE593B4,&H35C75D8E,&HCA76C2E4,&HA3526881,&H62247A2E + Data &H16E432C4,&H565A4D31,&HEBD8A502,&H450117D7,&H186AE2AC,&HD14D4409,&HBEE2FB3A,&H23AB36EF + Data &H6BB3A64C,&HB8A4943B,&H4247E125,&HABF775E8,&H532089AE,&HC19ACDFF,&HCE1DC352,&HC90C5B7C + Data &H257312C4,&HF8B030D7,&HE68D80A6,&HADF00A0C,&H9AA5534D,&H6ED53E41,&H3445E6AB,&H37C3E1C4 + Data &H90B2AF54,&H0900A2F2,&H0E6A428D,&H02087F76,&H4A4034A0,&H4E150103,&HF7A6552F,&H7EF3B0A1 + Data &HEBBD86B2,&HDD9D9E34,&HABC78F91,&HE2141E13,&HE9B5AD97,&H2F48E733,&H7E4B8D49,&H2284EFE0 + Data &H4EFA7267,&H97A3B405,&H21502C36,&HCE6892B4,&HC457E81B,&H1400C606,&H73CF2E72,&HEF52C942 + Data &H7B768006,&H1275C18F,&HA8C3BCBB,&H588492F7,&H6B316C0A,&H56C2172E,&H27417E73,&HF5B466BB + Data &HF2799995,&HEF8D4B5D,&H2FAB8436,&H5510D314,&H70F87DAB,&H81A99BA9,&HE9427508,&H6EDA2067 + Data &H4D233123,&H3F596677,&HC10BFAAA,&HCB30B7C0,&HCD714AEB,&H6D6523F1,&H51E5ED59,&HAF97FEFD + Data &HA896C25A,&HC5F93912,&H67CD999A,&H2C7DCB77,&H64EB56E9,&H7D3BFE1F,&HD0C96666,&HF8C60AD5 + Data &HA93D644F,&H5B2AF3B9,&HDF846A7F,&H7ED2F024,&H586E77BE,&HE7ACB9B4,&H84774974,&H6B7C7F55 + Data &HEBA15C36,&H51BD0001,&H8A10F000,&H49B7B6DD,&H12AAD260,&HBEB3E7DB,&HA4F1A3C9,&H2858DDA3 + Data &HCAC6A0DB,&HB305499D,&HED3639D1,&H35ECE2C5,&HF0265D2D,&HBBA331C9,&H87FC32A2,&HA1BE537B + Data &HEF17F694,&HBB310D89,&HB070AAAB,&HCB1A66F5,&H5E3AA960,&H5C02BD33,&HC2F35B85,&HEEFBA7C0 + Data &H68381500,&HB1303E9B,&H587BAA8B,&H1A6962FA,&H545A861B,&H66539661,&H872FEC0F,&HA43939A3 + Data &H28473AE9,&H3CBAF91B,&H5864CFF6,&H3277A6AA,&H3DD97015,&H8927C4BF,&HC861FAE4,&H9F88BF66 + Data &HCBCE8066,&H566CB2A8,&HB9FEB131,&HCEDBF82F,&HA0255272,&HB05457CA,&HC5DA6C8C,&HAFB2E5E5 + Data &H4C01C124,&H996FED8E,&H51C852FB,&HFBDD615D,&H8A8F2A22,&H274351F7,&HE56A6326,&H834A8370 + Data &H342385C9,&H7A592BE7,&H14D03AD7,&HAA350CC3,&H2F3FE7E3,&HBBF3BA4C,&HBB9FAACB,&HFADD9DF2 + Data &HF1BCFC1A,&HC44785B2,&H7AA25DC1,&H89E57FC1,&H652DA589,&HF7AE32CD,&HA6A47B81,&H7BB6D1F2 + Data &HB7D219E1,&H37F6EE66,&HCF99BC7A,&H0967D4C2,&H1E1EEF02,&H9A9F7C9D,&H8072EE9F,&H4C4C2247 + Data &HE317D9D1,&H2289DF8E,&HC0FCD39A,&H2295AD72,&HA7F203DD,&H83FD0C75,&HA42F71CB,&HC326DBE3 + Data &H66018D30,&H84B2EFEC,&H0692CBD3,&HDFFE3651,&H28377FCA,&H4571FA27,&H94EBE4D1,&HDB6016C1 + Data &H97AC04E0,&H7847192D,&H04F71367,&HCA78BB2E,&HC02DFFEE,&HDF5C0B38,&H96C3B79E,&HD8E43A91 + Data &H23C70AC5,&H9DEFE6F8,&H8532E4D9,&HDF253CE6,&H6603BCA8,&H657998A1,&H6F86BE7B,&H2A179EC8 + Data &H55BEAF30,&H5F84B065,&H2F481599,&HA521690F,&HFAF8EEA2,&H7F302FC1,&H87CAA00E,&H00F8C98E + Data &H9F8094B3,&H002E61FF,&H00939939,&HA5C8E6E8,&H2153F8BB,&H9A91D682,&HFB48964A,&H1ABD6C3E + Data &H14ED1513,&HF3AD666E,&HCEA2E0D7,&HCD026C26,&HF09127D1,&HAE2EA7FA,&HE795535A,&H91F4BF55 + Data &H51EC12EE,&H60D49C57,&H08D903D3,&H91A623A9,&H381CB901,&H30576E18,&HAF274000,&H1DDC6A84 + Data &H9773D995,&H2AE21EFD,&H89BC9E15,&HA26D37ED,&HCDCFA1A3,&H78221CAC,&HEBD8BAC2,&HDC5C52E2 + Data &H85D8451C,&H84ADCBE1,&H101383CA,&H7FA9D6C3,&H39A37FC2,&H7D3A2476,&HE1D25077,&H0570B28D + Data &HEB79A5FC,&H3C823C12,&H70C97029,&H77B5800B,&H4A43346D,&H2F307004,&HA4F97C18,&H5F84AA4E + Data &HC1DC75A7,&H49AA42D8,&H0E0D3D3A,&H53448A22,&HECBCF8BC,&H97F53811,&H9FC697B7,&H48494AA1 + Data &H54D69018,&H5B4ED7C8,&H9E3CEB8C,&HA3B1B030,&HCD65FEFD,&HFB7FD5F1,&HF2EBBE27,&H551ADE3A + Data &HB81D68BA,&HDABB61C4,&H7D72332B,&H05EF8547,&H990F2F89,&H2158AD99,&HB532B718,&HC4ACADA9 + Data &HC685B64F,&H9416F6BD,&HD78937DB,&HD05C61AA,&H355E5E17,&H7FCE2F62,&HB6BFAD31,&H862E1B83 + Data &H4B6C0BA4,&H868800FC,&H1A851586,&H7D833694,&H921D8096,&H7BC84667,&H89CD0424,&HBAEE3525 + Data &H98DC6A04,&HDCB5F77C,&HBA56B20F,&HB3B45617,&HE33D7F74,&HE916B024,&HA91E9706,&H768B05C7 + Data &HFA92E3FA,&HE903DFE3,&H8A672A00,&HEF965AC2,&H45DC293B,&HE1C2249C,&H5FF1AF40,&H666A49B2 + Data &H1C0D8D8A,&HB0158308,&H043BEDDC,&H81162EF7,&H192C7EF9,&H5B189FC5,&HF8A94FD3,&H7FB37122 + Data &H46D70204,&H203D5345,&H9F395E3F,&HDD75152D,&HB7F68F98,&HAD0D21E1,&H20C23761,&H17970D47 + Data &HF591F83A,&H441CDD75,&HBD98AA66,&H7B439AA1,&H03BE36F3,&H258827C9,&HC3F19A73,&H5F14BFBE + Data &H6FEB6CAE,&H23EC1562,&HC0098201,&H0815612F,&HE55C0C38,&HD863829B,&H239A3AD2,&HCDEDB641 + Data &H58A1A606,&H470E00A0,&H2D4C17B2,&H965B2B10,&H320EC739,&HE306860F,&HEE41A717,&H85DE4186 + Data &H27A84AD4,&HC9643AC1,&H151E8271,&H0822D42F,&H50D578A4,&HA08CF1B6,&H0690EB8C,&HAB782DAE + Data &H6E2980D7,&HB03B200E,&HEA012B02,&HCDC442D4,&H909C0A4C,&H3E0E1D65,&H1A6B805A,&H240A39A2 + Data &H101BEDBC,&H66EFDE9B,&H0A1731F8,&HD7B2E5BC,&H833BACB5,&H1EE754D1,&H9C37C3AA,&H5FD2EBF4 + Data &HE45B14F9,&H17564078,&H9401FD5E,&H89E085CF,&H24403F3E,&H6D47275E,&H63836892,&HCDF0AF81 + Data &H38259667,&H4C5E2819,&H48AF3C30,&HFE7124FB,&H90A156A1,&HC4434ED2,&H84DB4AC6,&H8D448100 + Data &H246F8DFA,&H3B223F69,&H946DE9B7,&H506E6EEE,&H01DE2582,&H44B7D88D,&H87A63411,&H6DE759D8 + Data &H3E1EBC4F,&HD89CFEBE,&HAC001338,&H346F98CB,&H9CFF9B93,&HF62723AD,&H0654CBF9,&H2B6F68A0 + Data &H24A63A6B,&HFD7F86ED,&H9BE22E7C,&HDAAEE671,&H2E9787F6,&HC077DAFC,&H0F763219,&H4C7EDD84 + Data &HE68DD43D,&HBAF49138,&H7A7240F5,&HC801786B,&H0532EEA6,&H112A4888,&HA721AD09,&HDCD5715D + Data &H13A70576,&HF86A830A,&H658624C6,&H044E0D0F,&H0ACC8756,&HB2B54DE2,&H89E9191D,&HEC5371D8 + Data &HE5C6BDF3,&H6B9DA41A,&HC8D42C98,&H3E8C4255,&H4C473E94,&HD76BD261,&H98606146,&HAA27D6FE + Data &H8C9654CD,&HFD1BB8C2,&H70124016,&HB834097A,&H07F48B31,&HF70A1E6A,&H3F73C705,&H5FA20B25 + Data &HE0D89858,&HDAFBE277,&HF1B85368,&HF1D9A268,&H173726BD,&H17F38ED0,&H6A912A78,&HDE9027F0 + Data &HBAEE8F02,&HDBD58954,&H9CA8540E,&H7885C37F,&HA3C575FD,&H207E8BEE,&H68B03C23,&H500134AA + Data &HE2E085D1,&H1E171643,&H19D59406,&HE8E149CD,&H4AA1EC5B,&H4480C54B,&HFEDECAB1,&HBCF7276C + Data &H560EE98D,&HD0131374,&H807D0077,&HC1CA6C1F,&HD8004F4C,&H57BEDB82,&HDAC80270,&HA9CCAEF2 + Data &HF707A2F2,&H935F07FA,&H3CF6E338,&H63C1937A,&H8E47FB14,&H877A1395,&H4F0B7320,&HEA0DD689 + Data &H0544C8CE,&H7BBC1A6D,&HEC912657,&H39374513,&H594C639A,&H7E0A2216,&H8DBE6372,&HD25AA7E9 + Data &H3435CEA1,&H2DD757EE,&HE026620F,&H9479F0C0,&H040A6995,&HA6DCF1C0,&HF07215D4,&HD5F77676 + Data &H0E37225D,&H45D566FD,&HE7622266,&H3AA9E13B,&H2411D8C1,&H9C0D0372,&HCFDA9404,&HE470E613 + Data &H15C568BF,&HE537DED0,&H7B17F666,&HA65B1390,&H5DFD5BA4,&H8C0E26F7,&H8FD24A49,&H89418621 + Data &HA8CD3CD4,&H8592AD1F,&HC2C74CDA,&H9A1A241C,&HC7152CEB,&H44F0228C,&HFB492F3A,&H87C40018 + Data &HB755C1F7,&H41C69258,&H98806309,&HE9B27EF2,&H661FD6C8,&HCA9271EE,&H888A1912,&H877FC224 + Data &HCB6D9A4F,&H7C4B5D4B,&H934FA3E7,&H555CFDDA,&H0BFA79BF,&H63D15281,&HC73425C2,&H9BD79491 + Data &H5D1DC50E,&H4DF71797,&HD41B42FD,&HEA31FD5F,&H4D1933DD,&H81C7579D,&H307BCDD9,&H1905F37A + Data &H0511955F,&H745AC425,&H90D55462,&HA499697E,&H249B0A9F,&H54B8521D,&HDD5AA736,&H1A739514 + Data &H163203A6,&HB210AFE0,&H851102BF,&HD06F2261,&HBA500C1F,&H4A85A9E1,&HEA9D3E2F,&H5FF2FCBF + Data &H5801CB7E,&H75F224E7,&HF38D1CFD,&H0D48BB4A,&H6DAE2E73,&H426027CB,&H6A9D61F7,&HA0EE5265 + Data &H2437061F,&H4C7E8245,&H8C5BEEF9,&H57F8BD58,&H418721CC,&H524965BA,&H2022F0C3,&HD571939D + Data &H03987BBF,&H5EE2791F,&H028F5FA2,&H6DAE42B7,&H034F7198,&H5E20BC83,&HF8AB8685,&HBFCE942A + Data &HBF26FB67,&H5F7A4991,&H85675473,&H037FCC45,&H5914AE45,&HA872593F,&H8F4BE90F,&H9926AAEE + Data &HDFF55C6A,&H1174016C,&H474AACF7,&HBCF37F1B,&H6E9379EA,&H7A3FE1C2,&H73E2B3C0,&H84958297 + Data &HF84F9F34,&HE255F25F,&HED75E3CF,&HB6756AFB,&HC73F540E,&H34761556,&HF839D9EE,&H63468C26 + Data &H64BC1076,&HB930D3EB,&HA4643FF1,&HCA4743ED,&H0572C850,&HDBF369DF,&HE23C83D6,&HDF1E4197 + Data &H2BDDBDAC,&HD58C9AB2,&H5C4688FB,&H08B5BF35,&H6815FBD2,&HF8A473DE,&HEAA1A961,&HF4C93F5A + Data &HA574E261,&H3C793320,&HE5F69061,&H5CFE7C57,&HEF74A20F,&HB6088402,&HDD2DD35B,&H9B2FD38A + Data &H37835FB5,&HC3E95C91,&H88567FA1,&H9CD0D2D3,&HB5BE8172,&H9D6B8E29,&H9CBD7698,&HAA3CC42B + Data &H846FD447,&HC1E2A165,&H33EE45F9,&H832B56C8,&H5F1781DA,&H979DE99E,&H27F5B59D,&H65A71728 + Data &HCF00B130,&H060E6291,&HE03588AF,&H3A1CB0B2,&HD0CE5644,&H04AC75DC,&HFCCE5481,&H26620EFC + Data &HD6B3B82E,&HF907E1BE,&H5B989294,&H8E6824B5,&H6A0B091C,&H0A086303,&H3187E479,&HCB433E2B + Data &H3655BAF1,&H23E62D2D,&H8A8A2C0F,&H743CFF99,&HEA36E171,&HA2D82B11,&H2A6F8126,&HCFEB2421 + Data &H1AA99CC9,&H5F2A0032,&H758AA014,&H12FBDEF0,&HF5A00262,&H74E6D511,&H79064315,&H86EB446A + Data &H6752E62C,&H26C4A1FD,&HAD9872EF,&H0B0662D5,&H8C7341AD,&HCC70E1A0,&HDF1D3447,&H81FE9FE3 + Data &HB2B86DE2,&HCB1A76FF,&HEB1B27C5,&H305485F8,&H1A569739,&H8007DAB9,&H98EA4C39,&HD571BBE0 + Data &HC7C2539D,&HB0CA97DB,&H2AA36930,&HFA5B749E,&HA48C7CC4,&HE6BEFEE0,&H4F14E4C9,&HE1F151FD + Data &H8DCF893F,&H4CB62A2A,&H4AF710EE,&H5D927690,&H43FDBD26,&H8F35BC63,&H8E83BCE6,&H68A1F25C + Data &H4C78EB54,&HA2149210,&HB605BEDD,&H8748E335,&H804D2B73,&H7212E9DA,&H5D33ED23,&H544DD09C + Data &H21786ACE,&H402EE59B,&HA0EBE053,&H136548CF,&HD25A7A37,&H89EE346B,&HFFBBA7AB,&H10DFF8B7 + Data &HB3B9C276,&HA77A67EF,&H96F87929,&HF488D9D1,&HEC949950,&H7467E575,&H114A0802,&HBC942734 + Data &HDCF71BB1,&H3F7BE823,&H377F5F33,&H6DBED3A5,&H5425E3E3,&H6F42163B,&H3484CB68,&H4D28BAFC + Data &H032741D2,&H11B9FAD8,&H467D7A9F,&HB05FB64E,&HB1EE898F,&H3ECAF0CB,&H7B056138,&H439763D6 + Data &H8A2D82D9,&H502A0FCA,&HF8EB351A,&H499FDB16,&H72907B5E,&HB63CDA19,&H36A37A49,&H37A79477 + Data &HDD5FFCAB,&H3BA33AF6,&H1C6F0B17,&H858F3715,&HEB345650,&HAC4315B3,&H2E48BD6C,&HB05897DC + Data &HC81762F3,&H1105C3CD,&HEE09EE74,&HBF3AC77D,&H7E6FB93F,&H29FE2A7D,&H359C3495,&H9F578791 + Data &HCFFA5745,&H390F8592,&H87862538,&H4DBF8C87,&HE5B17E65,&HB7F58530,&H7FD52AAB,&HB86F4ACA + Data &H7A84E8ED,&H3FECA8BC,&HC97E6BDD,&H54102E77,&HBA37E647,&HA5A9658A,&H7F2DE090,&HB56FEACF + Data &HD5BC73FD,&HADB39D8C,&H8C47B164,&HC81F999C,&H2E88F35C,&H85780123,&HCF7A1FD9,&H82F601A4 + Data &H503CC2E8,&HB6C81814,&H0E5ABF58,&HB9EC597A,&H2E623F18,&H9E5F54C0,&HF64E5C7D,&H8B4682F8 + Data &H09506219,&H1C71C592,&HA27FFB64,&H6F28DF39,&H539DD25F,&H8E8DD477,&H415ABC99,&H4E315A4E + Data &H6FF31DCC,&HECE9BE41,&H799556CD,&H659F1179,&HF4691EBD,&H54489FF4,&HEAC4174D,&H51A9D31F + Data &H5BACA0A2,&H3036082A,&HECB16167,&H02F6F9D6,&HCDCCA162,&HC02CFE33,&H88F95A6F,&H1549E1C1 + Data &H80092204,&HE7DB6C64,&HEA0C8DB5,&H7E6ACF47,&H3539A2FA,&HABED3664,&HC4F92F10,&HA604B672 + Data &HE560CD9C,&H4D90FED4,&HED26F945,&H81DC4E66,&H6AB6C955,&H942BC9F8,&H97759015,&HF0A5A27D + Data &H65F4D6CF,&H98C73E27,&HB64991E9,&H719F436C,&HEE072FE9,&HD9B88FE5,&HC92682D4,&H3366F1C3 + Data &H6391F054,&H6056CB88,&H16060246,&H1B5E1B3E,&H93EF8CD9,&H3476422E,&HCFFC8DA7,&H23738233 + Data &H8B32BAD5,&H3D895B76,&H768E31AB,&HDA12E25F,&H7EFECC80,&HF92E8B2A,&HFC9E7B2F,&H3E104FF3 + Data &HB5485238,&H606889FA,&H5D24F40E,&HCB829EC6,&HBDE4503B,&H714CB32A,&H03F92D8E,&H8F2BFDAD + Data &HB704580F,&H203142F1,&H593F70B2,&H9EEA6C0C,&H8FBF380D,&H53108930,&H15A1576C,&H1943D39A + Data &HDE7EBB9E,&HBC2351D6,&H2B967C31,&H90F364CA,&H71A72908,&HE65DB2BA,&H9D3067D9,&HE8401DB7 + Data &HA07218A6,&H7353E4CA,&H481F9788,&HC967F959,&H2FB5F2F5,&H605AA1A3,&H4D4A299C,&H37613AD4 + Data &HECAE48F6,&H5B8A7B2B,&H7C5C5811,&HFA2B80C6,&HA828957D,&HC2445355,&H41253A42,&H41922C46 + Data &HD96FA6F4,&HF50EFE43,&H0C58C303,&HC0795F42,&H36FA1300,&H5A630EFF,&HD0FD619D,&H5023A792 + Data &HA41A135A,&H44577B8F,&H885461E2,&HD0B6008E,&HA6D3265F,&H82E6996B,&H1791B66B,&H0A000956 + Data &H77CF67EF,&H28DA7482,&HA0F9334E,&H68D84759,&HD6C63A41,&H9F922564,&H676490BC,&HADA49FE4 + Data &H96F29E62,&H9A39B59B,&H6E777233,&HD52A986D,&HC1E32210,&H626BF426,&H5789593A,&HD5C67A30 + Data &HF8306EBA,&H1B9448B5,&H6EB8D492,&H2BC811B2,&HB68DA85A,&HE8BC0FF4,&HF498785D,&H7AAF555E + Data &H36D078A1,&HE8A4C963,&HB51F0961,&HB69C8A11,&HF4225784,&HAF6B3B0D,&H214E92F6,&HAD5DB369 + Data &H0AF68300,&H176BEBB7,&HF661764F,&H1BFE9BB3,&H1F28B6D5,&HE2C6E1E8,&HC9861C28,&HC2CBE9AD + Data &HF4BC254A,&H7F412B48,&H9D2C83E6,&H649B4EB8,&H5BCDA97C,&H29DF555A,&HED9AEB76,&HA511082B + Data &H481CB8CC,&H005E4857,&H729D11A1,&HFF5D6706,&H6059ABF3,&H13A57695,&H1251B7ED,&HD2ACB064 + Data &HE9EDA0F8,&HEBC4D754,&HAEC3EF7F,&H27614D28,&H8E6876CB,&H1F58490B,&H3E5C4A1B,&H35350259 + Data &H34C60294,&H06DB3DFE,&HBC121F06,&HA63D2EE8,&H8D1BE034,&HF75F04BC,&HFF4CFA51,&H22FD8FF6 + Data &H15F5C234,&HCED21153,&HB6BA9102,&HFBB4DFB7,&H2CF93CB0,&H8ED7F8FE,&H3FE3D788,&H8888CF9E + Data &HFF5DE716,&H70F09818,&H3360A6C8,&H8825BD32,&HD4F01F33,&HBC97AC5A,&H9085F3E9,&H434C1B85 + Data &HF46B7AFA,&H94FB501A,&H0E3FFBAC,&HA0656E23,&H27618C84,&H180A0C10,&HDA589D22,&HEC8CC7EF + Data &HE489EBA1,&H35E1913D,&H9F2566EB,&HE603D361,&H23B8B894,&HEB6053EE,&H015B962B,&HC36C484E + Data &H7478EC84,&H19726B51,&H86D2C0C0,&H86179C88,&HD7EB48CA,&H39E722E9,&H0B502DCA,&H48127340 + Data &H903D5195,&H8AB7AEB1,&H7F26A336,&H825808CC,&HBA054803,&HF2B75E2E,&HD1281451,&HAE2A3D11 + Data &HD253EB7B,&HE8FDE44F,&HEB32E623,&H193AE573,&H113F1969,&H2347B885,&HBF6B5D75,&HB1E9A969 + Data &H5AB3619F,&H7EA5CE27,&H84EAE0EF,&HF4E66370,&HABF204B5,&H1B146587,&HA40690E1,&HBD0FAC6F + Data &H40342950,&H72C9F273,&H942F2FDB,&H7F3931B8,&HD20BF7F9,&H0AF7B3B7,&H6D8F190A,&HAC57D589 + Data &H9B1569B6,&HFE1360EA,&H1A742B10,&H5A6956DF,&HE0DDEF58,&H58249474,&HCE796E95,&H10458F28 + Data &HAD4D9044,&H38B81D6E,&H9B393B5E,&HBF2F1C26,&HF154D220,&HB950DC64,&H45908DEB,&H47C898A2 + Data &HEA55EE56,&H0C4E7257,&H4E680DEC,&H5AD8C095,&H366A5107,&HD2BC8B04,&H83BA5C09,&HB6E8F32B + Data &H8CCB945F,&H7ED664D4,&H3C107E71,&H737931A6,&H9A3C87AD,&H7A6F2000,&H00520EE0,&HC7600529 + Data &H5FBB9253,&HB9B69B10,&HAEAEFD1B,&HE4098335,&H3566F2CA,&HA124AAD1,&H053AEFAC,&H0DE6BE9F + Data &H92EB36A1,&HE7EF14E1,&H478C9376,&H2CC89473,&H1B41249D,&H973B5637,&H0CDB9BF8,&H2B35B3D9 + Data &H5948D698,&H29B68B29,&H5B573BA2,&H156029D5,&H030572CE,&H9C0120A7,&HDB08BADD,&HD2FB0F78 + Data &H2EB4B23B,&H33FC41AD,&H22DF2227,&H025E8B1C,&H19C30FDF,&H3D48AC93,&H903A6D5D,&HD53F5865 + Data &H399C516E,&H630E3729,&HC49307B1,&H1DCA5875,&H9923D1CD,&H6A287201,&H46EF3AF9,&H2F60EEEB + Data &H107AD2E6,&HC2C480C0,&HB0B4D0F8,&H1A77CA8F,&H27021EC7,&H65420F6C,&HD45B8C5E,&H2484AA23 + Data &H06A71761,&HE074C66E,&H473A6E88,&H098E561D,&H2C35D950,&H19A537F7,&H5F4AFA4F,&H3D5CF9FF + Data &HE4A93AF0,&H4A71C653,&H7D70CD2A,&H7C23FDF6,&H3950BF89,&HC49AC734,&HCA1527E2,&HC4B1A830 + Data &HEAEF88F5,&H53805AFC,&H58EE5625,&HD0E79EC0,&HE90EA057,&HFB918E1F,&H7B6AACC5,&H540839E9 + Data &H7656D6BB,&HB3493A43,&H9C65EB86,&H78A92DE8,&H707BDC0A,&HA93FBFCD,&H9F629BCB,&HC10CC9AD + Data &H8788FDFF,&H61E5EBBC,&HDFE388C1,&H90F7297B,&H6292C1AF,&H501086C1,&H3E734713,&HC18C2290 + Data &H5D7CA0B9,&H800647CB,&H12EFB0DC,&H8904DD4D,&HA248F59D,&H7741D9A7,&H2DEDF695,&H0E21EEB1 + Data &H6EFDA439,&H55A816F3,&H7ED0D716,&H23B11B72,&HC27C1DD2,&H6788416B,&H52D8E325,&HC3FB2AF0 + Data &HCD13D58F,&HBA2A705E,&H70D73CB7,&H152BE4FB,&H489DEC0F,&HF1D4B73D,&H9636E5DB,&H34152F3E + Data &HFE2C8227,&H5DA42B69,&H5BC526BE,&H47AD99EA,&H05B679E4,&H98242635,&H003F3714,&H2B6B5B67 + Data &H22F72481,&H80332A92,&H7FD697B7,&H67823DE8,&H68F245B6,&H6F22A3B0,&HEB91F2E0,&H78977258 + Data &H673AC737,&HE4FA61E6,&H01FA7BC8,&H95597FAF,&HBC8CFB77,&H243406DA,&H1C99DF92,&H8F24CA7C + Data &HA9FFBACF,&H97C6EA99,&H76F5B490,&H2141A91C,&HFB12D59A,&H1C1A949D,&HE1CE1612,&HC248AEEC + Data &H7F41559E,&H53D7D9F3,&H2A34EDD8,&H7F938D3C,&H66376A2E,&HD6DD19A7,&HB4DAAAB1,&HE2E2C48D + Data &H7E3D5B97,&H7D7858D4,&H7F642D90,&H0EE8FDF3,&HB5EAFBDB,&H6B4EB1F4,&H4CF8E2F9,&H6D77C361 + Data &H94AD162C,&H791B4F9A,&HCD10B2D0,&HB9F81329,&H76142E36,&HE51313AD,&HFCAD761E,&HFEE792BB + Data &H617E274F,&HB5B9E0C8,&H9E0D27CF,&HDA56CF41,&H2E5E6A04,&H4D3FF340,&H6D093191,&HC622A2EE + Data &H1B0490C9,&H459BD677,&H53F878BD,&H0EA471B0,&HC652A996,&H5AEF168D,&H3D1A4BFF,&H1C6C5328 + Data &H1D37A2EA,&HBB5ADE7B,&H1C2CABE5,&H13A37CCF,&H514F69A1,&HC8503D42,&H399E43B4,&H6CEBF438 + Data &HCAFD11FE,&H9D685FFA,&H1C2C70F9,&H3AB79DF4,&H026D9F49,&HA124269D,&HF9044035,&H79C711D7 + Data &H49810AC4,&HC960383B,&H3A48FD4A,&H545F4606,&H9D5DF253,&H91873398,&H702C6B1B,&H75562443 + Data &HD8D7B523,&H2C127637,&H8ED97876,&H6EF0ACD4,&H502648FA,&H143E3ED6,&HF9236A89,&H8D73EE99 + Data &H8A2A4D38,&HC759B4B7,&H5EBF5B72,&HC30B9EB9,&HA2009372,&HAE30FE41,&H909C1F19,&H69212E12 + Data &HB9CB6B5A,&HC76F3290,&H0E80A9A6,&H8CC4B984,&HAF9A48DC,&HA4F1FA7B,&H4B743619,&HF4F93A45 + Data &H42CD61B7,&HA8196BF8,&HC298799A,&HF1459EE1,&HDD64F62B,&H3DE73430,&H1614B199,&H8EBB164A + Data &H46607D62,&H0782359B,&H394702AE,&H76ADD773,&HD737873E,&H5A738648,&HAE7FEC68,&HD4352F9C + Data &H3C47DD84,&H2F0DE7DA,&HF6778028,&HAAD3E4F1,&H4BD9D934,&HEDE8C035,&HD3C11C3E,&H2C5553A7 + Data &H6B4F5A73,&HBE483D48,&H9CEB29EE,&H87D69316,&HC1BE7061,&H23C56C0C,&HBBE9BB23,&H3F70D004 + Data &H22EF3CBD,&HB3EF4BE5,&HFCB163F2,&H2BA8A34A,&H91775720,&HAE9AF42B,&H045C1A38,&H3354BA5F + Data &H62FBE19D,&H7E2F2738,&H5B55157C,&HFFADF72E,&H3F0B6A38,&H33EFD489,&H5B284E47,&H1B24A26E + Data &H954CE107,&H0A6ABBCC,&HD6F3477B,&HF2360964,&HE2011E98,&H05F257E0,&H233FE32B,&H2B6EB898 + Data &H39D4DF9F,&H0D68C872,&H5ED15464,&H4E302BDA,&H0AA63479,&H0CCE4CAA,&HEC540811,&HA5FF7F0C + Data &HCD3D4257,&H756D46C1,&H47E27176,&H6DA4DE2E,&HFDE5534D,&HAE472138,&H09BC237C,&H0573241E + Data &HA11E3651,&H66B26B81,&HE4B5A739,&H5DDBD4F0,&H06BFBAFB,&H9C5A0CAC,&H27B50D97,&HA8CFEF54 + Data &H8FDDB37C,&H68A561AA,&H6A65204E,&H27E430E3,&H86F1FDA1,&HEEB10C2C,&H85C43153,&H767AB572 + Data &HB86E417A,&H7B7694F5,&H00985B53,&H0707427E,&H5CDC61A3,&HC42ACEAC,&H80F51B96,&H56E37133 + Data &H4E287648,&HF155EEFF,&H9A70D5CA,&H88E8B689,&HE4C8D209,&H9C1B4730,&HAE059B70,&H056A9B91 + Data &HF9B2B7C0,&H1F772861,&HE79B8B7C,&H90824813,&H93690DF8,&H2182F1AF,&H9A1E2464,&HA5828915 + Data &HC8B87A37,&H2F5355CF,&HCA6035D2,&HBD3178CC,&HE78D6AE3,&H0941ABC1,&HAE8CA5CB,&HDF70EE07 + Data &H442E2442,&HD42A09A4,&H41E41B7D,&H8598B45F,&HC250C1F2,&HB3D4E1A3,&H52B55DE2,&HA84B8425 + Data &H2CE6A7C8,&H2852046C,&H540E5261,&H9A12B101,&HC2BA4C93,&HE9B10AB8,&H9905FE98,&H5C163073 + Data &HE3FBF650,&H12827112,&H4C3DEA8C,&HC70CDBAE,&H108F2C32,&H6CFD9CBA,&H1F002072,&H31205C76 + Data &H48C25941,&HFBB1C164,&H98F91183,&H4AD0ECBC,&HE8149764,&H090B8AE3,&HC10185AD,&HD270E8E8 + Data &HA2A8043B,&H10EADBF0,&H962E2FF0,&HEF92D62E,&HCD11D4F2,&H35ED2029,&HF5194025,&H07F139D6 + Data &H47D775E3,&HFECA2F0F,&H01E1927D,&H7A16583F,&HBACC7658,&H7F227E34,&H23F5FB91,&H19B7E8D8 + Data &H246933A7,&H926A4297,&H22442D7D,&H93422FE9,&HBB97B384,&HDFEBE0E1,&H9599810C,&HF0D8380E + Data &H50C4895C,&H0D0BC127,&H28094E11,&H39FD7F34,&H53C8929A,&HED742828,&HDAABDD3F,&HECF28975 + Data &H7E34E46D,&H4B563BFC,&H244718E0,&H5933F6F9,&HDBAFFC55,&HC652AB10,&H86379F0A,&H794F6DD8 + Data &HE5A7E670,&HD49C1DA2,&H260EC95D,&H734667DF,&H273428E9,&H209FBA9D,&HF9BC443A,&HD5E947BE + Data &HFEF88AD6,&HFD31FD80,&H4DF3382E,&HF2BBB279,&H24AD2A3C,&H2D6E23CA,&H24A0C878,&HDD3FF8D7 + Data &HEB58C92B,&HA10626A0,&H9BDCCD10,&H5A9D9FEE,&HFBED83E0,&H762B072E,&HCD5AC935,&HCDD3CEA7 + Data &HD995D751,&H2309D8F9,&H8143E126,&H22A7A5C5,&HF8C478EB,&H1F097C27,&HC81E18E7,&H3EC61876 + Data &HD758CFAE,&H99F03AEB,&H442D39BB,&H44B9625E,&HB8CB9A3C,&H7692EF79,&H133B3D9E,&HE9CD1B7A + Data &H6C5BCC13,&HC0F74AA5,&H16662E10,&H7C4AEE53,&HA9DF4C4A,&HFD8BF914,&H1E343627,&H3B5AC293 + Data &H7CA23A58,&H748DE4F9,&H6C6B59B6,&H436364AB,&H2455478D,&H7799C09D,&HAB3916D1,&H447E04B2 + Data &H3BC1B3BE,&H6EB064B0,&H60E073B9,&H2AFCBDA3,&HCEE1FBCE,&H5839469B,&HF24B265E,&H231DA2BC + Data &H020B418E,&HFCA531CD,&HE606426E,&H6CC99820,&H9E520E72,&HB309E956,&H31A35297,&HA21BA2B6 + Data &H520B9225,&H41E0FD5B,&HC749401C,&HB31EAEA4,&H25F6E181,&HB7AAC5A7,&HDBDDD9CA,&H43B8E1BA + Data &H977F2DA8,&H13CC93A1,&HEF850A48,&H8E3C37C3,&H5E9F256D,&H076A0CD4,&H9BAD3B3E,&H18D0F1C6 + Data &H14907B59,&H59114AA9,&HF3B9C37B,&H9CED92EB,&H226EA83B,&H5DD6C602,&H20358164,&H5EE976F7 + Data &H26FD5933,&HAE13C04A,&H89224B45,&HBEE8DE94,&H96543152,&HB45AF604,&H06DE118F,&H7A2411BE + Data &HBDD34F36,&HD06313DF,&H7DCB895B,&H6134CC4F,&HA7C021C1,&H3D1D60E2,&HCA9BFAEA,&HD34E5ADC + Data &H27FCF6B5,&HFEBB8FC1,&HDE1DCF85,&H7345DD34,&H8DC548BC,&HF13F21C7,&HA4752563,&HFEBF6FB8 + Data &HEA71EEEA,&HF5DB98AB,&HDC6F6A53,&HC10B150E,&H047B0470,&H1FFCF3CB,&H4B4827DB,&H1E9F2B12 + Data &HF5EBE8CF,&H9DF46ABD,&H63357ADA,&H44AA8A6B,&HDD0AA135,&H6D9459C0,&H3F5E9518,&H428D87C2 + Data &HB1A81B7B,&HD33BB2A4,&H23822610,&HF149E9F1,&HE6879064,&H0D989244,&HFDA275A2,&H3636AD9E + Data &H8F930436,&H5E583CB3,&H5F26B2DA,&HAB75F220,&H42B0B24E,&H6D309731,&H1AA9327E,&H67F5E545 + Data &HEA305D79,&HDF22461A,&H1B21DBF3,&HF8BD8273,&HEFCB90B7,&H2AD0BCB9,&HBB38D2AE,&H675FE37B + Data &H3ED309D5,&HF7AFDF84,&HC29E7A5A,&HAE9C99DF,&H73AF5EB6,&HF1D1D82B,&H47DA7F3F,&HB8342E09 + Data &H1E2F56B1,&H709AE7AA,&H53A6E715,&H37332B34,&H51C83ED5,&H7FB5B62F,&H6ECC3323,&H276052D8 + Data &H18E22233,&HDBD3E074,&HE1FCE799,&H978C8A92,&H254C53D8,&HA958DA6C,&HBB95BA0D,&H1EA74058 + Data &HFAFCCED6,&H6C960BAF,&HABCCADAE,&H4FBBE723,&HC8D6B53A,&HF67F9808,&HEC74A50C,&H44E8DD99 + Data &H71B5CBC1,&H4C50B4D1,&H7A77BBE3,&HE060C7B6,&HB81BE0BE,&HE3583F0E,&H6558B60C,&H87B35B5D + Data &H4B599840,&HB9D2B245,&HD78A13FE,&H39B070AF,&H9A0529B6,&H92FD9E5B,&H2F66EAD3,&H27A5A7BC + Data &HA241CB9F,&H0EE0F29B,&H3101290E,&HEDC8762E,&H1D041257,&HAE149403,&H86E9DEAF,&HD4DD7526 + Data &H77360A53,&H9D6B8BA1,&HFF46C518,&HDA257A5C,&H3A1BB082,&HA62FD792,&HC49BF5AC,&HC4910878 + Data &H852F60FB,&HAE39A977,&H17ABD5E6,&HFDD475B0,&HD7A708FD,&H07DBC06E,&HE646060F,&H3A943E84 + Data &H65A75E15,&H0FF26137,&H9E3B6DF2,&H509F1ECF,&H2E0ABE1B,&HB99E0BE5,&H691F88C5,&HEF5EB2FA + Data &H1D2F430F,&H2242A39A,&HA1EBED7E,&H6D387541,&H95B8E25B,&H86A35FA2,&H0778CDBF,&H1ACB1ABA + Data &H26B8D152,&HB2E527D9,&H5B7C9908,&H7BF5967A,&HDD43F549,&HE4001491,&HC8CF16C7,&H2F1106D9 + Data &H7822562E,&H61C189A9,&H08986C11,&H97723217,&HE4BFA0DD,&H6DD85697,&HF0CB814C,&HDE0C1582 + Data &H6EAB7FB6,&H771ACCD2,&H29A9F66F,&H37E0C474,&H56355DA4,&H76B9E944,&H9FD06286,&H8225BB8E + Data &HABFB273D,&HA8EFC1AE,&HC6F46A7E,&H4E557CA3,&HEEEF2684,&H0C1A2457,&H47A84DFD,&HF407D3D8 + Data &HBBC8C458,&H4747F9E2,&H49284373,&H9CCDD5E5,&HA0647103,&H4114E6C3,&H5B4CC2BB,&H08ED57B9 + Data &H4855C7AB,&HC0CB0C73,&HEDD8FCB7,&H378722EB,&H62244F73,&HBE90C3A3,&HCAC97B41,&H159F8973 + Data &HA5451BDA,&HF7BBCFFD,&H759FDD5D,&H061A4981,&HE408AA18,&HB0660443,&H33CE857F,&H0B666FBA + Data &HED6B88E1,&H4FA535CC,&HEE4FAD98,&HAC3EDB3C,&H47BABC14,&H9A9489B5,&H727EBB5B,&H447047FB + Data &H2CCED2CB,&HFA80132C,&H371CA876,&H69C45024,&HBCCDF7EB,&HBFE92458,&HD8C80ED1,&H0420BDB0 + Data &HBB0C13D2,&H86973CF2,&HFA34812A,&HAC7E17C7,&H1818A1D2,&H3911511A,&H0F3364B6,&HB1BD3F1C + Data &H2E724852,&HB1E4FAC5,&HE4CAB4E9,&H0DE469B8,&HBBBE18FA,&H67F231EE,&H7BDD34D4,&HE7F8163F + Data &H52B787AC,&H646CA2B3,&HB6166867,&HA4FCA862,&HFF335BE2,&H739EBF87,&H0CD87D37,&HA9C75572 + Data &H0628437D,&H2D895FEA,&HF0E3CBED,&HA6996F04,&H27262F9A,&H2E720993,&H5959E2DF,&H27475C0E + Data &H70B9DE85,&HF97D95B2,&HA19C9AD4,&H1DCF11AA,&HDA166570,&HC42B5E51,&HB27A6D1D,&HE1FA5099 + Data &HD3D823BC,&H549C862A,&H88D3C3AF,&HDA9EE480,&H2ADFE8E1,&H0113D32E,&H71DD3BA6,&H346F55D9 + Data &H80967C6F,&H60B8DAEE,&HFC24198C,&HB5597A35,&H185B8DD0,&HFFFE1A91,&HCA152FAB,&HA4B98CF1 + Data &HCE44905A,&H9119E77E,&HEC9164DB,&H342E9414,&H29AEA3BA,&H13C3C1D3,&HBF5F6C9F,&H322B1D4C + Data &HF64DE693,&H2EC997A8,&H780F9B90,&HA422A831,&H893B7554,&HEBF222C1,&H2C8B24FB,&H1BDE1A47 + Data &HB498FA7F,&HB9A0F88A,&HA5F9020B,&H0B444142,&H66733BA9,&H33D4851E,&H62D3D43B,&H62FCB4C2 + Data &H165388CB,&H88EF604A,&H4920E0F3,&H2913A2B7,&H1B1BE0F7,&H9D31C86C,&H08A00C9E,&HD7F24CE4 + Data &HA4DAE760,&HD4720395,&H758EBFD4,&H40CA0136,&H30F4C167,&H3D11DBE7,&HFD6BBCB6,&H3F523DD8 + Data &HD1027600,&H9DEB8D1C,&H91A28675,&HDF81846F,&HEFCFDFB7,&H56EEBA56,&H3D791AA7,&HD215EA0D + Data &HED022988,&HD6A53E0A,&H8025A87D,&H060270C8,&HB9928BB4,&H4F202A41,&HDD19A40A,&H9621E777 + Data &HCD348D80,&H3DA695CC,&H40BE425D,&H2A440A28,&HC25EDC8F,&HF6DC33F3,&H32A112FD,&H91EF9BFC + Data &HEC36B8B2,&H8746F734,&H31949298,&HEF799402,&H96DF406C,&H145CC168,&H9757E6A5,&H3F0B264F + Data &H5C809D63,&H8FF92398,&HE260797F,&H20EC2E67,&H955BF28D,&H30148885,&HC8F55C18,&H23EA2E02 + Data &HECB2B1FD,&HCAD9BC32,&H647D08FB,&H070684E7,&H32572C9E,&H88441C79,&H7117AC7F,&H6EB3FA2E + Data &HF52403BE,&H38048F9D,&HFE9BFD80,&H6525A901,&HFE478A13,&H989FBC7B,&HFB347CFB,&H857401D2 + Data &HBEAD3615,&H2429FC6F,&HAF0514B7,&H2EE6EE68,&H50FFAD43,&H9661F63E,&H2D8C3016,&H36A4C626 + Data &HF0E48A4F,&H751BC4C3,&H946084C0,&H7546D39A,&H1BEF0EFE,&HA5174FC6,&H2233C80B,&HD7F4A593 + Data &HD05566E4,&H44C01D2C,&H2A247C14,&H99B99914,&H9D220AE3,&H51392AED,&HC97486E6,&HF35C563F + Data &H70FC974D,&H715AC2B1,&HFD5B1EF5,&H49EF6E9B,&H728550F2,&H656C67E2,&H72D98792,&H6D886C34 + Data &H090B0C90,&HA30489EF,&H3B13D438,&H1722785B,&HCFC92881,&H98068F8C,&H27D8B01F,&H4114AC4D + Data &H1161E53B,&HB2D0EB5E,&H7D8667F4,&H3042FBE7,&HFBCE0B0D,&H4134FB37,&H9C714080,&HC203CD29 + Data &HF0D218CA,&HD16BF74D,&H407216CC,&H4E7B78C6,&HF2766CC0,&H635FC864,&H59006E89,&HAEC9727B + Data &H42271142,&H9523EB0C,&HC325EA18,&HB0524BF0,&H2127D12D,&H2D5AB0C3,&H2C8CFDCA,&H999F2143 + Data &H711A8ADA,&HD5DB8E2E,&H88697D5E,&H2F8D4D33,&HBFB9B2A3,&H84B3C220,&HE684FAC8,&H39CD23DC + Data &H05F8BBA3,&H237EC4C5,&H3F77C00F,&HF2882BA7,&H207FFBDB,&HE3276FD3,&HCE0DC6BE,&HF7039591 + Data &H9C0136A5,&H64969682,&HB60AE241,&H1CDCEFC5,&H4F6CACDF,&H39CA1BC4,&H2A40ED06,&H7CBCAEBC + Data &HF9095F31,&HFD472067,&HF2000565,&H537C006C,&H1E767ED7,&H84F09F6B,&H147F69F4,&H89BD5D38 + Data &H30E13BEB,&H6AA873B6,&HFDE4185D,&HD57C10DF,&H8D8F07E1,&H1C3F12AD,&H38CD7B06,&HBEF52A59 + Data &HE2BBF76B,&HA8F30A51,&HD31AB4E9,&HA94AB627,&H5C7C1E55,&HB9B4F653,&H360384F4,&H8310408C + Data &H74B99311,&HD7F70053,&H32B34584,&HCA5E3F8C,&HE81B3796,&H957FD61F,&H5F9867C7,&H07CC256C + Data &H73AE7E59,&H692727D3,&H37E75D40,&H1B6F9AF9,&HCFEDFDA9,&HE65962AB,&H9F157344,&H4EE32EFC + Data &H7526A8A0,&H92E460E5,&H70378C68,&H71FD2CC7,&H77B8B12C,&H0CE5D5F4,&HE96BFC56,&H08608CDC + Data &H0D98BB56,&HF0013081,&HD3965CA0,&HC4A23F51,&H7F8D74F3,&H8EDEC370,&H41B3E27F,&H13A389ED + Data &HDB5AA3DF,&HA59DA2CB,&H0398BE9B,&HBF3A3649,&H2EA7DA0D,&H86FA951D,&H313EA2EE,&H816725E5 + Data &H600FF2DF,&HF62FFE60,&HD8AD326D,&HF7251415,&H24A8A47D,&HE0866438,&HEADC51F2,&H82D38D64 + Data &H8544CFF9,&H48332DB7,&H27E616E2,&H351A6A0F,&H850A11DB,&HE67FD530,&H5299A4B7,&H34B7DC15 + Data &HF698527D,&HC2B255F3,&HC70D6617,&H3F8E68BF,&HDCC1BA69,&HD2142185,&HE86D4922,&HA760C05F + Data &HF8375061,&HE5C341B7,&HE334FAE3,&HFB0F3592,&HF210F974,&HFCB713B5,&H36E18582,&HDE2AA88B + Data &H8A65F8CF,&HDD385FEF,&H7898D50B,&H890745F5,&H4AE552FC,&H2593C2B8,&H78191868,&HF67143E2 + Data &H50F60AF2,&H8D43EC40,&HFF6F427B,&HB1C440AF,&HB9CDDD1A,&HB2FDDC66,&H54D371B5,&H3561C843 + Data &HAFA868C9,&HEE47AFFD,&HA95FD839,&HFB8FDC77,&H77F8012A,&H21E550DA,&H5D7444CA,&H94C50EDE + Data &HF14F2187,&HB0ABB104,&H2834BFE3,&HB8D692F2,&H14F0D9AD,&HD1B592DC,&H7B27DE70,&H24582425 + Data &HD7625675,&HB0E7A734,&H0F59BA35,&H075801EA,&H46D9EC60,&H89D1B816,&HE8746482,&H4C6E44F9 + Data &HC6CE5CEF,&H3D3A4BE4,&H9CC338DD,&H424F0171,&H09720173,&HCFBABBDE,&HCDDFB0BF,&H2D2B4CC6 + Data &HB2601249,&HA639AFDD,&H61AF78AD,&H34E45673,&HAB4979FE,&HB4597603,&H85B4E4A5,&H03B877D2 + Data &H69BBA64E,&HE6A36D3E,&H9CF085B6,&HAD294F19,&H7F31F42B,&H937F3BEB,&H44AE6BD5,&HE9A0A473 + Data &H913A37DE,&H1A43B2ED,&H5C34C6FE,&HA5A45B08,&HD4D5193C,&H9C94EDE6,&H1CB12B33,&H3625DE19 + Data &H8777125D,&H7DED8A48,&H4BFF1F67,&HD167FAB3,&H4062CF13,&HFAD49A7B,&HE9A606F6,&H078E6A21 + Data &H2FC8D606,&H7ACCBB01,&HA1D023D2,&H7F8D210A,&H80509B00,&H361DB9B8,&HE7F84AA9,&H3CFF9CDC + Data &H8D47F123,&H6A3BDA43,&H3FD30E46,&H53E03CC7,&H727DC7BC,&H0262CBEB,&H92DFE538,&H1A98E583 + Data &HA003ECB7,&HB9030BCF,&H5E6176DC,&H5222B2DA,&H21013D08,&H1CB74953,&H83F109A0,&H3EC0A540 + Data &H784E0C9B,&H43EABD21,&H35DDB771,&HFC3FA8A7,&HA2864EC9,&HBD5D0F13,&HC96496AE,&HB7783D50 + Data &HEC84E66F,&H77E15449,&H8DF2E250,&H4D669CBF,&H4667ADCF,&H9E9A3EB6,&H8AABE7EE,&H03F92402 + Data &H1205C1EA,&H8FD1D3A6,&H9B30A96D,&H1E4A387D,&H1CE99FE2,&HE7E07D4E,&H2F14AB59,&H8DE8CBAF + Data &H967C4C49,&H52E64ADD,&H53F1CE4C,&H2AAC9B9B,&H07AEE35F,&H6366134D,&HC558F442,&HED81E903 + Data &H37FB6C8C,&HFF29F94E,&HD7739BC5,&H142685CA,&H3D2AC152,&H9F0B09F2,&H586986C7,&H99569238 + Data &HFF30B374,&H98FB28D3,&H516AA080,&H6158F089,&HFEB6FC46,&HDA1C37A7,&H2DC5415F,&H0F7B8D95 + Data &HEE04BDA4,&HB4776105,&HD9BA1471,&H6410BC5B,&H9C28F037,&HA96945DD,&H1EA6519B,&HD33B5073 + Data &HC9344DDE,&HFDB8B0D5,&H6F4C84B7,&H891E66F8,&H67716D57,&H8B06C622,&HF08AC19B,&HD4000B65 + Data &H5DCB69CE,&HAFF8C60F,&H2F5744E3,&H0AEFC70A,&H80D9B583,&H1A7C9F75,&H597B028E,&H17602617 + Data &H831DCDF0,&HE16BCDA2,&H9A890CF1,&H7E0D8A34,&HFFAD3AF1,&H795D9AA8,&H4F5F5D87,&H8BFA31C8 + Data &H58965744,&H77DDB8C9,&HD2E7BD0F,&HCEF27A50,&H66489D1B,&H13A374E9,&HC5E061DF,&HC47C4F66 + Data &H1D25ACFF,&H31A3AB17,&H45218DC1,&HCE21E315,&HEEB316B4,&HFDB0B9EC,&H0F290432,&H9A07E53B + Data &H4A5752AB,&H00D6730D,&H7F1024D5,&H49A4F5E9,&HD5E4FC7B,&H8F5BF31B,&H1CCD344B,&HCA7E9538 + Data &H726929FB,&H703E3C38,&H7E9675D8,&HCF724987,&H02259B94,&H1922D1CD,&HA8A1B185,&H95B2EA83 + Data &H1FDCCF43,&HFDB18A37,&H9517AB65,&H8D861A57,&H5EE4A3F3,&H222AF566,&HEB4A977A,&HA84F5F13 + Data &H00F7C001,&HB53EE180,&H7ED42E13,&HED6C8963,&H4ED8CA02,&H8D4F4B1D,&HAFEA06D5,&H179A53EA + Data &HC6B96991,&H2CD23292,&H3FC53421,&HFB6BE996,&H6C13FFCD,&HF91C27F9,&HA2345CDF,&H16250C9B + Data &H8C86A8D4,&HCFE5EA5E,&HD12EBAB9,&HF9004E24,&H60E6DC82,&H8B203B1F,&H7E121E84,&H83E833FE + Data &H04B7887E,&H0E029364,&H1E198992,&HC35C263E,&H1FD7BFC2,&H3D29F195,&HC76A8658,&H43AFB560 + Data &H5F28A0D3,&H284CEB2B,&HD79CAAED,&H3E6F8374,&H33015E57,&HF9E06773,&HE5875C20,&HE340FFC6 + Data &HEB5C119F,&HACEC3A83,&H910463E3,&H2A8CF245,&HB4D2359B,&H69603082,&HB10BCBAB,&H5760B063 + Data &H142A7C61,&H29078C59,&H2C66B795,&H742667B0,&H2CFFA695,&H5E97DAE0,&H9C53539C,&HFE52FA9C + Data &H4BFD8AF4,&HD8AF3322,&HE4F57410,&H8FC1BA6E,&HBC979DB6,&H94F78390,&H385F117D,&HCC2028E7 + Data &H40713794,&H797CED51,&H198DC0A1,&H3B6A58F4,&HFCF2FD55,&HE2D11DC2,&H0A7B60C5,&H3A97C273 + Data &HFA8A30CE,&H80C7D620,&H15814127,&H0755CBE3,&HD8487EF6,&HABBC6E99,&HB0C43EF8,&H80265AAA + Data &HB3A932CA,&HBA68BE2B,&HB4777968,&H31EDF3E4,&HA32CD247,&HBDFAF14D,&H91CA4C10,&HA6DA6DDD + Data &H6E27C5A1,&H7C9BA863,&HB2682C90,&HEA904E56,&H766DD901,&H89CA6292,&H1931DA0C,&HF8BA387A + Data &H0033022E,&H4B4E32DB,&H195E0CEA,&HF7A4E373,&H6C6E5657,&HD0A3CAC9,&H04FF066E,&H0B687CE9 + Data &H47FEFA4A,&H70A254DC,&HA1179AB3,&HDA98472D,&H7A5A9B3C,&H3E62B794,&H7DAA7E10,&HC0E49C7F + Data &H88EB6B6D,&HE980BC5F,&H4F873376,&H74C7C86A,&HD9820BF1,&HEF5E3CF0,&H3ADBABD6,&HDFC2455A + Data &H576E28A2,&H5B55D8D8,&HDFD345C6,&H6D94EA0B,&H8C82269B,&HB72DB9BD,&H4BC2F825,&H14CA78C5 + Data &H2596DBFC,&H3A73261B,&H3B1384A1,&H5006B421,&H51A7C889,&HCE75DF28,&H1C804D62,&H0B670189 + Data &HD9BEB2EA,&H5A95F641,&H98158FE6,&H4BCE6E84,&HBD65411E,&H411E2036,&H8A935035,&H2A8FE523 + Data &HFC341C5C,&HA57E38E6,&HDB951204,&HCAFA0322,&HA68AC391,&H2B4BC262,&HEA6FBE85,&HD37C47C1 + Data &H7C13AE23,&HE69F9437,&H9FF5EF9A,&H80019829,&HB6B64F56,&H93A9EFE1,&H507773B4,&H178D0DAD + Data &H063DADBD,&H3F153C81,&HF04BCB59,&H6E181A2A,&H173378F9,&H78B50060,&HA648FA79,&HFCA4A6A3 + Data &H7143E0D0,&H1930F36D,&H2FD92F2C,&H024E3E09,&H6281390C,&H0C00EECF,&H02D5689C,&HCF401ED0 + Data &HD451A6E9,&H4F6CCD2C,&H6FC6D17D,&H1C6369F2,&H4D44C6B7,&HAB6199FB,&H4BC81EC3,&H963C4177 + Data &HF70FD8C5,&H7701D261,&H0F2C0760,&H92EA4E81,&HF4911C05,&H44E386E3,&H86706B2A,&HD3A8E992 + Data &HCB817482,&H4CC81A75,&H67E8CDB3,&H1D5F1737,&H1FD5B570,&H14D2A3E0,&HCAA6C11C,&H050CC154 + Data &H38735D48,&HE7D6B7F2,&H360C6D44,&H390C0082,&H0E7417C1,&HC05E007D,&H9CB603E4,&H9897E046 + Data &H4B836C00,&H64C93A05,&H53DB1200,&H57AF6A00,&HF1CD2597,&H7E4DE11D,&H3DEDC923,&H67D1E743 + Data &H2EF234C8,&HC6088C16,&H4B0100E6,&H1B4A6055,&H484D38C2,&H7F155588,&H5FF77478,&H5B419999 + Data &HEE34B067,&H527F2B89,&HD8D3B063,&HA4FDC88F,&HB6E2E3DC,&HF0C2CE2D,&H0B9B01DA,&H92CF07D8 + Data &H1B4369F4,&H08FE6326,&H9EEF3D1E,&HA0F8718A,&HB49DE999,&H8F06DFFB,&H9CD0A35A,&H2EC6D224 + Data &H98A05400,&H9005B63F,&H2B31A97C,&HECDF15E5,&HF0E4C0E8,&HC1DA1BE6,&H3BB7B3D8,&HD595D1D9 + Data &HDBE9DB49,&HE6EFFAEB,&H3DF26EBF,&H3DACF9A1,&H699D4FB8,&H3D5CE08F,&H4E9D849F,&H9BE66D7E + Data &H3B8E077F,&HF5B836EF,&H5E6E0D57,&HE86E7CC5,&HD7B76108,&HE09CF295,&H48AF7B87,&H9CDC2449 + Data &H0A081463,&HA5530E2D,&H0EA06079,&H3FA334AE,&HB5BCB5A9,&H723BEB7E,&HE326C913,&HC33776BC + Data &HDEEB518C,&H10B102C1,&HBCD6FCC2,&HF9FB4130,&HFAECD7CE,&H05A61C14,&H4B858C5E,&HDAA624C7 + Data &H95ADB160,&H54E9A90E,&H17FF09D2,&HB1056740,&H47DB075F,&HA02B2CB1,&HDFFB1F22,&H04E69431 + Data &HDA8491CD,&H46315620,&H3F8E10A0,&H01C13477,&HAD386F33,&H02A8E99B,&HD761A5A1,&H8D3FD946 + Data &H5D2D5300,&H22253498,&HEDE34C14,&H32A70502,&H6B7C100A,&H53D04716,&HDDF2A003,&H3722014C + Data &H27E96592,&H65A44013,&H11A45301,&H288D8001,&H7BC54DBE,&H53E2A7ED,&H72578668,&H4672AAD7 + Data &H815EE6F9,&H9BC5741E,&H9A0D050F,&HC5298FCD,&HCE5C10E8,&HCABC5F4A,&H90C0AD81,&H88206EBD + Data &H1F174972,&H2F43EA54,&H1252E060,&H0E1AFE06,&H06DC6DBA,&H4011B1F9,&HD5776677,&H4484C6AF + Data &H999B827B,&H1BB9B194,&H67A8CFD1,&HAA903962,&H6D653C36,&HD59CB07C,&HF574EEDD,&H490E7D87 + Data &HC3D283C8,&H844F99BF,&H9DEDBBC6,&H2CD474DA,&H68EDCB8D,&H001B443B,&H296E9AD8,&HF8E2E300 + Data &H4E0D4C46,&H605F0C60,&H5990EF89,&HA7A5890F,&H43446B1C,&HFCCDC424,&H14CF92C8,&HDB94B910 + Data &HE323FF06,&HC836DE1F,&H049ABE3C,&H36180125,&H089B174E,&HAC9823E6,&H21ABCE8A,&HC0526FE3 + Data &H3AE779C9,&HF4DC67C9,&HBF49020F,&HBAE3A2F5,&HE6CC6391,&HD5322215,&H99B46AEE,&HAC56FB69 + Data &H96241E34,&H43131039,&H64B84F7A,&H7CB25E41,&H53943337,&HC7F16310,&HCAC8ECF7,&HBFD66BDF + Data &H8A421E6A,&H6717A399,&H73CB3FA4,&H5D8FC486,&HFB720CA9,&HE947B142,&H4EE2EABF,&H2F235AE9 + Data &H3FAAA814,&H03F09A34,&HCE7C7A8B,&HD240BF1B,&H9402BBE8,&H849D30C5,&H35D4DE86,&H4F603222 + Data &HF93D8C32,&H4D2E38F6,&H3547056E,&H58A709D4,&H37B23E22,&HD47F3B56,&H94C87AD0,&H6DD8F17E + Data &H549D30DC,&H8683AFB2,&HA30DCAB9,&HF0AB9FED,&H142A9602,&HDB572AC7,&HD7BAA4B3,&HD3E3B76A + Data &H04B9329D,&H321F498D,&H5E5A4793,&H7EB2D344,&HB1799BC8,&HB5BB7E22,&H9D6BF719,&H012C4508 + Data &HD9698AB0,&HFA169D33,&H55C00E0F,&HE4D54ADA,&H74DD61A0,&H367F4CBB,&HA3CBF636,&H9677A398 + Data &H365CB2EC,&HB5F747B4,&HD89434B8,&H9CDF748D,&H9D732E91,&HC6E96C36,&HCD7B122D,&H5C54BB8D + Data &HF96EED16,&H7566E8B0,&H8E69BF31,&H8B9E3315,&H7509C691,&H7340F148,&HE5CEC9E4,&H040CC0A8 + Data &H18CBD533,&H2EC622D3,&H230B12A0,&HCE8F1B9E,&H53B7B4CD,&H352EE043,&HCBEA9E12,&HEE2E352A + Data &H0773DA2E,&H83F50771,&HEDF5007C,&H485DAF3A,&H7E45940F,&H27053768,&H8A0096EF,&H9A6B9579 + Data &H34135E95,&H9D2E0DC5,&HB06680FA,&HDA11E3EA,&H77DF85AA,&H3230FC9A,&HD1CD0B30,&HE174B124 + Data &HCEAB8943,&HE627B081,&H484C324E,&HDC88AD20,&H4495E809,&HE1C7C331,&HB6F44E27,&H903072D1 + Data &H043F3819,&H65B99490,&HC977993E,&H8A369043,&H9B1FD836,&H30A11F35,&HADD82211,&H09781C10 + Data &H2216A3C6,&HC1F99053,&HCBC787E7,&HCD24B993,&H39E37F76,&H76363D13,&HC7312413,&H05639A05 + Data &H3B26EF8D,&H0C6D56D7,&H823A91C1,&H08C5CA54,&H02E3BF72,&H0019F220,&H8D5F8A69,&H23C34636 + Data &H9F90C1BE,&H299B6042,&HF60038C8,&H30437FB8,&H22C19134,&HEE3D1F69,&H836E8AD3,&HD846306F + Data &H2A3C2046,&H210E8D80,&H19477F16,&HEBE4517E,&H253F0C4D,&H0A80B7A7,&H8167462F,&HFFBC7D2D + Data &HBC4D01CE,&H7A6DCE8E,&HBFAFA967,&HD1C21365,&H140A643C,&H08E1345D,&H1814543B,&HA611FFDE + Data &H65602105,&HC969D5BC,&HD62A5D32,&H71B571F4,&H94B9D9FB,&HA5EFF1CE,&H01315DE5,&H965C48DA + Data &HDFDBDA65,&HA6CCE749,&HE2628B1F,&HD8FD4C3F,&H4EC929A0,&HFB2099C4,&HE98DFA3C,&H3DFDEC24 + Data &H5EC6A69C,&H348A2734,&H42FF6DCF,&H326B0C43,&H114925A2,&HDD5B6D4F,&H95232E99,&HD75C82BA + Data &H7A2D7775,&HF79B035B,&H726BB695,&HDBA27EB8,&H6577FCA9,&HD4C2E7F6,&HAD18E8EF,&H10A51E72 + Data &HCFB42CFC,&HE9615FF5,&H37C36010,&HB4AEFD78,&H57CC7704,&H25D5B020,&H349950A3,&H570A3D32 + Data &H6A2D29BC,&HE67BE0F5,&H7D1B9573,&HDF7EE9FD,&HD4D36FDA,&HF23F9037,&H49D2AFD2,&H6BD9FEFF + Data &H795D8116,&HE205F627,&HBAEDBA3D,&H23B06604,&HEF560AD4,&H84C3FDF8,&H04F084B4,&H21857D3A + Data &H14F8EA6F,&HC79A0210,&H2A180948,&H3A211A69,&H9CEFE1F6,&HDC3EE22A,&HE1A0EBF8,&HD863B784 + Data &H6ADA9083,&H88F5F4ED,&H7F50B0C0,&HC1B99BEE,&H30816447,&H31768BD1,&HDF5A3BAC,&H5DC5720F + Data &HDAE51254,&HF655A105,&H51894465,&H1332E9AC,&HC84F6CDC,&HED1596ED,&H46BF4372,&H40238079 + Data &H11023A05,&H64F7148F,&H3B2BEF73,&HD2B1F95D,&HD53D7EA2,&HFE8EF4BA,&H6BF14EFC,&H7C872A77 + Data &H694E5704,&H3BDD8160,&H7973925C,&H3C1913D5,&H37BBD4EF,&H4EE54E75,&H7E75EE1B,&H16AE7793 + Data &HC8689A92,&H7FA2031A,&HA58F4D64,&H10524305,&H155743B1,&H14275D57,&HD80074F7,&H83E4E808 + Data &H2C4806A1,&HFE0B3371,&H8EC35B48,&H6F19FBBD,&H961DEE99,&HAB679088,&H7A90D44F,&HC3403B49 + Data &H7347F555,&H6E44FFBA,&HF354F163,&HED786C1B,&H362BFE1E,&HBB21AF1A,&H48AF0BF1,&HCE77E075 + Data &H1B1B5479,&H591761BA,&H81875457,&H1D7DF642,&HE2A80049,&HB888B860,&H2B5917ED,&HA5E0B495 + Data &H8A6D42A0,&HCCADF9CD,&HA85E950E,&H76CC8B1F,&HF8866F7E,&H2BACEA7E,&H53494C2C,&H8E338690 + Data &H1DF091E5,&H0B4F07F0,&H15D41BE4,&H6E0DD53F,&H781CB525,&HB8539EB7,&HB5B1D1DB,&H4FD37034 + Data &H26ED9D24,&H8DD7CEB8,&HA7525194,&HA5A37EA8,&H3513378D,&H00AE3522,&H8F7E3EA4,&H65289D84 + Data &H39A46DEE,&HAAB8E8B9,&HCE55B5AA,&H6198BA9D,&H4425E1F4,&H14235B3F,&HDB7A9C6C,&H3AC9F5A2 + Data &HEE49DDB9,&H2D706E3D,&HE65C9AA1,&HC0D6F387,&H230204CC,&HCD2B1376,&H99AD967B,&H93CAD214 + Data &H688D2A3E,&H637247CE,&H9A181FDF,&HC2A19062,&HA5DFE8C0,&HC21DA938,&H4C9B6C83,&H5CDCDA63 + Data &H952BFE56,&HDB0A5B2A,&HA98B5FAF,&H89A7DD32,&H28FE4A0C,&HB398D0FC,&HE2DAC0A7,&H7D31FBE2 + Data &H83F45A68,&H6A87FE34,&H259C8DA7,&HD4ECFAF5,&H31641E3D,&HA99CC974,&H84DAB7C6,&H6A170818 + Data &HBABD14BF,&HE3CD4367,&HE68F6326,&H5BEBDF44,&H8EB8A0EB,&H1A6159B7,&HA264BF4C,&HA5316240 + Data &HDC736938,&H5BB654A0,&H25AB6F2C,&H92C0E66C,&HF83F0276,&H755AB63B,&HA46063EF,&H7A9F90F9 + Data &H7611C42C,&HB6D92858,&H2733B8F8,&H144F93CE,&HC3D9185C,&HB85E2B13,&H4EF821E7,&HE045604D + Data &H695F834C,&H6F69C5F1,&HCB2C99CD,&HC6F04C25,&H9B7B9307,&H28018172,&H86D9A7D6,&HDD72ADE4 + Data &H44CA0E5C,&HD0AAA9D8,&HB7F5A8BA,&HF13630C4,&H7E8CC7D2,&H29B8793D,&HF439E38A,&H07ABD838 + Data &HCF742D51,&H07EFE761,&H4F4C387C,&H91344E98,&HABD14C10,&HE8BB4418,&H6667393F,&H02EBD5A1 + Data &HDAF5F18B,&H19EC9B66,&H21D5E0CB,&H4C21B1F8,&HFD370BC2,&HAC3B8951,&HC3103BBE,&H77819AA0 + Data &HDB914F6F,&H1648E001,&H1FEDF633,&H585AB86D,&H3E71415A,&HE1900759,&H4461BD00,&HC3609427 + Data &H9E8A5C13,&H16F0A1B8,&HCD1C7073,&HB90BF801,&H1D0CFB76,&HC0302373,&H0047EFE3,&H9E32754B + Data &H047B1359,&HD40DE216,&HD204598A,&HB89F9DC6,&H3D1CA24D,&H0C17CE6E,&H45BABC49,&H984E75D1 + Data &H7EB5586C,&HB5A60829,&HF3010A84,&H7F5917F5,&H26DA5FDB,&H40C379ED,&H6BD3D12F,&H2FD46AB7 + Data &HC85BE83E,&H87C39006,&H258B472A,&H34D6A9E0,&H6732B900,&H5EDB39BD,&HD6693CD0,&HED32B32E + Data &HE0D86AA1,&HD8024C95,&HDA09C07A,&H4EE11053,&HAB29CCE3,&HF5FF6187,&HD9BB785D,&H1B4F8250 + Data &H031200E9,&H4A06024F,&H5ADE7AD3,&H5B73E32F,&H85A23A81,&HA41B2008,&H8BA67554,&HBB5A8480 + Data &H35FF6865,&H604BB3FF,&H9AD38526,&H42CAB496,&H305E4C2C,&H27AC3A10,&HBB233C5A,&HBC918D5A + Data &H6EA791AA,&HB524C439,&H828FC365,&HA5FAC24A,&HEC272690,&H717C9FB7,&HFCEFFA2C,&H0B8CFE34 + Data &H28D41F85,&HFD2B0304,&HC3D29780,&H436E5638,&H94F8AF3B,&HF8EA34EE,&H2BDBDDE0,&HF333BE54 + Data &H77CBF11A,&H75D8291C,&H328115FD,&H5ECA0E8E,&H6EA8D2BE,&HAF956242,&HEBC56606,&H34B6B9A1 + Data &H4F73E4C6,&H9F9F844A,&HBA888E96,&H1987FAB2,&HE8DA8EED,&H1E95D234,&H827F7CFE,&H6F73713F + Data &H197C8537,&H1CF71881,&H044229AC,&H6F580F6A,&HB7BD5BEE,&H6F51C817,&HD4640A30,&H7A870C19 + Data &HFC2C96C4,&H4F0FD435,&H4BA9A3E3,&HA0C61AA8,&H624C6968,&H677624E6,&HFF5B7AA2,&HADB347E1 + Data &H097C64E7,&H1E11A101,&H8E6A686E,&H62C37C09,&H39908D2E,&HD1FBE90C,&H5E05E12B,&H6A50DEFE + Data &HE9F34CBB,&H67E379CD,&HD5DF2D9A,&H9AEC76D2,&HDE361096,&HEB64C346,&H36ABFD4D,&HDBC4F0BC + Data &H54D1B4B6,&HA5765C02,&H085688DB,&HC1351C05,&HC23ADF6C,&H7C21F4E0,&H99FF6D53,&H3CAF3DE9 + Data &H0D961764,&HA44A4537,&H8FA5D9FB,&HB7D210AD,&HD7794AF8,&H591C3826,&H6D20132B,&HD96FCF59 + Data &HC430F72F,&HABFAE7FC,&HFB9BC5B9,&HA4E47125,&HA5CEA93F,&HEAE51BE5,&H768DA6D3,&H4387E5DD + Data &H881020B7,&H98D853EA,&H6DD36042,&H1D2B1A4D,&HCCC9A677,&HC522B2D5,&H0DFB92B9,&H3035BBD2 + Data &H21380AF9,&H9E509251,&H4F91E9D0,&H118BA7D3,&H2FCE5426,&HCAF46427,&H0A57DF19,&H5897A029 + Data &H8E6D4D94,&H6A513DBD,&H2E465F44,&H345E7049,&H27F7D37A,&H3BA6ED1D,&HC46D5127,&H6DEF87D0 + Data &HB5F74B0B,&HCF174EA2,&H7FA89F93,&H4966D33D,&H8F4C0FA2,&H9913A4D3,&HA5911703,&HBCEC8229 + Data &H7072DF4F,&HEEA139F0,&H63058726,&H92AA1127,&H71B47FA9,&HADE2D9E7,&HF80A2F0B,&HA4A613B6 + Data &HF1007B47,&H5E9CA6FF,&HFE74B59A,&HC5F75465,&HD988FD75,&H8BE9BF22,&HD1359295,&HB5C741DD + Data &H2F8CE5C9,&H02153F6A,&H11CF81A1,&H0F2DBD29,&H7C9F3693,&H956D6BA3,&HA5C0B8D0,&HC13F18BB + Data &HF90AF8C0,&H2F2E40B8,&HEA70CF8F,&H75CBF801,&HD8E05E35,&H60BD3EEC,&H5FB7DAA4,&HE16616CA + Data &HDD256B83,&H61754191,&H821186A7,&H71A729B0,&H8D3C4121,&H86669661,&H39A261E2,&HB0EFD475 + Data &HFA05CEA8,&H997D205D,&HEAB66DCA,&HFC5532CE,&HB5F79489,&HEECCF879,&H8807A963,&HC2579EFD + Data &H6D33261D,&HC3233113,&H97F5BC14,&HDB366325,&HE04E0EE3,&HA79113E5,&H59614746,&H96254362 + Data &H25D38CBC,&HAFEE4229,&HEF4467D7,&H5D42787D,&HCC1114F1,&H4357C1DD,&HBB5B2B4C,&H5CBD6357 + Data &HA9CD1EF6,&H8147F324,&HA6280D0B,&H2730B0BD,&HA3F13E7D,&HBC79C964,&H4742A8D3,&H047F5BFF + Data &H62D879B5,&H57B5DDF4,&H1BF0BC93,&HE75D42FC,&H311EFD71,&H7BFE1DDD,&H9D88EAC7,&HE4A180C8 + Data &H97EB42D0,&HEA8E5E39,&H7B8FF6C3,&HC6724303,&HEC97D409,&H7D0BDC64,&HA4797ED7,&HB1B82B41 + Data &H22115AF3,&H9CDC1FF2,&HE010FC2F,&H4A671B5B,&H78CC8433,&H32C7992B,&HC33827E7,&H6CC43B0E + Data &HE9EB783F,&H4E6E4A55,&H02456B5A,&HE5232311,&HE63B770E,&HFD8D1108,&H859125BA,&H25BC34C5 + Data &HDDFE4F78,&H92B75E00,&H4F645DCE,&H3D3E402F,&HEA58FDF6,&H3AE05A2A,&H5A8F591B,&HE3D471F2 + Data &H7029E049,&H83AB707F,&H6FC46E47,&H529FC6F0,&H8172E810,&H3516281E,&H555DC157,&H7C578C9F + Data &H9F5D4C5D,&HBBA62677,&H721EABBA,&H8BB23644,&HCC2B3E2E,&H62954CCC,&H02986E72,&HCF3B317B + Data &HC6B2F6E6,&H4EC771BB,&HD0BB8AFF,&H05FE827F,&H86BEB225,&HE0FF9309,&H5C3EA587,&HAD39502F + Data &H0D50862D,&HCC2B09FA,&H383C1C2F,&H835FEE0C,&HCFEE6FAB,&H4235273D,&H05B486FC,&HB5D278D5 + Data &H92329286,&HB211A730,&HA40EF8CF,&H8ED9BCD2,&H73598F9F,&H45B52D42,&H8DA981CE,&H58EC044E + Data &HEA760CB2,&H046855A8,&H1EAE971C,&HA73CF486,&HFAEDBEA1,&H520F8F13,&H94D14BDA,&HA14F8CF8 + Data &H5A97B533,&H89509F1C,&H9C60AA18,&H78E0B2F5,&HDABCE70F,&HA6E0CDA3,&HBD403B6E,&H7E438E9E + Data &HD418D7C9,&HC37488F7,&H3DDA360C,&H24C4EEEE,&HDE9882DF,&H4260C9E5,&H9E995B25,&H406DFAE4 + Data &H8B20E1F0,&HA1FF919B,&H2287BC97,&H2A14C2D5,&H14D99FE9,&H34146BD4,&H8628FB86,&HFE2F2B34 + Data &H5D0ADA75,&H40191E2A,&HFCFA387E,&HC468BDBB,&H07140228,&HC276C1F0,&H61BA55A6,&HC9304E68 + Data &HA40F7A66,&HD4113C3D,&HCAD06446,&H59B8CDC2,&H987C8764,&HF8D22048,&H1DE114B4,&H317818AC + Data &H9F9C0CAF,&HB39468C9,&H8E7C2C7C,&H6E176AF2,&H7589FAA5,&H2D875F40,&H1A4E1D40,&HD2859A5A + Data &H5F19A365,&HB2D02ADB,&H5E3EFD81,&H4BCD5B4C,&H5DAA7F8E,&H3E62095B,&H3587B755,&HB735F4F5 + Data &H485FDCDF,&H5F6D5323,&H86E350BA,&HE1156538,&HEA65D3EE,&H41EF33BD,&HB276549C,&H5E3FC1CA + Data &H772F7C3D,&H9C1E5CA9,&H73E03DD4,&H7904EB2F,&H3971DE16,&H7F3C5C87,&HED66902A,&H3F178255 + Data &HB11724BC,&HD8F0DA2C,&H9E540F4C,&HFDB07FEC,&HDFA4E73C,&HDD566B56,&H59E0635A,&H307AE8AF + Data &HA8149347,&H1DF558B9,&HB81429CD,&H0F44EB44,&HE4964F08,&HD82BAA7B,&H6429BDCD,&HAB9E9540 + Data &HBFCDC6BB,&HEDD9306F,&HAA7BEB1C,&HA39AC185,&HC49581C0,&H08FCB2F0,&H3C34E676,&H266B026E + Data &H04ABE072,&H215C08A0,&H8BD8C0A5,&H1205DDBA,&H5AD652BB,&H27152B3A,&H794CC137,&H0A250572 + Data &HB12B4AEA,&HFB9BC49C,&HFBC98F59,&H9DF7D09C,&HCC43C2B1,&H935FF439,&H5192AFD0,&H1B762608 + Data &HB6250828,&HF9F5E957,&HDC069361,&H9109D537,&HA95CDD16,&H2ABA2AA9,&H574D1FFA,&HFB84D61D + Data &H4888477D,&H1BA4A308,&H5A4043A4,&H53A4443A,&HA5690424,&H7746104B,&H5D21DD2A,&H07C284A2 + Data &H74A8B486,&H46904E48,&HDB061A40,&HFBF985FB,&HE33C0FF1,&H6D9F7679,&HE7BDEEEF,&HCBCE73DE + Data &HB6D5BB52,&H3ABE9C5B,&H9CFC1702,&HCA33260D,&H91C09362,&H56FD7CFD,&H27D8A049,&H6C8EE136 + Data &H53298D49,&H93DBDD0D,&H85F79327,&H08647E7F,&HECC5721B,&H42C99465,&HC4F3D7E1,&H892292B6 + Data &HDEFC6B19,&HBD1BFA70,&HBA45F877,&H910CBEDE,&HB4B2DB64,&H9653B6EE,&HE1FD5B9B,&H5EE50C58 + Data &H02F3B9CE,&H25516F56,&H4BDAD06C,&H940AFDF7,&H64695C01,&HFCF8F069,&HC9F2E75E,&H4CDFC579 + Data &H3A9EB96F,&HBA1D484F,&HEA7257E8,&H3BE95FFB,&H95CA6EF8,&H1884836A,&H7421A0DE,&H6404C9F3 + Data &H8AD93950,&HA08BE6A5,&H064EC7CE,&HA10AE4F2,&H3980B22C,&H9B9EC5BC,&H9101E194,&H64E6BE3F + Data &HDF129C30,&HEABC7E7B,&HB0AE6A50,&H703055C8,&HA9815C4F,&HB58A7870,&HB2E0B396,&HF4E55732 + Data &HE58213AC,&H11EFB60A,&H640FA6DD,&H1AAD1BB5,&HFD2BE414,&H77108EBC,&H18ED516C,&H8EBEE0BB + Data &HB85640E3,&H0FBD7035,&H30652C56,&HD4162742,&H10AEAAC2,&HC08FA6BC,&HF4303DD1,&H2E016AA5 + Data &H9BF260BA,&HFD5BF683,&HE069C6D8,&H2C9457C8,&HA1855272,&HD7484924,&H7E9B3568,&H0D4BFA38 + Data &H6919BE31,&H6E3B3C78,&H9B733312,&H88C30AFC,&HC95742B0,&H55B79BB7,&H4C406F52,&HE9B2DE3C + Data &HC0DC9250,&H2EC8B8F0,&H23F9C118,&H68856C52,&H8620BE56,&H5BD5464C,&HC8B333A5,&H82AF0AEC + Data &HB06B07E7,&HD6A99AF9,&H3F621CC7,&HC47CDC13,&H5E8B1D11,&H5F8548ED,&H36E6E98F,&H7FACA503 + Data &HF5E3F197,&H2EEADF13,&H64387F4C,&H69CA8138,&HFD46D5F6,&H56C59219,&HFC86EA28,&H94CF54D5 + Data &H7FED501E,&H325DD880,&HA361CCC2,&H46D66CF8,&H129BF3AE,&HA7F0F50F,&H77994738,&H773B1B15 + Data &HB07DEE19,&H9DDE7BB6,&HAD73CFAB,&H399A6BD5,&HCEF916BC,&H36E1FA64,&H143C8F7D,&H41113652 + Data &H68900BA7,&H96AB3BB4,&HD5C020F2,&H81EAE4FF,&H894029E5,&H500B211F,&H8E4EC4AF,&HA7F9F8EC + Data &H6D9A6BB6,&H1F018FEF,&H47F7CC35,&H8C3C8C88,&H8F5F5AF7,&H69E433DA,&H54F7C14B,&HAAC88A8D + Data &HA757A1C0,&H4DEFBE66,&H134CC54A,&HB884F2AC,&HD1AAFE51,&HF70D8D6B,&H9C197472,&H4806AE30 + Data &H9EF10DC1,&H8AA0E1D7,&H8C39553E,&HE56C12C5,&H3B61ECC1,&H608F444A,&HB59E7A63,&H63F0BB5A + Data &H14C662FE,&H6C8BE483,&H3C8E6D2F,&H8456DD4C,&H5DD09592,&H34E81CF3,&H1E4C9DE4,&H9A8C2C0E + Data &H07FAF4D0,&H9A0B2FC5,&H9BAE4D9A,&HB44933C7,&H978FC280,&H5037809D,&H65A7956E,&H7606C790 + Data &H9AD7BA49,&H9806FDF1,&H594E06FF,&HCD6F195A,&HB504A1C8,&H6061246C,&H47462F57,&H2EAAA8C5 + Data &H6FF8DA4D,&H927B87A1,&H175ECBAF,&H7B5FFCFF,&HFA489F4C,&HF738F19B,&H244FF2A1,&HE7DB5B2B + Data &H20513075,&H6A36081D,&H2D288451,&H66FFD6CF,&H321AF7D3,&HDCE93A15,&H9999922D,&H2A56DC79 + Data &H1FACC71E,&H29E2B8B3,&H8E252BED,&H03DA3C26,&H213D19EA,&H58E77910,&HAAAA8FD5,&H63EA59C5 + Data &H28BF9591,&H9FE24D8A,&HF4695030,&H76C7AB5B,&H3ED30DAB,&H4F37FF63,&H7246FCAC,&H1E93D426 + Data &HBE2FA312,&H8D8EC892,&HAAAEC645,&HAAAAC29E,&H8870E3A0,&H0B4263A5,&H159851C5,&H63A5B9D6 + Data &H0DF27380,&HE76EA147,&H6471A100,&HCAB3C696,&HC11D2F48,&HE3490B48,&H51D1497D,&HB90854E7 + Data &H44B603C1,&H99C4BDE5,&H9C331404,&H6B506977,&HF4BF60DF,&H5E3785B0,&H28F260B1,&HEC70BA8C + Data &H2F9F35FB,&H693B8859,&H6E5CFB5B,&H37D174BA,&H540B2DE5,&H54D8EABB,&H335F017B,&HC588F241 + Data &H80E27B5E,&H5FBE393C,&HA1586D32,&H46007A99,&HFF282A84,&HB03B927A,&HD41EB23E,&H467EE499 + Data &H74A7EE1B,&H1C8F0F83,&H4586BEF2,&H1EFBCFE3,&H4CAE6B5B,&H3D7D9D66,&H0487EA42,&HBD18890D + Data &H59252FB3,&H8999CE3F,&HDEAD1B72,&H321AE7F7,&HC8FAC6EE,&H8302A349,&H2C004B49,&HAA3BB3C6 + Data &H1A5FDB0F,&H882F87E7,&H3A4E780C,&H2F6B4BF4,&H559298D0,&H6A2B6529,&H591AA955,&H3E90166B + Data &H26C1390F,&H37442F9C,&HB27073B1,&H2A50C07B,&H8D69C138,&H935B668E,&HB7CAF85E,&HB60DB061 + Data &H0C11BA23,&HDA902F9F,&HC6F7EF13,&HA34FE52B,&HC3AC0B60,&H8E048180,&H2E546DF5,&HC1517A17 + Data &HFD76A6EA,&H0FAD2ECF,&H1F4E3D26,&HE6B5A6C1,&H3421E7F8,&H8718F262,&H9DA2E981,&H99EE7B19 + Data &H2E39AF18,&H5E662FD1,&HC6CEFF9A,&H845C09F7,&H80721AA1,&H78CE1B92,&H95CC9FC7,&H1AB10A55 + Data &HE973F747,&HB00FFED9,&H9A370FFA,&H706209D1,&H5777E063,&HB2303D5D,&H0C096E44,&H123C3644 + Data &H60DA76A1,&HCA5FADF2,&H44B50028,&H3E7ED17C,&H938E1363,&H997EE121,&H2D55015B,&HA7858E2A + Data &H4E42C644,&H4470B63D,&HA7F6C891,&HC39389F1,&H4CF0606A,&H0276FB7B,&H4A5B61D8,&H8506263A + Data &HB2B97B8E,&H31B78C3C,&H59144F9F,&H0C89ED1B,&HC4A7E0CD,&H36FC761A,&H204B58E4,&H83A1D833 + Data &H6AE0BD02,&H8902C535,&H871DC5F3,&H359E07D1,&H0C94C77A,&H91F0A746,&HB312458C,&H804BC90A + Data &H4C0DE124,&H3CD18185,&H0EC7BEB7,&HADAF40BE,&HA92F0E04,&H1AE8DACC,&HA183CB14,&HE94D2B7B + Data &HAB61E595,&H2F234256,&HFDC7EFD2,&H9E9B2BE3,&H853BCDAC,&H1091DA37,&H0F3A2B3F,&HF80F44D8 + Data &H46CD46BB,&HA646B39D,&HD57A59BA,&HF65535E7,&HB494E4F4,&HC1C812D5,&H553FA30B,&H8D2D928D + Data &HBD0D8091,&HAFD0DA68,&HCFB4227F,&HFA00B571,&H385239B0,&H032016B6,&H00FB431A,&HC3AE5C18 + Data &HC22137C6,&H21789257,&H4B8397F4,&H37B304D4,&HF8F721AB,&HDA6C4FC7,&H6DCCEEB5,&H3D41D822 + Data &H8B2F0BB6,&H3FDA2821,&HDF1C16C9,&H7AA75BF0,&HFFA1B99B,&H9BA48D99,&H5590B7F9,&HED395E8F + Data &H724B0AA9,&HC7A681A2,&HFFFAE88F,&H98F6A6C6,&H25C82395,&HCD3F6842,&H9ED5A5FB,&HBD59C952 + Data &H09EFF9F3,&HE76F6A82,&HF1B94CB7,&H438BC9B0,&H9ECC12B9,&H1B7933F6,&HABC3E7E3,&H77BD3DBA + Data &HB29BBD39,&HC29ECC34,&H8ECD22E8,&HE781C6A7,&H31111E85,&H9B5F5F5F,&H97861FEB,&H30D2BF97 + Data &H469092FD,&HAC806492,&HFD59DAD5,&HD2793F39,&H7B19682C,&HD1D6BD39,&HC1FB771A,&H9871D3FE + Data &H8E2AC971,&H7B5EDDE7,&HAE1A50CC,&HFEB888BE,&H2BE2F87D,&H0AF7D706,&HBF7ECFEB,&HFFCA27E6 + Data &HA2424C81,&HDE4E52CD,&HB88B09F1,&H1B9B19C0,&HDF6C86B3,&H9E589144,&H75C17856,&HBCC4075D + Data &HA58F5C7D,&HE10D7FC9,&H883BF401,&H48C26913,&H9084382C,&H54A2D83E,&H2841E67F,&H33F5B01F + Data &H24D03623,&H8890A978,&HC1190F18,&H9C2548C5,&H657FF600,&H6373582A,&HD027AAFA,&HB5CAE425 + Data &HFF8B6F4E,&HC15B446C,&H3E068F55,&H07A1D699,&H46566805,&HD7FDAB01,&H80C0FC5D,&HBCEC11F8 + Data &H4D454F40,&H5EA5200E,&H200B46E8,&H3DE6E6D0,&HCC0719CA,&H0AA6A68D,&HEC662E28,&H41FF683C + Data &H1F9CFBC7,&HEE75F238,&HA02D677E,&H7417BC1F,&H9CACAF9E,&HF3E14335,&HC3FD9477,&H374C66A7 + Data &H7E35B8CF,&HABA368E2,&H173D3F74,&H5BFB027F,&H946BD008,&H055D1583,&HBDF65500,&H3ED11F06 + Data &H4C38C656,&HC110757C,&H27306B5B,&H6F26937F,&H39BD3EBB,&HC137CCF3,&H2B09F489,&H65225821 + Data &HE90D6995,&H11123860,&H9F0F5247,&HEB02BAA8,&H6FA63482,&HC8EA63B0,&H20B1F82C,&H94243FC6 + Data &HCDFF49EC,&H43925C6B,&HF4F578DF,&HA385AC3E,&H4EB3963A,&HB4C53F26,&H669DA7E5,&H07DE231F + Data &HC2ADBCBF,&HF330479C,&H48214D90,&H298B633D,&H54162CC5,&H524060BD,&H403E2474,&HAE2F3762 + Data &HBD9F2B93,&H1D850F1F,&HF619F233,&H38FC75A6,&H1344FAAD,&H04FB56F1,&HCCA55D0A,&HF4C00322 + Data &H4B1D3FC5,&HB41237D5,&H3D6B46A9,&HAC16402B,&H8AFF6730,&HC6A2E457,&HA06966EA,&HB9287F1E + Data &HA4102D1F,&H3493828F,&HCE7E155A,&H7E09C3D9,&H1481E2D3,&H805195C6,&H2C5BCF0B,&H915364BB + Data &HB553B4EF,&H9B780FC6,&HFFA5696D,&H9B53E821,&H2750E97A,&HFA79705E,&HEDA42B27,&HA8006B9B + Data &HDCA1D675,&H4A17ECC2,&HB768E828,&H1C2E63C1,&H3A3BD142,&HB0AEF1DE,&HFEE9D375,&H89717109 + Data &HE3371586,&HED60746A,&HB60A3B7E,&H2A34EB17,&HA770287A,&H70A629C4,&HC38F8D5F,&H94E34CDB + Data &H7298FB74,&HD6BEBB97,&HF3BFADE5,&HBCBB8778,&HFA9A7764,&H7B6CF962,&H9E0BDFA4,&H9975BBA9 + Data &HCC08AF9C,&HCABE8A04,&HAF036AC0,&H6D0AD4E3,&H66C9AE2F,&H633961FA,&H1C5DA38E,&HA99F2B0F + Data &HDF2D4618,&HF6B89A5E,&H08D6CDE9,&H6E2B4C84,&HC2594F54,&HFF423A1F,&HB88576E2,&H93D8E784 + Data &HE077972C,&HD23FCFDF,&HCE654AE2,&H468ABE76,&H4326F3F3,&HCFC9EAE2,&HFCAD07F1,&H11443429 + Data &H1EC37ACA,&HAA7C180B,&HCAEE494C,&HC5271A8B,&HDC2A8071,&H55B5C520,&HE3FA112A,&HD14DF3A7 + Data &H9FB9B3B1,&H5286114E,&H306EB13C,&H7A40AC28,&H35838B6C,&HD3340192,&H6B760007,&HCBD841C1 + Data &H32C69441,&H07F4236C,&H53DECC4A,&H78B1534A,&H6C8E032D,&HCDE1C533,&HD2BE4039,&H38DA51A0 + Data &H17796CD0,&H677FB96C,&HD34BC315,&HA3BC3D20,&H6E52E684,&HBC13847D,&H0A73E9F4,&H108884AF + Data &H5EEFCE13,&H42764698,&H53E23F36,&HB80C94C2,&H516EEE0B,&H7D02BBD1,&H645B2749,&HF8F0D81A + Data &HEEFE8720,&H69AD4554,&H3A5C774D,&H98886CCA,&HAFACAF57,&HDDA0A639,&H6497A794,&HA66519C5 + Data &HB1DF6B1E,&H44ABAFE5,&H79CE2ABB,&H50FC612A,&HE83F419E,&H0E4A0CAD,&H1E38DA36,&HC3351627 + Data &HE82E0D06,&HDE9FC077,&HBFF50FD7,&H89EFD730,&H5D50C0E4,&HACDF4E31,&H0CC1A25E,&H9EFE9403 + Data &H0DC736F8,&HD7D92148,&H96055204,&H47429D05,&HDD32232C,&H012A44F2,&H8D6AE733,&HE2A9305A + Data &H7233043A,&H77AEA1D6,&H28A43E9D,&H23E47E00,&HF93077A6,&HB14C9BCD,&HF7719C0B,&H4282F6B4 + Data &H743936EB,&H1199D748,&H930002CF,&HC6D45B33,&H74885E2D,&H8BF51579,&H324EFF24,&HB63459CD + Data &H5F4343E9,&HE5FCC6AD,&H9FA1BEA9,&HBE3709A9,&HDC1B4F2A,&H94F4DDA7,&H5C0E3DF5,&H33FD37AB + Data &HCAF6172D,&HA7E8CE7E,&H17594211,&H3DBE0A61,&H47B9A3DD,&HE59AF7CF,&H4B785D5F,&H48540A75 + Data &HA7CB69B1,&HF6E8D178,&HCED6A84A,&H50E9B1C5,&H9C9BF9D1,&H7B0E2033,&H4D3A8014,&H57C17174 + Data &H9D85C3A2,&H09EB8FA2,&H7E23777F,&H9537A1EC,&H09699A6A,&HB933457C,&H79959FC7,&H07E6BFE0 + Data &H034DFFAE,&H6ED83A6D,&H7ABB124B,&HD9F56A7E,&H0B09A14A,&H1E452CAE,&H37461A9F,&HB1702195 + Data &H924E1FF6,&HAD5DC41F,&H4B58A768,&H601D6F1B,&H68E07276,&H87B51462,&HD6CC2941,&HD4ECEC7D + Data &HA3CA0203,&HE43E67F7,&H408F1B24,&H6170F53E,&HE23AA03D,&H90E9D12E,&H2B906687,&H5F5B3569 + Data &H52B61C54,&H403A14AB,&H4CCD2A9F,&H8A3D50D0,&H37859648,&H343E7AF0,&HFE30C243,&HA3C9F748 + Data &H18C04833,&H3EC98435,&H5EFDFCCC,&H7A4C0AE5,&H5E7C9F62,&H498BF425,&H9664E56A,&H0AC42A4C + Data &H269E0057,&H50932212,&H60DCD6AA,&H97C137A9,&H90B6D13B,&H325135B6,&H9B8FCE3A,&H70026AFE + Data &HAA359FD4,&H1BD6DF13,&HD9F96EDD,&HD4F11794,&H775D3588,&HB134B973,&H83D0C059,&H4087474B + Data &H9227D314,&HCE48210B,&H1C5D94FC,&HAC390C47,&H69C237E5,&H14210AA9,&H4EE2E09E,&HE6C98FA1 + Data &HFF5DC38E,&HEE918ED9,&HC6B55AB1,&H8BD73312,&HBA2559DC,&H39FAA3C1,&H38130974,&HC2E96E3C + Data &H99CCB82A,&H2A2E4D43,&HE393F684,&H518F6962,&HFA04644E,&HEF3A4D63,&H58204277,&HB97B0557 + Data &HAA1C3FF2,&H829F0C29,&HE2E8D674,&HB9B624CC,&HEABD7064,&H7F63E726,&H468B2FF1,&HE6C2C734 + Data &H7BA33D4C,&HAF8FA82C,&H02EFFC35,&H30221FED,&H25E9A7E4,&H7AC2A912,&H50397874,&HBFC5663E + Data &H19C4E8F1,&H6F8388CB,&H1099F26F,&H656D06E4,&HF93318A5,&H20D74D60,&H9F20DF47,&HB98FD198 + Data &H614F8FBD,&H2F5297D8,&H7B30964F,&HB2E87B5A,&H2FDA38D1,&HFF08F1B6,&H39C300EE,&HB3967E40 + Data &H330365C3,&H1D772BC4,&H2187E184,&H8A46203B,&H385D8CB2,&H252BE5C5,&HA3271BA7,&H2F9FDFEF + Data &HCC212046,&H707AE9FA,&H524AD11B,&HD8C7C8E2,&HD6AF6A87,&H9B6DEF9D,&HB4EE7C6E,&HCFE77FC3 + Data &HD83596F6,&H52322D49,&H5E7C6DEB,&HC454BB96,&H793E3FF0,&H35F75195,&H2FCDE6AF,&H7661C56B + Data &HCABB053D,&H54749FD4,&HAB16103A,&HE57825D9,&H0AB97E54,&H4C9E099F,&H59BEFD00,&H40D108A2 + Data &HA59D8775,&H448227DF,&H14C17205,&H6ABB328D,&H815556ED,&HC982FFA6,&HE48D7E6E,&HFA1843B9 + Data &H726DD4CD,&H252CBF9B,&HCFE5BFE7,&HBF0F9525,&HBE7D669E,&HD9E18AC3,&HACD80AC5,&H15665055 + Data &HEF70B242,&H79E61312,&H53BEA0D3,&H6523EBD5,&HD3BF8546,&H331B3228,&H3E9BCAE3,&H2548BFA7 + Data &H0B3FBB08,&H7E801200,&H62153A99,&H5FA5AE8E,&H0AEE50F2,&H752CBE05,&HAE322A52,&HB149BB74 + Data &H4D4B97D2,&H2782BE07,&H7839468E,&H255FC556,&H4C0C04F4,&H48B005A2,&HFE1A7DC8,&HEB3A1CF1 + Data &HA8FBCE49,&HB1D807B1,&HEFCF82B8,&H467D0020,&H6EED05D3,&H39929B7F,&H89D3BA7D,&H8FB1C02C + Data &HDB9DAB8C,&HAD69D2C0,&H98FFBAAC,&HE2B51BBE,&HF3E0E3FC,&H86DFA56F,&H4864B2F3,&H078643F5 + Data &H77623BCB,&H2D8B6086,&H5077A3AE,&H454E76B2,&H40EFE072,&HDB1F4002,&H1290CDDB,&HFDCEE72D + Data &H03233BDB,&HC284CF08,&H9428E31B,&H212CDFA4,&H1BEE1F00,&H58D82D3D,&H7862951F,&HAD4B67A9 + Data &H96FE4879,&HE83C067D,&HDD0AB78F,&HD875BD47,&H207283EF,&H0A96B682,&HD26821C4,&H2DA368A4 + Data &H30BC2700,&H41A92C50,&H508941D9,&HF7319679,&H1B91ACC6,&HE4EB443D,&HF2292B7A,&HBE7846AB + Data &H3AE42E19,&HD4341E8E,&HD12D0997,&HD0CC6CD3,&HEDA7FBFB,&H078FD9EE,&HD14BCACC,&HEC242C3E + Data &H93376CE4,&HCAF4F528,&H998AAF0D,&H49BF83F7,&HABF16D30,&HE4B7CE37,&HB14C3BEA,&H87723677 + Data &HE270A503,&HDBFDEF2F,&H908B3DEE,&H67EF235B,&HEFE2AF71,&H5F7784B8,&HABEEB70E,&H389527BF + Data &HA47857B8,&HC9D3E3A2,&H6F398CA1,&H3ED7A3A4,&H348DE173,&H769D8FBA,&H7616F95C,&H01D24427 + Data &H87B61ADA,&H6E97404E,&H02A46EAD,&H7BE4CB80,&HD276A90E,&HC795DBDD,&H4A78F79E,&H492AA9E1 + Data &H9CDC8904,&H37592ED3,&HCACC99F4,&H796E31B0,&HCA7A7B69,&H24D02D5D,&HAB3758FD,&H29BF9C29 + Data &HE07A0A14,&H83AEE597,&H6E63E289,&HC88271D7,&HAF3A4FB8,&H2BD8ECFD,&H4A273C38,&H57F9C15D + Data &HB21CB818,&H28663A33,&H10E08730,&HE1E29351,&HEE416160,&HDCADE8AB,&HA8801522,&H6EF24B87 + Data &HED4D9925,&HE655FA85,&H5BCB55CC,&HDE3E4CE3,&H4BF5ACD6,&H57F0500D,&HFCE6B663,&H4F8EE510 + Data &HACF96294,&H67BED10F,&H4A5257D1,&H699747B0,&H99271D78,&H80FF89EC,&H242929B9,&HB565C373 + Data &HD868612B,&H829021A3,&HDEFBDDA1,&HE7677CDA,&HA54079BE,&H7D61B7C5,&HAEFBD2AB,&HDC6F87DE + Data &H8D1AB9D8,&H8E349F4D,&HF254D327,&H7CC85A8B,&H31CEB6F4,&H32CBB468,&HDE6109EE,&HF249B92F + Data &HE49E34BB,&HE2BBB94E,&H60A19AE5,&H21DDA759,&HFB6DF7BF,&H19AA5085,&H58417E8C,&H32DE6D64 + Data &HE7D20AC9,&H13E4FCBD,&HCF656BFD,&H5AF50C32,&H76E758DB,&H3FCD8F61,&HC7EE3103,&HE13ED9EC + Data &HB6B8D13E,&H4995372F,&H25BFBF3B,&HA55EEE3A,&H70CA251D,&HE690E57E,&HA2ABA650,&HD2AE8C36 + Data &HC09DBC4B,&HF7C3059F,&H2FDA56B2,&H412E2128,&HF09FE671,&H320518C3,&H88E3AF18,&H98705343 + Data &HB1497BCE,&HD40DD819,&H15974897,&H8B5AFBD9,&H218A6A26,&HBE018090,&H4BE307A7,&HBFAF3D52 + Data &H69BB921D,&HB1D48AFD,&H8DED1323,&H35E4252B,&H01C9DC09,&HFC00C411,&H4740DD70,&HBD73E8BC + Data &H20E386CA,&H31EF88C7,&H20B148D5,&H9FD0A338,&H4182A619,&H38372361,&H123917E0,&H47C01F18 + Data &HD3BEC8A4,&H4FF0BF5D,&H1AC43F1E,&H22AA8F25,&H9B9DF46C,&H29BE8E2A,&HF1186F6C,&H76252FDF + Data &HD741F16D,&H6B720BA2,&H7692898A,&H8FDD5032,&HA30BD001,&H9F777AF9,&H047F582B,&HC19FF36E + Data &HE40ACF1A,&HBA105506,&HD4D8218A,&H01D241B1,&H3C9CFDE5,&HC4894241,&H8FEFB1F2,&HA6B01633 + Data &H1D7AEB8F,&H56349AC7,&H24CCFC9F,&HA602F9BC,&H6B217DA6,&H072C8038,&H70139D97,&H9B7B65B0 + Data &H62AAAACB,&H67359750,&HB1BA950A,&H58E4C0A4,&H089487B9,&H4175B2C7,&H5BE1D3A5,&H52995CDC + Data &H76507B15,&H6FA130CE,&H4474CB2C,&H1F0B7179,&H79A5CCE6,&H11A67E8C,&HDB0041A8,&HBB634F60 + Data &HCA800F7B,&H4C649A3E,&H807E837E,&H08933CDF,&H5580456F,&HFF0FE7FA,&H4A1ADE5B,&H9BA42A15 + Data &HFE96F55A,&H1D4531F1,&H8442CF82,&H258D838B,&HF3AD5C1F,&H673BBF34,&H9F5B9795,&HADD0B177 + Data &HF1195EE8,&HE636CD2F,&H2F08C92F,&H7EC0F656,&H28AF54C2,&H9627031B,&H5C8EE9C6,&HB02F5CAE + Data &HBE3E614C,&H79AF3C6D,&H81C2ED22,&H92269FD9,&H5E1D04F1,&H08BE27FB,&H2BC5DED8,&H549D46B6 + Data &H1F3B1153,&H4E4502A3,&HAB9B9413,&HC58C2F6B,&H5CF3F8FA,&H53C0D2E0,&H73AE73F8,&HA6AE0AA4 + Data &H32DCC0B6,&H4D37EA6F,&HCE1FFEC9,&HF115112B,&H93A6A37C,&H893217FB,&H880D0304,&H833A24D9 + Data &H637EF614,&H0F9E7CD9,&H633E88FB,&H83A79561,&H8538EBE8,&H2BCB6142,&HD144F3D7,&HFFCEBD37 + Data &HBA37FFED,&HB33F3BC0,&HAF5D7E2F,&H329ECBDB,&HFF5ED1CC,&H23795485,&H129A9F83,&HFD358D8A + Data &H2DD907A1,&H0186896D,&H814039C4,&HF6B45F53,&H1CBF5071,&HCC5B7500,&H9CBD0F96,&H66DBA66B + Data &H85FA090C,&HF63538EF,&HC4530991,&H2747C746,&H9289E662,&HE2513914,&HB6170BE0,&H29830A8D + Data &H4948CD03,&HDE3B2B3C,&HAE8915FB,&H0A6E78E8,&HE8A24775,&HACCD5FFD,&H1EE0EE64,&H80CE9EB3 + Data &HDDDD28BD,&H09F8ED3F,&H5B111FD5,&H60915DDA,&HD4017117,&H7EEF8F2C,&HA860035B,&H2CA00D54 + Data &HD24EB9BC,&HEFA599ED,&HEE878E47,&H43D6C45D,&H9A1B7A1F,&H0A25F461,&HBCD71AA4,&H4866D89A + Data &H4DFF2DD3,&H1C21C205,&HE31F5D72,&H49B1210B,&HB366F98D,&H93E92DEA,&H8B4FE0DB,&HDA88DCFD + Data &H2D3C0D31,&HC5FF5327,&H48374029,&H12827403,&H9FD9F375,&HC2F706DD,&H0BC77237,&H8B3F6A45 + Data &H393674BB,&HE90D5AAA,&H0CECD225,&H4ABD1E43,&H2ADA0C69,&H76D02E4B,&H9E72E8C5,&H08C31690 + Data &H9B95B8CF,&H11FB6071,&H05A7F3B0,&HA648734A,&HDAAF7981,&H3146B876,&H162B8C1C,&H74379B3E + Data &HDB2A2329,&HA41B6969,&H715A07BE,&H4D9B18B0,&H1ECC6EFC,&H2937329C,&H4F427CD4,&HA5147E15 + Data &H7CB7B114,&H2DB8723B,&H545F4F6C,&H4FA9F2F5,&H77BD1979,&HC373CE14,&HDE7AAE27,&HFD980AD2 + Data &HC79907A9,&H9C55CE21,&HB9505956,&HC9B70A6E,&H4252D8EB,&H8CCC9C2F,&H4CAA81CA,&H628E8057 + Data &H1577EAAC,&H7B88D96A,&HF472D9F0,&H2A421BD8,&HAD144224,&HBDFA3D68,&HC625D1C1,&H7BC8DCEA + Data &H6FBB9A96,&HF482E354,&H2A7CF7BF,&HDDF45278,&H70A93FEA,&HB33606F0,&H25FA3272,&HAA7BA98E + Data &H78312B16,&HD63C8C21,&H899613FF,&H325FA404,&H3C888C48,&HE6B06E39,&H970B3B24,&HE6AE3D86 + Data &HA3988E9D,&H11E3F8B6,&HCD737F03,&H66B7D898,&H3F0E8B85,&H62CA72CA,&H1BD9B939,&H143BC71F + Data &H37955714,&H7A599709,&HECFE7F71,&H9741FF97,&HA7AD863D,&HD243E432,&H8FB56C31,&H11786BE3 + Data &HB60450B3,&H94AA6E6E,&H9C75CE8B,&H6B6FBE24,&H074CF013,&H7DC8C708,&HF531E546,&HAD8F149A + Data &H1EE3AB6F,&HB4704DAE,&HC4A17CEC,&H4D6C73E0,&H6D90DE00,&H1B1535FC,&HEEC42E0E,&H42721B44 + Data &HBF29B8AC,&H04371BBC,&HEAEA79A0,&HA7355DA8,&H69DB9F29,&H0997AE60,&H60CED3EB,&H033FA834 + Data &HE1B529FC,&H9AFC6D6D,&H93B9EEF7,&H3EBFDA54,&HD319ADF1,&HCE2920F9,&H956ECA06,&H74839787 + Data &H0D165DE0,&H5E8EA50D,&H95970A7A,&H77165357,&H7786CA70,&H98AE7CA8,&H1C947945,&H4BFAC8A8 + Data &H0A74B96F,&H4907B5E4,&H1CF73280,&H10A8107A,&H0C8AFF69,&HBBB72524,&H1123D5C2,&H5D00C364 + Data &HCE3BC527,&H7F0DA84B,&H56CB3080,&H8466FF1B,&H92282FD1,&H406FFB66,&H831B954C,&HF1940D23 + Data &H43084373,&H70C49792,&H943E4935,&H097A5BE1,&H1B357125,&HFD80E849,&HF22123B8,&HA14E9A8B + Data &HE1555818,&H11371229,&HFCBC3231,&HB8185BDF,&HA35BCF3A,&H1014165F,&HC1B7783E,&H8F5CF1B6 + Data &H5BB57BFC,&HCC20E78F,&H47E21E54,&H3FC2C792,&HB76A3AFA,&HFAE1FFA4,&HF35B4627,&H4D8D1A8E + Data &HFEF65F7E,&HD3AD3116,&HB97C4597,&H8DEC8D80,&HE6402CDE,&H5FD244CE,&HAA8DB5E1,&HEB97742B + Data &H5DB15648,&H3177C8A9,&H7F727B48,&HAAD2F1D7,&HA9D3FDF9,&H89E65A19,&H997B428A,&H0EC73917 + Data &H049594B5,&H594D2FBB,&H1B610FED,&H9C7E13BE,&H353D4BB0,&H640FD974,&HE07E662E,&H39F03D0A + Data &H356A4B45,&HB9F2BB1F,&HCB838AC8,&HB0305B2F,&H0500F2BF,&H74ED8285,&H690F69D1,&H3DB142D1 + Data &HC1085A60,&H4AE76087,&H0A9247DA,&HFF4DCB55,&H14D0AC94,&H50EA71DA,&HC62DD18D,&H107A9535 + Data &H84B8AFC5,&H61310857,&HD2EAE90E,&HF9AC17D2,&H1CF2413B,&HB3E83FFB,&HC34C5B90,&H5C18B1A9 + Data &HBC1E2D0F,&HD812C1C2,&HC1244A35,&H91A6C18E,&H6AD1C40A,&H41D931F1,&HCDA3755E,&H14A9D892 + Data &HF529E4CF,&H48A9CC5F,&HBBD3139D,&HC4D58648,&H97669594,&HBC27B3A1,&HE02B6E2F,&H04E1AEB2 + Data &HBA93F3E7,&H1F6AA92F,&H9CD43267,&H33BC5A9F,&HD29FBD34,&H1EFB795D,&HDDAB0C75,&HE6BA4D73 + Data &H578A062A,&HE0AC3E18,&H955AB5B7,&HD84864EC,&H482057BD,&HF24FA549,&HAC5FDFF9,&H3AF85DB6 + Data &H054F9C3C,&HBCF21818,&H33FABEAF,&HBEFCE0D3,&H6C5243D5,&HFCA2B7B7,&H118D88C1,&H4F60D82D + Data &H84F09D2C,&H162CB97E,&HC4BECE79,&HD5F7A297,&H566EB334,&H42CAD4D1,&HBE667646,&H49FF53A9 + Data &H42C5EC0F,&H26FD6865,&HE3FF1FA4,&H66D5FFFB,&H9685D6BA,&H1754165F,&HCD3CEE31,&H82A5016F + Data &HD65ADFA9,&HC7475D65,&H7ACB3DCE,&HDDD4B183,&HC2204CF8,&H87387826,&HC40BFAA1,&HA620E879 + Data &H20C6C73B,&H90085D0E,&H97E28431,&HB043C07F,&H15D5D5B2,&HD009661F,&HE5615DA1,&HF131C900 + Data &H5491C1F4,&H6C4DD53A,&HAC12A9AF,&H59D87C7F,&H495DD3FF,&HAE26468D,&HF1E09443,&HC82EE741 + Data &H5F630109,&H1F08C582,&HB7F63A7C,&HBEFE68F7,&HD4697370,&HE418C755,&HB6C3441C,&HA7115878 + Data &H40BD97BE,&H34296A87,&H5C05A3C3,&H50019BF6,&H3D33D0CD,&H828E0C31,&HE1CF7D93,&H31C7A7CA + Data &H752F9DF2,&H661FF785,&H2F5DF642,&HE3AF33C7,&HFAF543E8,&H94CFD179,&H0456BD5B,&H7588A9A2 + Data &H4D15475E,&HAE6E0DED,&H6C815362,&H7850CBCB,&H7CBA1961,&HB9E0B437,&H43CE382B,&HD8B3D400 + Data &H1E83E4AE,&H5DDFD71E,&HEEE9BE05,&HA286AD80,&HFF7A0CD1,&H32E204AC,&H6E9A828D,&H4358C447 + Data &HE6C88EEB,&H2624BD2E,&HC1447517,&HC403FB11,&HEB4F6606,&H58B45500,&H69EC9236,&H561CFD74 + Data &HD6D10E8A,&HF45A5DD5,&H36053CB6,&H65B03071,&H71B5089D,&H3B07AE16,&H4B60946A,&H82286026 + Data &HDA793551,&H7A615699,&HF1D21980,&H942EAEE6,&H7FC973F7,&H18C0BAAE,&H81B175DC,&H584EC31D + Data &H26E13D06,&HF379873C,&HD5A74297,&H03A41283,&H1BDDC0D2,&H589F5FE7,&H5F7637DC,&H9ED3F072 + Data &H12D876DC,&HBB9F9B40,&HBD32E675,&H7E4A620A,&HD9D724A8,&HB153CCF6,&H875FD2AC,&H6F01A421 + Data &H064B9170,&H9113A64B,&HC7C07103,&HAAE88625,&H6616B0CF,&H05F1599E,&H303561FF,&HBC86C358 + Data &H1AEE8BD0,&H64ADBDB7,&HBAFDCB08,&H42D9F5C8,&H665A6BED,&H9BFF8A4D,&HDD4C323E,&H7892FD26 + Data &HB1489401,&HF9CF5623,&H60EDA177,&HA1E662C8,&H737219C9,&H82C92F50,&H497E3566,&H6DC9ADA2 + Data &HD92BBC36,&H5DB17C9D,&H9DF1D1DB,&HD8AA693E,&H4ACBDCD4,&H491F8E45,&HEF5FCA34,&H66BDB789 + Data &HC31DF1E4,&H4FD5CE2D,&H77D1F732,&H3629CAFD,&HCAA2EB3A,&H80DC5362,&HB04443DF,&H336CE180 + Data &H6FF19677,&H65B1CDAF,&H76C043A9,&HF5B52707,&H3787EBAB,&H9F3829D6,&H260DB8B2,&HF7631B13 + Data &HEBF93F19,&H6004D19B,&H3DC3A7D2,&H49EFAE44,&H50479650,&H05510060,&HB91630AD,&H2FE7E82E + Data &HE061FBF7,&H03CC4B80,&HC4A03E06,&HFC5A59F1,&HF8475F23,&H0BD19FC3,&HD8B39E40,&H51075191 + Data &H71E3FDB3,&H5CFE8321,&H6FDD13CF,&H6E0779E0,&H388D16CF,&H4D7C8ADD,&HEA241E70,&H9008ACBB + Data &H1FB0D578,&H444D70A8,&H80ED4297,&H5157D3C0,&H9688E02D,&HAACB8007,&H16050867,&H17B700B2 + Data &H61035FC3,&HB04CA677,&HF7319E1D,&H847CE43E,&H29116F73,&H4C8F25AD,&H7A9BE5B9,&H8A55D6AB + Data &H1A2935DF,&H0E7E5733,&H93B410FD,&HD0DF957A,&H2D13626F,&H20C01437,&HFDC92801,&HC7DB2F4C + Data &HD57F9570,&HFF780187,&H666D66A5,&HDCEE6D59,&H733D79CD,&H9A6E5607,&H440EC10C,&H0BA93286 + Data &H20E71959,&H0CE8860B,&HE3D77D54,&HED3EE6E6,&H7683B16D,&H168360F9,&H62AC10D4,&HCACD1FA3 + Data &H55807AF0,&HF31B5A53,&H9741F9BE,&HB20C267D,&HA8CF1B06,&H41500A2B,&H63DED0C5,&HF875E8AE + Data &H2C696CF4,&H71930558,&HB8A36E60,&H6AD10210,&H83231157,&HB2A2C03C,&H33024D84,&HB396D8FF + Data &H4E09D391,&H161BD062,&HB433C88F,&HAEDB735C,&H2F85BF5F,&H2F63437C,&H32AAF7B3,&HC0200AB0 + Data &HA3D3B729,&HF2E28963,&H38EB80D3,&H3E736471,&H83296773,&HC923E93F,&H685365B8,&HA7CAD5EA + Data &H782149C8,&H04C4F844,&H47429058,&H6C8B32FE,&H0F4E5E87,&HD759D97C,&H4B128180,&HB9A97D2A + Data &HFC50A881,&H5D5A74D2,&HAA1EBCDD,&H31597B99,&HFC8E5E9F,&HC190EC5E,&H1498C8AB,&HBF9702A3 + Data &H9242CE2D,&HD3A8606A,&HB013FDC9,&HE5A1737B,&H59364E3A,&HF9FAD757,&H2A6C2E63,&H85785A81 + Data &HB8EE4C5F,&H23FA0778,&H53E8DABC,&H1EFA209F,&HC1FD9078,&HA0740DFD,&HACD34422,&H50968227 + Data &HB60EB7D4,&H14FB5F06,&H5D603543,&HB2907F24,&HA53EDCF2,&H6074449B,&HC5DBEDAD,&HBB1989D2 + Data &HEEC6886B,&H2F909987,&HFE65F7F9,&H7BCDDE56,&H91259EFB,&H63E30528,&HB6182556,&HF55C07F6 + Data &H6D07D789,&HDF7EDBDF,&HA86633F5,&HE821D7E3,&H0B17FD90,&HD1CAAADE,&H5009B495,&H72D33C81 + Data &H85AE0DF0,&H2872E107,&H5C0B7330,&H9400265E,&H7A1307AF,&H19EC0B71,&HA2C6A356,&H74343975 + Data &H066D062A,&HE258DA4D,&HC325A3F5,&H357E4295,&H7E4DF040,&HBDE66326,&H5171146A,&HC07BD782 + Data &H24E57086,&H9B925C42,&HC1516936,&HDF5B0F7B,&H693DDFC9,&H32C9D5B1,&HB4A64F82,&H7B2FDEB0 + Data &H270933BE,&H5D5F8283,&HDA9AE8F5,&HEE9800A3,&H57ABD57F,&H47A888B9,&HE164F56E,&HB4DB1083 + Data &HD19F4EBA,&H2D81BE79,&HAFF0BC41,&H72BE03B2,&H4FDE9FDB,&HC21ECE25,&H1884E9D0,&H54992C3E + Data &H8299DD3F,&HC092EB72,&H755C2E98,&H89EAC7F0,&H27F84FEA,&H6DD453A2,&H943DE656,&HDEC3A5EB + Data &HA15DD93E,&H9683368F,&H7DEB98E0,&H74383553,&H10EFF489,&H3BD82CA5,&H9C152DC9,&H250276E4 + Data &H7FA93A6C,&H45164984,&H3A22E70B,&HDF54984C,&H5D3E48DA,&H3B39A97E,&H4624695D,&H0FEB711D + Data &H8CFE4E66,&HCEFF4CCC,&H2E635419,&H21FDAB44,&H7EF8CEFA,&HE81ED551,&HE26B04A1,&HC460151B + Data &HBFE4D2A7,&H65020458,&H3906FBC2,&H7B3DDA5F,&H11A06E53,&H6D7DC0BB,&H3727CC13,&H0D1CA542 + Data &H87BF8489,&H74109B7A,&H52E01921,&HA26B25E4,&H455BE3D3,&HE232F408,&H7758F058,&HC5D7B2F8 + Data &HBFCD41E6,&H5D01E07C,&HF8877F42,&H32EE36E3,&HA130032B,&H26B1FEBA,&HEE5D7E54,&HE147E790 + Data &HEEB51F68,&H01F15292,&H2D43246B,&H2BF8B394,&H37070429,&H0531595A,&H52B1E0E2,&H5852C1A4 + Data &HE7A73088,&H1F10F9A7,&H69FBF5C1,&HD8C65B45,&H28953167,&HE8FA7513,&H0126491F,&H3BF2FD4C + Data &H1C9A74C2,&H250D352E,&H44FC31EF,&HBDBEEC72,&H8C8B1D6B,&H596E0B90,&HB373FDEE,&HE80F43C5 + Data &H41888EFA,&H02FB3BC6,&H85B711FF,&H830BB0BE,&H314D72B6,&H4169C9FE,&H55AE724E,&H3CDA1F19 + Data &H0D2F81DD,&HFD723B8D,&HB44A2A3F,&H46654B4C,&H60057E46,&HE91BA19D,&HD6EC3724,&H54DFE4B4 + Data &HACBF46DD,&HE93DAE59,&H15BA3C56,&H47F18315,&H5CF4154A,&H2659009B,&H77D14D10,&HBFA44F5F + Data &H32BC1993,&H152F8B7F,&HF7B9C5AF,&H25EAFBDB,&H6DFFCAFF,&HD32BD7F5,&H3D8012D1,&HA9157973 + Data &H0D2641D7,&H1A746E90,&H34A4230F,&HEDE7AB01,&H8A0037D4,&H9F580053,&HEAFD639D,&H3172D41C + Data &HF72E0E01,&H919EC1EA,&H0407EB0A,&HCBEE5A0A,&H9F8FF1CA,&H76C36848,&H871483FE,&HF3F3E0F6 + Data &H62969AA9,&H558D5ABD,&H3C7BAAB5,&H8943331C,&H1B0F0486,&H17DEF425,&H823D2C77,&H6EE13B28 + Data &H84292678,&H02FBB0AA,&HEFC107A6,&HFF828723,&H5A75A3F6,&H97105556,&H9180EC0F,&HAC2AA8F6 + Data &HB08BBF33,&HD0D00108,&HAF5AA4AB,&H63EDC6CA,&HC772CE05,&HD2BDFE31,&HE21479D7,&H0718D838 + Data &H00171A3B,&HC143C72F,&HCAC46758,&HD77FA447,&H320EBEF3,&HFB90DF86,&HFE070C80,&HC37500AB + Data &H8DCCCD0C,&H44D4BB96,&H619C2187,&H55EE27E0,&H425B9BDF,&HC24278DD,&HBBE82E06,&HF85EFE49 + Data &H2A20B647,&HDDBF6606,&H62E43622,&H5D189589,&HC1628833,&HFD0D57D2,&HEAD963A7,&H68D34117 + Data &HF6ABEAB4,&HED70DB7D,&H18F3A326,&HE81A30F2,&H7B7F2649,&HEEEBB7C0,&HA535A599,&H675808A3 + Data &H56B6594B,&HD0406E16,&H42EDFE7B,&HFECD1D64,&H8C09579A,&HA938148C,&HCF853687,&H9A7D9D02 + Data &H51619E61,&H8C1FDDFD,&H15A0EF3A,&H6CAD7115,&H0116F539,&HCE938B37,&H62FE1707,&H95543FF6 + Data &H81E84C17,&H0C8D66F0,&H03F8E886,&HA9C71A8D,&H81EFB472,&H08AA9345,&H31308FAD,&H6B4B08EA + Data &H7AB6DCA1,&H65F894AE,&H94DB10BD,&H71D1F30B,&H20EAC151,&H8181BA9E,&H8E2DD6FE,&HC950650E + Data &HE49429B4,&HA67B4E16,&H9C54D0F4,&HC8B15ECF,&H3BFC4BE4,&HF7F61B5B,&H7BB18FA2,&H15B23D70 + Data &H97CD7B31,&HF7439FE4,&H9234BA0D,&HDD2EA11D,&H120E0F75,&H1DC6D9C1,&H44C0EAB9,&H60489400 + Data &H5900AEA7,&H6E8B9071,&H3EB587B8,&H425178C2,&HA5BBC5B9,&HE2A99976,&HE2AF01D4,&H90E58E33 + Data &H7CAF3CD9,&H0EF25E31,&H5F13A8A9,&H63466020,&HDEC3FA9E,&HB276155E,&H56AD3B70,&H5769F6D7 + Data &HA7FF855B,&HE3AEF77B,&H447322BC,&H6FFC406F,&H53E67E37,&H93BC9D34,&H8A0DB9A7,&HC4343EBD + Data &H09FE53EB,&H3E9F178D,&HA32A5BE1,&H4CE6689F,&H8393DEDA,&HFB8DB59B,&H5F7B3BDD,&H1EEEF158 + Data &H52517C81,&H47FE02C4,&HF55C7361,&HC51387FE,&H5932C9DF,&H1AED2562,&HBBDD0926,&HD8FA605A + Data &HF6A20AA7,&HCBE85969,&H8015CB96,&H52C4FFD9,&H9EEEC139,&HBCB78751,&H32FD6AFF,&HD631F1F4 + Data &HE51FFEF8,&H86E1B905,&H55746F65,&H23960D2E,&H60F1A41C,&H754A2D3F,&HAFF89858,&H87DB8D4D + Data &H7B3944DB,&H774CDC70,&HA6485231,&HECD182A6,&H44F4CEB7,&H57016CBB,&HA5F4D07F,&H3D335FBA + Data &HB2709295,&H9ACCB53A,&HDF649362,&HA3747B6D,&HA7C60537,&H921A0E3E,&H4D6207FE,&HFEAC9A90 + Data &H6CC86718,&H433E0886,&H76955BCC,&HF3B6BE03,&HBBDD864E,&HBB4A4FF8,&H638254A2,&HD5728503 + Data &HBF8855DB,&H548FAE15,&H2D4FD88E,&H5DA90721,&H367B8709,&H9A240904,&HBDB90D03,&H20D95BF2 + Data &H4E4B47A0,&H9837EEBD,&HC83BDA10,&H6C076B07,&H417B302E,&HE63A92F4,&H3BC2915E,&H67E47422 + Data &H0F6D1A92,&H18DE1D5B,&H1F555159,&HC24685D6,&H2F5F1937,&H7B1815BC,&H4DA2999F,&H1E3C9FB6 + Data &H5D406569,&H6BE9595C,&H3954F266,&HBE653CCA,&HEB55B79A,&HBF92BF58,&HDD18ECC2,&H468FDC71 + Data &H9FD6D3F0,&H514735D4,&H8F81220A,&H65CFBA1A,&HD5F7966B,&H992C904B,&H57F6EBD4,&HF27F8301 + Data &H0C9D5E29,&H1B70C640,&HA2BA0DDB,&HD9EF6360,&HC0FADFD9,&HCBD3EC2E,&H3BB9E364,&H8D66AB22 + Data &H96CCEFA7,&HD19CBDA6,&H33B9F3CE,&H970A1EE9,&H382AF42B,&H4705F637,&H9A6928D6,&H5F3ACE15 + Data &H582700F4,&H9E430171,&H28CD5AB5,&H9D6480AD,&HE375E5E5,&H01E928E4,&H91DB3B65,&H60A12370 + Data &H2D3E0C37,&H764228A7,&H34F5BE42,&H1DEA8DBE,&H204087A0,&H01BB144B,&H92262D50,&H3E7A6CA4 + Data &H40A20E89,&H9FFE6381,&H43DEC5B4,&H81F94EBA,&H199B3D65,&HF9362AE5,&H6B41B3AC,&HAC6BF7FC + Data &H97CB8CDC,&HFE1B15BA,&HB1FE7FEB,&HB45BCFCF,&HDDC3B140,&HA8FF364C,&H6908D96F,&H3BF5DC1B + Data &H6D6032FE,&HE306C866,&HA34F507F,&HED784514,&H3D92DDB3,&H6FC3FE13,&HF7DA01F1,&HA934EBA9 + Data &H9CEE5273,&H98577590,&HF4B63898,&H428CCDA6,&H5BA353E9,&H8A97CDF5,&H2E7E1B25,&H0FF5C425 + Data &H852D9DF9,&H54CA8D1D,&H6984FB0D,&HBE8A7DA5,&H7EC5A478,&HB0D1F00A,&H36D4B3AB,&H59FBF473 + Data &HC86DB3FD,&HA16324E3,&HE274DAB8,&HAB27C62B,&H796DB054,&H6EAAEB1E,&H5EFB0F8B,&HBF9DEBD7 + Data &HA393B974,&H15982E94,&H750CF6DF,&H72A85CDA,&H0567D3A9,&HEF2E118A,&HEF838CB2,&HB7CE3CC2 + Data &H1E196CB6,&HC516AA6E,&HE876AE4B,&H1A046DE0,&H706D6EBB,&H39B98E10,&H266B0258,&H8A3CE8B7 + Data &HFE7FA71F,&H4A43037C,&H13F80B83,&H318FE3AB,&HA28147AF,&HBCEE8D8D,&H2B5B149F,&H262C19D6 + Data &H84F0F50C,&H18FB387B,&H690BE0DA,&HB50595E2,&HAADD09A6,&HF0983EA7,&H28EC8D32,&HCDBA6919 + Data &H1FAA9E74,&HBBE59115,&HB49FBEB5,&H1D76DB00,&HA18690A4,&H1A04D68D,&H5F5AE185,&HCA600268 + Data &H90E1FEBE,&H4A4B9AA8,&HCB128525,&HBD994AD2,&H92D7C11F,&H147EEBF5,&H774DA1AA,&H0CD3D5C6 + Data &H81329900,&H932D94C1,&HA6E9ACA9,&H03CE9D1A,&HD90E625D,&H9A9126EA,&H5AA53B17,&HE0E7DC4D + Data &HB1C9FCD4,&H69BA36EA,&H1EAE5C79,&H0B6012B1,&H8B6863A4,&H1577AA7E,&HC0FE9E27,&H9D7215CB + Data &H1044329C,&H419C2043,&H1B816CC8,&HF02EBE8F,&H84B71DCC,&H5775C6B5,&H163FF460,&H87461A64 + Data &H8D833167,&H59ED81C6,&H41C51DB4,&H891E6B7A,&H64351388,&H2FD81CDA,&H7D41A520,&H96BDE903 + Data &H0A8CD8E8,&H5E00691F,&H69AE89FF,&H245144B0,&HD7510FB1,&H3FCBB45D,&HEF47BBCA,&H545F49BD + Data &HCBC01B4F,&HFDAAA2F3,&H0CE13DBF,&H1690B25B,&H526A47FF,&H946712E4,&H4C474DA9,&HFC7FC8C8 + Data &H9C263A1D,&H59F09333,&H0F0BF4A4,&H3C3AA433,&HDE56174B,&H9DC3637E,&H4E35D976,&HD3FC837A + Data &HB05E4E53,&H84E2DB02,&HF2917110,&HE33E9050,&HFDEAE967,&H259E4378,&HF5F4A295,&H43C27A2F + Data &H37526835,&HF58C9C33,&H76CE20F2,&HDA978357,&HAAD2DFBA,&H77B96D38,&H945F7F72,&H63ACCEAB + Data &H57331653,&HAABE810E,&H0996EFFA,&H23336417,&H90E774C5,&HF906D2EE,&H679FC26C,&H6C6E2625 + Data &H35CA64E6,&HF41699D7,&HC7BAFF37,&HB9696DB3,&H6A2E0DAD,&HD14416DA,&HF1DC1F72,&HF390329F + Data &H523DC6F0,&H738A7297,&HEB2E9D96,&H7211AFA8,&HF912704A,&HACE93E82,&H636838F8,&H10AC2466 + Data &H6D68CD80,&H4B5F4F54,&HE76F0FF5,&H4F31FDAC,&H316F3ABA,&H36D28654,&H2631B3A9,&H189339EF + Data &H0E385CBE,&H631D4F55,&H39018236,&HDD98EBFA,&H0FDABFA0,&HB4CEC6BB,&H407AB7E4,&H5A2AB708 + Data &H1AD77EEC,&H2BF80F8C,&H9DD439DC,&H39E1558D,&H3E2099C8,&H13B039C9,&HF0AD80CB,&H148DD4F8 + Data &HA989C0DF,&H827DC4A2,&H23DF3BD1,&H04175BF3,&HE2D8A4DD,&H1EB5022A,&H109E9EEF,&H70727EC6 + Data &H65AD3132,&HBBAD21BD,&HE34EFF6F,&H8B0D288D,&H46714F31,&HE50548D1,&HA69E8A2E,&H11A01CD1 + Data &H0FED0815,&H32B469B0,&HD816540C,&HD0C01ABA,&H9DA2E009,&HCB2623DD,&HD3E2B61F,&H3F689858 + Data &H703355AD,&H0703869C,&H9D28EF85,&H9128E3AE,&HEB5DD21C,&H15575AF4,&HA56FE608,&HE62E7958 + Data &H1D09F469,&H8BC7C0AA,&H6F6EB74E,&H0F9A9617,&HA7D2B1F0,&H6D33845A,&H9D5DA515,&H84FA54C9 + Data &HA22A382D,&H67EE5658,&H684CFDD1,&H44A29B04,&HC28E92F5,&H1FFF41AC,&H56793E56,&H250ED4C7 + Data &H5E1A33AA,&HE3D70454,&H860CB6D1,&H6777AF5B,&H582FF730,&H619A8790,&H5683AFFC,&H524581EC + Data &H7BADE10B,&H8C1E2F7F,&HEC288E22,&HBE8915CF,&H729534ED,&HA8DFE67F,&H14046A05,&H6E1BC7F5 + Data &HE91F66C5,&HE766516B,&H035947F1,&H930DAD1D,&HCF7CBA72,&H7C3EC7A5,&H7B9670DF,&H0DA6F277 + Data &H794BF342,&HD7C055D5,&H4EFB2D9E,&H1B21B56A,&H7C48BF52,&HE837E30C,&H7B4DDE4C,&H5AD89FED + Data &HB6053E5D,&HC5F3CF2A,&H30C3FB41,&H71353597,&H3787BD14,&HBAE09C18,&H6BEF84A5,&HF256A3EF + Data &H54D848C3,&H822BAAB7,&HDF382F6C,&H0F533F05,&HDC164642,&H1A40AD6B,&HF538C8AE,&HA5515B5A + Data &H4F66BB0A,&H2F477DF6,&HFA3C6489,&HDD87BBA9,&H07C4EC59,&H412E27A5,&H8EF99802,&HD89F55EF + Data &H0A49A9BF,&H6507D189,&HDEB711DA,&HC5E7E18A,&H463947B4,&H8BABE7D8,&H123DB38A,&H9CF27458 + Data &HF03E724C,&H93771227,&H9643EA7E,&H1E0261BB,&H9B975CA3,&H7012195C,&HE11F5295,&HFD9EA3BA + Data &H5B7E5FF7,&H0566C561,&H6AABF192,&H01EFA398,&H7EAD3998,&H474076F8,&HF3464449,&H078BFA6E + Data &H1F2F7E18,&H414CDAE1,&HD1E409A4,&H269B24DD,&H431BFD72,&H1E7FC9C5,&HAEFDE0E4,&H0F3978BD + Data &H1313EABB,&H6E38A42D,&H0046B4DC,&H7C9159B0,&H3691404B,&H8FA6920B,&HCB5F0A13,&H9A9F7AC5 + Data &HD2E43AD8,&H0C59B511,&HEB17F25A,&H5B602E4F,&HD6EFD3BE,&H6C066AAF,&H213C1CA8,&H823BD78D + Data &HCF853EA9,&H9A27E3FE,&HF74C10D9,&HD4016293,&H1A8BD4A8,&H28D3CB78,&H9A54EE03,&H4D7BBC5A + Data &HB736B85C,&H745FF01A,&H45362D82,&HF6215E98,&H6E257B73,&H210EF32A,&H6D2D297D,&HA6E41FA0 + Data &H055AEF2D,&H591ED35B,&H39CF8185,&H4A7E77DC,&HB58A26C3,&H4D45A32E,&H4798DDBF,&HC1EA7C54 + Data &H1CBFA01F,&H448855EB,&H9B5F9BFD,&HB9A2085E,&H2EAD9B95,&H00B0DE16,&H8E5542F5,&H73618D11 + Data &H13DD51DA,&HB51EC4ED,&H98E0897B,&H7608B2F2,&HBE01633B,&H2DC28149,&HE9A10700,&H77C53951 + Data &H7755CD81,&H34383748,&HDBD70001,&H652F0AD6,&H1A024BE3,&H428B6112,&H2DFAB6C4,&HF7B62E31 + Data &H1202DEA1,&HA278D2A2,&HFFA4A201,&HDFC589E6,&H1A8ED1C8,&H6E3C87AD,&HFA9DEB12,&H69C922AF + Data &HD33B8BE4,&H20480BDB,&H96333A77,&H63743A6F,&HA657BDB9,&HC6B4EDC7,&H5AF4EFBA,&H388DD448 + Data &H5197A195,&H9F3E813C,&H5407436F,&H5AF5F31F,&H5779BF1E,&HF7323B71,&H9CB398FC,&H9DF9CA83 + Data &HE3838129,&HDB5A776B,&H672A0F50,&HE5DBA71E,&H5C71D7E9,&H136908EE,&H791687A2,&H761C163C + Data &HCCF1C59C,&H7987421E,&HFE91CAC6,&H0A628FD2,&H30DD5F29,&H5E52A734,&H30F56049,&HC71B30FC + Data &HCB2B4538,&H5ADEB727,&H9FBF151A,&H76FD3F07,&H22FA01CB,&H7473679D,&HB55FC277,&HA0A4DD71 + Data &H3303FD96,&H2B7B8F04,&H2E139BFD,&HF933F925,&HFAFD5BF7,&H4C71F4C8,&HF282F89F,&H960F58E7 + Data &H61C410A9,&HD4FF8B02,&HA18DAAD4,&HE49F3E3D,&H85CCAC61,&HF42EBBA2,&HFE73AA6E,&H64CFC82B + Data &H8DD3FBC7,&HBFF73FA7,&H18D8572C,&H1E9A7B34,&HC7AAEEAA,&H27520FB8,&H3FA62458,&H742EBD7D + Data &HE018B362,&H31DF8363,&H359D4918,&HAA2CFB13,&H9039D532,&HBEE57CE4,&H88B3DD50,&H98E137FC + Data &HBD2ACD7C,&H299C6F36,&H538BDD81,&HB3E15BF2,&H2C46D0AE,&HB3E711A7,&H9DCC72B8,&HC4A52515 + Data &H83F1F1D6,&HD097BFD0,&HC61B58CE,&HEAEDCAB7,&H5FE65115,&HA68D1BDC,&H9D7A2C75,&H84F542A7 + Data &H3A3ED233,&H7B69B93C,&H701F58FB,&H9F31079E,&HAB3A197F,&HA99E319E,&HF3CD9DC0,&H0A3B549D + Data &HE8C101BE,&HD4481569,&H0D617E7D,&H2D144353,&HD9B12307,&H920057E5,&H00B33EDC,&HA8F24A2D + Data &HB9564B16,&HAA3C10C3,&H018AEFDF,&HE158C183,&HAC8820CF,&H9EB47101,&H8F44FC2C,&H0F962B81 + Data &HE2860F2B,&H8DCDAA91,&H9D4E03B8,&HACA2C398,&HC4ACE7A2,&HAD3A9D59,&H67741B0A,&HF0758FEE + Data &HA5172D12,&H70619A8A,&H84B405A3,&H550D0D89,&H4E5DFD9E,&H2E724DB7,&HEAEF9D0F,&HCA523B95 + Data &H9A8EFD7B,&H799C0189,&HBC0ADA35,&H08EA5289,&HB0794D81,&H19013392,&HC03C8255,&H5F2780CE + Data &H3DC34CE2,&HA11105C9,&H83DC3504,&H21AC6DCD,&HCBA9F34B,&H8FC1BD88,&H3724B5AF,&HE0229178 + Data &H2AC23AA1,&HA91811F8,&H87FDEFB1,&H76FB754F,&H5307B865,&HC0E95FA5,&HC21FA82E,&HB6D92B2F + Data &H0A6BF038,&H015607C0,&H6D987EA2,&H3CC51C24,&HB8F64735,&HC0120063,&H32739028,&HF1D02715 + Data &HCC5BEC58,&HFD7865A7,&H654B8DEA,&HBD20B5E3,&HF0923858,&HE74B9223,&H3BBA5C14,&HB97B73D1 + Data &H1F888B33,&H8255D429,&H30E6E292,&HDF0C090D,&H75DB3A05,&H1B17CD71,&H5B440380,&H321C9E16 + Data &H8214348A,&H12A4D695,&H58FC48E3,&HDCFC72CE,&H0805EBEA,&H2FABD60D,&HA432D30D,&H2BA885C1 + Data &HB61AC929,&H69D83D3B,&H406DA7F0,&HCDAB305B,&HD93F35B2,&HB73DC75A,&H684C86C1,&H9F3907D8 + Data &H37BBB519,&HFDD73071,&HEB7A7B10,&H62C478BF,&HB9BFF76B,&HDEDF684B,&H9115BDF5,&H09F981AD + Data &HEB2209DF,&H3DA27410,&H9C53AEB2,&H92C3E765,&H9C2F1454,&H9F2694C4,&H3DDC9184,&HDA11CB3F + Data &H0E724FFF,&HED36ED54,&H1656BC8B,&H9618759D,&HD6582B51,&H720697D4,&H4160B5EE,&HF9D825E6 + Data &H8B26BDF5,&H719AC748,&H14E2B1C3,&HFFA2A4CB,&H575838FF,&HC605AA71,&HB34A6AD7,&HFD6020FC + Data &H8C63D402,&H8DE5581B,&H061E9394,&H729199CF,&H8A176219,&H39E8969A,&HAEEB656A,&H85A5AD1A + Data &HC5B7517C,&HED5DEACF,&HCA0E1012,&HF9D17FD4,&H21203EAE,&HBB787292,&H9BE40BE8,&H9C4FF2AC + Data &H95FBAFD9,&H5ED7AAE7,&HDA36A1DC,&HC4014535,&H9A9E7427,&H0AFB98E2,&H8DA82834,&H042CC0E9 + Data &H2E2F5EBB,&H9F251549,&H295286F6,&HE785D45D,&H906D56D2,&H3D5A758E,&H990936F9,&H3E4F6683 + Data &H158A49F8,&HCBAEC122,&H17A09F1C,&HA33FE854,&H1D55F370,&H4ACB4933,&H4EA27258,&HB9E4A154 + Data &H09C89675,&H01A556F0,&H383B5026,&H4F9E8D3A,&H74CFEBA7,&HC4F87AFB,&H9F0C3DF8,&H61D480C2 + Data &H8DD2056C,&HC9D2C2FB,&H8999B9FB,&H04E116F6,&HEC2573AF,&H0958CE50,&HBCD4D00A,&H13CCCF7A + Data &H21B72EC0,&HA93A491C,&H0D38377B,&H9BE26A86,&H439B608A,&H69D6860E,&H491044D7,&HA63DFDDD + Data &HE4C0FE4C,&H5166DF88,&H2EBFB673,&HA1DDDBBA,&H94F143ED,&H0C4D97B1,&H7EE72B71,&HDCFAE59B + Data &H87E27E3F,&H313EABEE,&HEED675BC,&H6CE12A4F,&H0B64FB5D,&HA75FC22A,&H962CE170,&HC8116E98 + Data &HADAC8EC3,&H92D18446,&HD0B3BF29,&HDEC94111,&HA4AAD971,&HE3FB7C61,&HC303F7B4,&HF4058E20 + Data &H986EF2B7,&H13A53B05,&H4561673A,&HB7DDB7FA,&H728A96EA,&HAE347AD8,&H34827F9D,&HEDF2E712 + Data &H6DF2CBAE,&H6455F4C7,&HAFDF464B,&HCCF242F0,&H8FA5FEFB,&HBDFE5E2D,&H201F6572,&H0F3C6014 + Data &HB4204054,&H83280548,&H7DF1627C,&HCE9D7602,&HBE4AB488,&H7B152CD6,&H96973C86,&HD4973027 + Data &H14CE8D80,&H9C70EE2E,&H4818CF38,&H529BABF7,&H070D26F9,&H20EFB9CF,&H9A5D5E87,&HFC496F5B + Data &H0F19F852,&HC7AFC104,&H03FC308F,&H4EB1DFE8,&H17F7DCC8,&H216C10B8,&H1C764A2E,&HD67B886C + Data &H371D675D,&H3B3D50C2,&HA20994A1,&H4A66708A,&HBF68F995,&HD144CF21,&H2E7237C3,&H726C552F + Data &H453C6D7A,&HC38FA64E,&HD47DE244,&H777CBCA1,&H55174C75,&H92F46CEB,&H8C720B4E,&H937CFA69 + Data &HFC83EC66,&HEAA894E4,&H2FB66DAB,&H73302D03,&H9D3212FF,&H5D73EDA1,&HE818EC52,&H8F87DB7C + Data &H9F08B11A,&H630D6766,&HA3398004,&H77B1BA94,&H5C898966,&HC4E72E0D,&H4B5E82FA,&H9E9E66EF + Data &H4BB2FC2D,&HB29C5354,&H722A1710,&HB7CCD93C,&HA935738B,&H16F49E42,&HB978FC0E,&HF1EE62F5 + Data &HC2D9BA96,&HF19DF4D2,&H45F7E7D2,&HE8A0681A,&H662199EB,&H3428DCEF,&HEE47F6BE,&H9B6C4FAA + Data &H727E9BA7,&HFCCFAE7D,&H1969C49A,&HF583D983,&HD8EA60F4,&H6C6C84F8,&H0B52138C,&H15E752E3 + Data &H0D4ED11D,&H9FE2A0EF,&H11E4DB94,&H1B107FA9,&H0803EFF0,&HCD1EBF20,&H25E7A375,&H33E166CC + Data &HD4DC485E,&H03594F37,&H070AEF1C,&HBB4F9BC7,&H76785306,&HBBFE5FB8,&HD911E0AF,&HE0BB9C97 + Data &H5384970A,&H89F4917C,&H317DC7F0,&HF021DF64,&HDDFCB937,&HEC723627,&HE61111F2,&HFB0793BF + Data &HD257E097,&HA6734DE5,&HD68AB035,&H87348979,&H6CA81AB4,&H27370D43,&HCF5A0334,&HD1D74DAD + Data &H35968AB3,&H1D62B733,&H74C91B36,&H4D5502E0,&H7FE3F5AD,&H1E65AB0F,&H78597F70,&H660BA987 + Data &H364FEEA4,&H93C2AF20,&HDFB3C605,&HAE833F9A,&HC7A178FB,&H398782EC,&H5CDF5D47,&H28AA9975 + Data &H8FA847EA,&HDF5F5F5A,&H15F0F145,&HD86C152E,&H83BB2EC2,&HE9BC3F0A,&H1E4567F1,&H530E1B33 + Data &H23EEA43F,&H8A565CD2,&H4D66614B,&H4D6D6626,&H3326E5D3,&H50F8A879,&HFD463289,&HD9C5BA2F + Data &HF18E09BA,&H3E82F505,&HD9D7E7FB,&HD70BF23D,&H68965556,&HE0A93C05,&HD242D088,&H6B9E899C + Data &HC09D9F57,&H2C102610,&H3EC7B041,&HCBDB1ACF,&H30646C9B,&H3133CF4D,&H3E9D9EB1,&H6CB4468C + Data &HA91FAD31,&H0AD05A82,&H04EC2C03,&HA009E7C6,&HEB555110,&H467B06AA,&HD41051F0,&H321B46C4 + Data &H7B79BD2F,&H8C86271D,&HF2006EAB,&H23C5EC6D,&HC54321AF,&H9456086E,&H1DE15897,&H2C795A59 + Data &HFC325A3C,&H9E2B6A3E,&H0CED27E3,&H2C096B47,&HE6C3A101,&HE802FEA7,&HF6E8CB3E,&H02EC939A + Data &HB7EFC7D1,&H58DCE2C0,&H47BF0B9E,&H95DBCE5F,&H0FDD31CD,&HB9BF19FF,&H7B649315,&HDB7A5139 + Data &H15FC50CB,&HE277BCDF,&HF74625FE,&H4E3E439E,&HE0B1D18B,&H50F419BF,&HDEFC3EAE,&HFD91E1DE + Data &HE379897A,&H293607EE,&HBF8FF143,&H4F0E3AF3,&H27A0C61C,&H5E5CD23A,&H03A41914,&H5F51AAB3 + Data &HBC8DE3E5,&HE619030A,&H8FEDB976,&H4685F18F,&H333A48BC,&HB586BE73,&H33AA2E53,&HFEE07EE0 + Data &HFBABA63C,&H234C78BC,&H2E652EFE,&H0E46DAB2,&H38112FB3,&HCF309B5C,&H70008134,&H1D005DE9 + Data &H5CEAFAF0,&HDF14757B,&H0D618677,&H3C3AD169,&HB050AABC,&HBAE50E11,&H4B7ABD13,&HB05CBB00 + Data &H0C7FD5E5,&HCD06DED3,&HC582DD28,&H2DDE7BF0,&H164DDB46,&H6766AAEF,&H93EC6AF9,&H2FCD906B + Data &H16C850F5,&H1E5E5E67,&H9322C35F,&H62EA36DE,&HEBFD7556,&H36942A38,&HC5D03A30,&H504B9385 + Data &HC85DDD34,&HCAAC1D13,&H4BA899C8,&H7DDC11BA,&H8C1D093B,&HCC8E411B,&H8F575740,&H9ED99402 + Data &H6B741D06,&H3270267E,&H849EFC76,&H9943D03E,&H36E47F09,&H4361B47D,&HA3C9CBAB,&H91EC4E61 + Data &H36BF1C16,&H23E2FA51,&H86A55F75,&H7797B72B,&HB44ECA13,&H3FD9F0B6,&H08A52C3E,&H6919CE09 + Data &HC4C51B78,&H64B01C8F,&HF6A7A5E1,&H87A3C3E2,&HC13C81AC,&H08FDD20E,&HC9734BEF,&HE7789991 + Data &HA5D86755,&H38C1D3E6,&H665950DD,&H4E6E6419,&H972C0650,&H491C1ED4,&HE2C21A5A,&H382A80E5 + Data &HB28DDB15,&H2506D45E,&HA5265EE2,&HD5647D00,&H862066F9,&HA2183445,&H861BEB7E,&HA335089F + Data &H2A9B475D,&HB58C5AB7,&HB0E2E2DC,&H39796770,&H7AE09EC5,&HC1BDCA66,&HC2BF6FBF,&H9BE0B6DD + Data &H0E2065B1,&H52987A16,&HE0C43D90,&HE399382B,&H07E3CA7E,&HD9507D10,&H6ED80F83,&HA7643073 + Data &HF72843C7,&H3BF04544,&H5539E6C3,&HFA84DBEE,&H10F94EBF,&H4883D673,&H2A576A17,&HEBF8C939 + Data &HBD50B638,&H0568C327,&H43D7668D,&HD5E7C47A,&H59301CFE,&H987F4C6F,&HB20A77EB,&H4301EB05 + Data &H3AF73D9B,&H9E8073DD,&H407BAB19,&HE37B5F6D,&H35304909,&H28732A73,&HE0F0C1F5,&HF4556413 + Data &H2BC59EF4,&H9BC3A477,&HFEB4F9F9,&H526FB41B,&H6FE88CCD,&H79A0DF7B,&HE35D56C0,&HFAC386C4 + Data &H72AEE7BE,&H227E3FC4,&H5038D741,&HDDBEC094,&HF11E02E3,&HA8ECEBDC,&H5E4F1CA6,&HFB5C67BD + Data &H6DAB78DA,&HA8559332,&H7967FB85,&H9DF6EB80,&HD4B8718B,&H1FABE23C,&H65AD3BA9,&HCA09E092 + Data &HB6E65D62,&H6168AF17,&H5C74ED0C,&HB8F4F5A5,&H24B385E5,&H2E0D59DA,&HD0FBFBF5,&H9EC6BA7A + Data &H54D44257,&H2C0D5EDD,&HE3AF931E,&HBFD3F505,&HC2BD664F,&HFFED83CE,&H1430D2E7,&H4FEC70DD + Data &H61D5E2E3,&HBAAC3CFF,&H8279F4BD,&HC75FF4FD,&H5D7FB49B,&H73AEF66B,&H50CCDCDF,&H1A2EF0B4 + Data &H30DE45F6,&H2B2FDE52,&H0ADEEDF2,&H010B812E,&H02669284,&H621EB7B2,&H36858F77,&H1D4A823A + Data &H1E476A62,&HAE4C15B8,&H7FA38B28,&H27998D69,&H55B85372,&HA867A7A1,&HA77E6621,&HDFA99E22 + Data &HBA69439A,&H3CB62243,&HFEDC14E7,&H173CA656,&HA826C28D,&HCE1DCAFC,&H31D62A00,&H3C8F852D + Data &H3CFC1359,&H93620B9D,&HEFE0F1A0,&HB485E97D,&HA9F432CA,&H3A632EA3,&HA8E057B3,&HE1B93465 + Data &H6CAF9AE8,&H676C2EA0,&H23801DB7,&HEC007B1F,&HB49B7830,&H5AF7760A,&H6079F537,&H3A9C2972 + Data &H2502C8C9,&H76D08D5A,&H60833E49,&H8E59686B,&H3EF631F5,&HF57FD12E,&HE67C6AB9,&HE5F6BB24 + Data &H257D41FB,&H12512058,&H33D02DFA,&H9B334D6A,&H004961CC,&HABEC2AA8,&HDC4A2BA7,&H64E77C5A + Data &HEC854E08,&H662A502F,&H2D52FDBF,&HF486B527,&HFC1034FF,&HF47B79E3,&H2DF6B9DF,&H9DFC6E2A + Data &H8362390A,&HC695DD59,&H785AF98B,&HCEC91EF0,&HCF61136E,&HAB2A9F69,&H583479AB,&H6FEC715E + Data &H6FEA6148,&HC03B7AE8,&H36416B55,&H763DA903,&HE0BE5C55,&H673F8EC0,&HA3E41AC3,&HD5C4D816 + Data &HC476FD0B,&HF375028C,&H48BBACA8,&HA2A8F5F5,&H6C58149E,&HEA63877B,&HEB340DB6,&H3FDAE064 + Data &H5EA4B3F1,&H4EFDF195,&H1F97BD23,&H95954AEA,&H7687ADBE,&H0BB79714,&H205C3C8C,&HAF852E09 + Data &HAA5C06A6,&HB6EDA33E,&HEA71D65C,&H851B85B9,&H79287AF5,&H8D956A0C,&HF220AB83,&HE38D2048 + Data &H5F265BBB,&H4146AD60,&HAF7CF3D6,&H7F794630,&H9168F615,&HAE48F652,&H063BA2E9,&H7C2B7B98 + Data &HFFE9CCA1,&H87513D61,&H379DD904,&HB11EA923,&H1FD3510C,&HFE73224B,&HF4391C34,&H5DAF3731 + Data &H640535CC,&H60969AF4,&H1A9DFECD,&H0394693E,&H5C9C7F03,&HA42C7939,&HD581E7EF,&H0847FF7D + Data &H2B7D2530,&H988DBF38,&H95AAEBE0,&H425FADEA,&H4B19E89E,&H53B06DA6,&HF7A87DBF,&HB9F7E13A + Data &H75E6C8B0,&HEEEE049C,&H64EEFFB5,&H6672409B,&H42E44EAC,&H75C402EE,&HB9B44DA1,&H2819C8FA + Data &HE01F7934,&H13C85CAF,&H511AFFA0,&H8258D276,&HADCAE961,&H6353C593,&HA3ADA1FD,&HD669CBF2 + Data &H84D8D4DA,&HCC2CDAE9,&H4B288FC5,&H8FE9B218,&H251B6CD1,&H53915D87,&HFB739AAF,&H6968E9A8 + Data &H8186B9B9,&HDF39BFE1,&HFCF04C21,&H4249BF2A,&H52B84C87,&H47F10A68,&HD5846DE4,&H0598F86B + Data &H29B9EBD8,&HC41DBC05,&HE8C00BB8,&HB08EEFC2,&H0B3652FF,&H5654FF35,&H5379354F,&H9DF3BF76 + Data &HAF41AE0B,&HFDB2C799,&H9E146866,&HF6E90EA0,&H2C6BA82C,&H868F2B47,&H2E4D2B25,&HF2CD9816 + Data &H9B6DF0E5,&HFFC30695,&HA1D47FE9,&H903FEC50,&HFB37552E,&HBF9367EC,&H5BCC10BF,&H55FC4BB2 + Data &H479DBB5C,&HC1F4DD0E,&H9F3273A5,&H1C6B56E0,&H966B1026,&H8746AAEF,&HD42F4926,&HBB46D76C + Data &H00318C8B,&HD2B9C45D,&HE1C55AB3,&H00114A4B,&H249BE40E,&H49F3E33C,&HDEBE4AC7,&H98E17779 + Data &H7CDCD25E,&HF78BC034,&H37E12B87,&H761EE629,&HF44CD20B,&HD47EABAF,&H9EBBF7ED,&HD4953219 + Data &HBE5D26A3,&H432A498E,&H5A8D0BFD,&H808C01E0,&H1ACF91FE,&HE5165C0C,&H3E88C9A0,&H091A821A + Data &HD12E3DC8,&H36A316D1,&H21B6F6D4,&H3B536823,&H97256F76,&HA710B7A0,&H3193ABED,&H6F6E8E87 + Data &H81D3AEB9,&H70089A57,&H2A805DCC,&H8034FA28,&H21F1DFF1,&HD7E7FF3D,&H6F08FE09,&H02A1B402 + Data &H1AF3D1BB,&HE4640CAE,&H0682AC0D,&H47968818,&HAA700EE5,&HE5363FB0,&H9C15AFCC,&H38F90C2C + Data &H1C8F762E,&H460955B1,&H91E0C7E0,&H0378BD5C,&H59182F9D,&H9203A03F,&H51A48AC3,&H49435210 + Data &HE4BA7244,&H11B7A402,&H109BABF2,&H0F76C67E,&H204997B3,&HDF1A0BD3,&HD8D3D2FD,&H75CAFE0F + Data &HF95B4512,&H53842B61,&HFF44161B,&HDF3B6A36,&H1165DFF0,&HF853BC96,&HEBEEDF29,&HCE9888D3 + Data &H0515EF78,&H4E217D00,&HAA8AB3B6,&H6F926AFC,&HB6B91F6B,&HF086F0CE,&HC92CA352,&HC10253FE + Data &H252FB7F7,&H5B795729,&HDEFDEFA7,&H3AEB7C0E,&HAB24F595,&HC309AF1E,&HDB27CF5B,&H7ED8837A + Data &H51D58A4C,&H83586E8B,&H4F05AB99,&HEF5619B3,&HDC0EBEC4,&H087B020B,&H431930AA,&H756F024F + Data &HF64EE2D4,&HF6809DF4,&HE1AE13D5,&H6BFBCF68,&HE73F86BC,&HFEF9BC9E,&H99F7ED2A,&H657133F6 + Data &H42737DBF,&HA3287527,&H6D72E862,&H43FE944C,&H3ADCC68D,&HA65BCBE8,&H7BCEF8CD,&H2BBE31BF + Data &HCB61C2B5,&H7E99C22B,&H8B9B3490,&H45248C75,&HE74FC776,&H2EA4E623,&HF2B28DD7,&HCE14A6C9 + Data &HB86304BD,&H94E84F05,&H761B2CC8,&H9B55CDEA,&H4A7C4BEA,&HF77C2100,&H041CB6CF,&HDCBB57EF + Data &H9C67D093,&HC14D3C02,&H32234A2E,&H08F29F7F,&H37B154AD,&H483F358C,&H9E0AC349,&HFA6E5171 + Data &HF212E3C7,&HF8DD0114,&H74C6EFD8,&HDCAC5C11,&H76F8904A,&H4658B732,&HBD1FF792,&HE368FED9 + Data &HA0EF0BC2,&H91D0926B,&HD58AE776,&H83417930,&HE28757FB,&H001C50C8,&HBFE3052F,&HCEC77225 + Data &H08451447,&HBD941683,&H2969373B,&H02AD7B65,&H9A412365,&HF979374E,&H190FA2CD,&HA80F67FC + Data &HAF53B30D,&H5D253CE3,&H534266EE,&H8392B787,&HC8E0DBB4,&H93E2AFF2,&H916B42D3,&H26198D49 + Data &H46EC4E80,&HAB2F8B80,&H7DE25F82,&HD6BC2D9D,&H93DB3C13,&H541A0658,&H28CE46B4,&HA8D376EB + Data &H5E85A289,&H5DB8138F,&HC5885A10,&HA4A03139,&HE0F11048,&HD717F46F,&HA9741A0B,&H75972C24 + Data &H12DA655E,&HA988E419,&H29509E73,&HF9D79BDF,&H5108507A,&H4464F83E,&HD45B9DCB,&H62F35EE7 + Data &H1594136E,&H38AFB3E4,&H7CDD4469,&HD20AF27D,&H3685A544,&HE3713E82,&HADD6A5A5,&H5734DC89 + Data &H95DF0579,&H6740623E,&HAB843C7A,&H99B920A9,&HDBEFF02E,&H0A457373,&H003AA402,&H3712AFDB + Data &H16D16E7F,&H6AC52C3C,&H89C4C6B6,&H013DA721,&HAD1A3467,&HC55FB515,&H0B3AE366,&HA4F8F821 + Data &H42B0B7B8,&HE71A8B82,&HC4FC7E0D,&H1A40FA27,&HC8512998,&HD7E19126,&HCDCE8D97,&H4988F690 + Data &H7AA36263,&HAD2C77B6,&HEECB152F,&H06BFC6BC,&H8F78D46B,&H7C3CAA6A,&HF844B882,&HB0BC277C + Data &H95F29A6E,&H9FD96BDD,&H6913FD01,&HDEE62448,&HD9D20C8A,&H4573C3A0,&HE385EC2E,&H3D04BD82 + Data &H9738BDB1,&H9D441699,&H1EF19B78,&HF53B090C,&HB7CF986B,&HCF9C6723,&H8DB29AEF,&HB3D0817A + Data &H963F8A87,&H5009F1A9,&HD53FAC5E,&H21129914,&H41D435B4,&H6D18F4A8,&H17D76475,&H3B9DEA5A + Data &H0B6C0633,&HF9EF7BDE,&H1FE43FB9,&HECCDB5D7,&H9C75CD06,&HA9E9013A,&H7E99969F,&HA1AE0F58 + Data &H8B42EA0A,&H87C086FE,&H558378FF,&HF18162A4,&H7B116B7B,&HD9F85EAA,&H94626BD0,&H801CADC1 + Data &H42D97FC4,&H75839A8F,&H9EC11013,&H3F0BE1D2,&H5A490898,&H7A2393BE,&HFCB1B934,&H827785E6 + Data &HB09142FA,&HB044565B,&H15820426,&HEF2B15D6,&HBEE2F8D0,&H4FF50C27,&H863D11F7,&H8D7DE9D1 + Data &HC3716E85,&H49F1A37F,&HCA20A640,&HFE4B6E74,&H07EFE5C7,&H42D7D211,&H571BFD2C,&H86255D58 + Data &H05EA5A5E,&H70F80705,&H0B5D13AC,&HC73B0E88,&H595EF477,&H4F826898,&HEAE64603,&H7BCA58F2 + Data &HA4A5BDDF,&H919E192D,&HA56E2D3E,&H5ECB2791,&H5343C28C,&H7FAB5680,&H83D6AAFC,&HF089138B + Data &H289BFC4F,&HE0997415,&H1D4BFAB9,&HB17513A8,&H8AA64019,&H0A2D81C9,&H5FD577DE,&H8E9DCA3E + Data &HEEF110FB,&HEF9ED31E,&H7A7FC960,&H80BEE7A4,&HEE158CCF,&HB13F03E0,&HA4F9AEB7,&HC1B223EC + Data &HA1F9366D,&H640392F2,&H72CB3317,&H2DA64900,&HF8A0129D,&H27B1BDE7,&H3AFC86BC,&HE2B26BCE + Data &H82B46DBE,&H99D827F2,&H1FE24FC1,&H4508B63C,&H8E57E9AC,&HE919CA8C,&HE6EFE2FE,&H9656369C + Data &H4C413FB9,&HAFD1B27A,&H6153B35A,&H4C358FEC,&H9C147744,&H828BE62D,&H50F78AAA,&H68903835 + Data &HDBC94D44,&HF058F579,&H272DB6BD,&H46A012CB,&H5418014A,&HB926AD8A,&HC8C7751F,&H7301B17F + Data &HD43C58EF,&HAD14F43D,&H7E66F5A0,&H42858C0F,&HA1912520,&HF1F60A8A,&H25E39FF2,&HE533EC9D + Data &H0F9514C3,&H9AB885CB,&H8E3D493A,&HED531DD3,&HAD7F1492,&H3D58E0E8,&H44F5D88A,&H13D28726 + Data &H63FDD5DF,&H70788096,&H00EFF1FC,&H229976FB,&H854B4381,&HDB818835,&HCFC6A107,&H9B6D9D8E + Data &H5D2F734F,&H45357E31,&H7D41FE64,&H905CEA72,&H3A17B3B3,&HF35E374E,&H3E55B99B,&H6F47047D + Data &HF1BFEFE7,&HBD24A1B9,&H308D5B31,&H3C5CEE37,&H9B8CBF44,&H2E9E7FF6,&H7E80F01D,&H75E53D47 + Data &HB24C9D53,&H34FCAE77,&HB29EBCE8,&H545A6D0C,&H86818F3F,&HC62C1672,&H0610249F,&H6F38777C + Data &H929B4874,&HFFFB3B3B,&H6107CEF9,&HE35EB5DE,&H98083F6C,&H417C254B,&H8AD87F14,&H5EEB6D49 + Data &HB3428751,&HF815D53F,&H6CA87199,&HFD3972F0,&HD93538D9,&HCC21AD5D,&H6F646ECC,&H2BACF0A9 + Data &H7AC6B64E,&H26A5E03E,&H55CB6F71,&HAE24F7AC,&H2EE6FBE2,&HBCDD76FB,&HFCFBA87B,&HE1D7F041 + Data &H46AE5DE5,&HBB892A7A,&H98191353,&H29D48CFB,&HAF0894DC,&H672289F9,&HC7298AFC,&HD9B75B1B + Data &HACE98239,&H20551F61,&H96067D89,&H1633FA68,&HC5583626,&H380825CD,&H1EB94480,&HE24CB549 + Data &HD98A3080,&H57030B38,&H0AEB7170,&H5691B7A1,&HA2FFCF4C,&H6EEF2E02,&HA52FC35B,&H50E372C3 + Data &H2392DFDA,&H17C27DF1,&H210B44AA,&H73316B6F,&HE2A85D58,&H825F6621,&HB25779E4,&H7A1B3ACC + Data &HBD4B0CE9,&H46D10873,&HC692744D,&H5FB0A280,&H358AC83E,&H2F0B5EB4,&H0006C8C1,&H3036BA9B + Data &HFD16CAB0,&H8E0D3FD1,&H8322A48E,&HC761F7A1,&HCE9DC191,&HB6E48714,&H1936D812,&H158E0C17 + Data &H14F6131E,&HBCB0E794,&H9BAC2841,&HE18E551F,&H9193D887,&H2A2C9D75,&H04E27C68,&H2E812507 + Data &HAF382F88,&H994709FB,&H0962B050,&H257A2E00,&H2CEDAD6C,&H64F3ADD1,&H4935C7B9,&H7CB084C4 + Data &H2F9FCE8F,&H82C1AC4B,&HF0CC8055,&H9B49890F,&H746DBFB3,&H423F5FB7,&HBD9DD269,&H96BA5B01 + Data &H1551ED68,&HAACDBCD8,&H6D6303CD,&HC590D670,&H182A5004,&H4DF4005E,&HC903E2D7,&H46374F9E + Data &H7CB5DBD5,&HC225E082,&HC8849A06,&H561E074B,&H820D4D59,&H763C0328,&H6C6C5237,&H94686C05 + Data &H510CBFA0,&H97C1ED7D,&HC89AC731,&H366279D3,&HA1EFEB4A,&H82560ECB,&HBFB81BC1,&H516C43B0 + Data &HD5EFD12A,&H5542B33B,&H1BD48B6F,&H450B226C,&HCFDC21FB,&H15C5905E,&HDBABFC06,&H026EDF5D + Data &H89EC2E35,&H4B29CF99,&HA22B028D,&H27B02E09,&H49CF4E0B,&HDC638952,&H69E20465,&H152DB887 + Data &H1E5A82D8,&H660FC993,&H7E620241,&HA2D365AD,&H8F6CC4D8,&H1DD83646,&H42E26D35,&H2538B886 + Data &H9F7271F0,&H621B5144,&H3B9B8E77,&HEB8B87DC,&HDA17CBAE,&H396C4CE5,&HF1048414,&HFEE66344 + Data &H6B614279,&H0E597BED,&H67FA2590,&H59B78A9D,&H12B25824,&HF2C874BA,&H7D01309E,&H3C898CDA + Data &H5B9925F8,&HF1B53875,&H90F3792D,&H3FCD6AF7,&H2A5A153B,&HE5BC306E,&H7AA54C38,&H10BB12F1 + Data &H9A13BD4F,&HF748FA52,&HCFB2C764,&H2DA50729,&HEAED86B9,&H20937FEC,&H5C447EE8,&HB0DCD3CD + Data &H5185A531,&HEBFC7FF7,&HD74CDA9F,&HC1C986BE,&HD3E71D19,&H26F05233,&HEDD2D9FF,&H9D76D5FF + Data &HDD054D51,&H01693436,&HD29BD092,&H454D2E94,&HD200928A,&H415483A5,&H03101010,&H4254520A + Data &H05528A53,&H5D2029A4,&H546020BA,&H45095482,&H25288094,&H51441074,&H2799243A,&HDEBF7DF1 + Data &H7BD6B7AF,&H77BCFEF7,&H66BBAF65,&H99F66732,&HE929F67D,&HF310BF48,&H44755104,&HB3E45DBB + Data &HDF6C8E49,&HA3A5618C,&H16F43DED,&H3920B825,&H8F5E8206,&HD54040C6,&H9FB873F9,&H5A9D7BB5 + Data &H44DF4C43,&H580145C7,&H2B700812,&HDCF0FB03,&H3B1CE392,&HEC985A21,&HF36B934B,&HF2FFE66D + Data &H7FC39FE4,&H1A124240,&H18523E59,&H7B4E344E,&H094D5981,&H5C6121AD,&H17592C67,&H1FB5A969 + Data &HF14F2B4B,&HF810C5E2,&HE170AB89,&HD70BC8EE,&HE831F48A,&H200C692D,&HF3EC2C1B,&H9862C141 + Data &H9DC19D8D,&H8A25D73D,&H81745C63,&H488305EA,&H9AE342F3,&HD2B4EA91,&H8495DA6F,&HEE4193F5 + Data &H0DB8D471,&HDDE3E9CF,&H76C23355,&H83D93E0F,&HF066AEAD,&H91FA93E4,&HF35D41B7,&HC2900346 + Data &H68F02834,&H3D274060,&H1F6B5262,&HCFD1657F,&H4E0E9668,&H18D2910E,&H05FE4A0D,&HC31B5972 + Data &HB8325FFD,&H98BF3D44,&H697E223B,&H7E319922,&HD3FE9FD6,&H4B99EFE2,&HE8F29626,&HF80C65AF + Data &H2F254C06,&H3F8F2FB5,&H03FC3B42,&H634279E4,&HE85023EC,&HB59064A2,&H993602E0,&H60D14BD2 + Data &HC9579B95,&HFF9E38CF,&H7F4ED6D2,&HA0264B80,&H4083671C,&HE47B1AE4,&H62E59604,&HEC6E6CE6 + Data &HB88B2F7B,&H793046F6,&HD58DBD58,&H0124F798,&H5DA8278A,&HE6265F52,&H02D3BF56,&H36B50F1B + Data &H96246252,&H4644E9CE,&H08D7F552,&H4CADFE3C,&HF225BE21,&H4AA407E9,&HE204133B,&HDC41011E + Data &H7837BA7A,&H42830B0B,&HCC62C0FF,&H96F3E869,&HEBF6A052,&HE4086510,&HDAB40751,&H2DF7F21F + Data &HD2C36B26,&H8687A439,&HCD569AC7,&H8FA1FCE5,&H1BD71756,&H7549AB0B,&HACC17021,&H4C68CF8B + Data &HC5A29045,&H47E901D9,&HCE606805,&H7CA93887,&H7FC627E2,&HEA68C259,&H7C37CB1F,&H3F1E1537 + Data &H6034CE0E,&H4E2651CC,&HA77211F8,&HC6F156A5,&HE110442D,&HAD7177F4,&H213E9FB5,&H7AD1BD2B + Data &HCBAF33EE,&H626C867E,&HCF9B4A0E,&H39198BEB,&HDB847F68,&HF176F13E,&H2EB9B5AB,&HCCC1D0F3 + Data &HFB1D133D,&HA6692FE2,&H143F8AFC,&HF5AFCC34,&H0F697551,&HC9068AC5,&H3491926F,&H37E362A9 + Data &HFA319631,&H786632A2,&HFAA6E0E1,&H0203425B,&HF098367D,&H304EF0E8,&HF648857D,&H9B261FDB + Data &HB8ADB32D,&H6BEA82A9,&H1A98B63D,&H9E75751F,&H3D72B346,&H6577C9BE,&HB71775DE,&H5DD41F3B + Data &HCED939A4,&HD41A783D,&HEAA90482,&HCD6CAF88,&H47C12852,&H50BAD4B6,&HD13FCE47,&H4C3E872A + Data &HD9C195ED,&H8569E5EF,&HA757F4A6,&H4138B7F3,&HD27CFBF9,&HF4922F0B,&HD9B9AEF8,&H007183C4 + Data &HBFAAF850,&HBDC5A3FC,&H8E996C41,&H57DA4C7C,&HC6D438CE,&H83D4B6F6,&HF4DBC62D,&HDFD65511 + Data &H46372C64,&H1D66F499,&H29D14524,&HA92D05EB,&HBE4257D1,&H4EFD4037,&H69EB525A,&H3E88113D + Data &H68EBBFBA,&HCC89704B,&H56090C49,&H56602E06,&HFB2536CC,&HC9C6B446,&HB553CC5C,&H8BD8E8EF + Data &HEA76B538,&HB5CE7A5C,&H41046AAE,&HFDD6F0C8,&H1349E84C,&H699898E7,&H40878715,&HC0639AB6 + Data &HA24BF176,&H866E9919,&HC5D6FF71,&H491FE93F,&H79010B3D,&HC3B018DD,&H9F238161,&HA7FFE833 + Data &H10A66587,&H1B3A5720,&HF0CC2C7F,&HA40F9227,&HEF26F375,&H93555571,&H31CB3DAF,&HA2DBC6B2 + Data &HCC2053A6,&H8B88363E,&H7FD6AAF9,&H65D2A444,&H48EC0755,&H95726163,&HE7980CCE,&HCC692474 + Data &H75B70226,&H8BC3DC33,&H5A8D97F2,&HD83E693E,&H05ABE7D3,&H3F2C43C0,&HC8FCDAD0,&H8DD37227 + Data &H2ECEAA0E,&H4C1E3109,&HF46D2BB7,&HA568E18D,&H7E218CF9,&H3E5D14F7,&H8682240D,&HC6C7C61E + Data &H302C811C,&H5EBBC612,&H9CAF4A39,&H48131AE7,&H0EA456CB,&H4AEE709B,&H702C9DE9,&HE88E9893 + Data &H4AA8AFD5,&H88CECDCD,&HDE97E370,&HB283CBE3,&H28E12D9C,&H9DB0F4AD,&H3D2C1418,&HF09B2BEE + Data &H449D28DE,&HE6A36090,&H26729F2D,&H19EF0F74,&H238F20C2,&H2A778A93,&H10F69EAD,&H1A391C2E + Data &H3FA48919,&H9E789902,&HBBE130DE,&HD4598A10,&HB5F263FB,&H1722BAE8,&HFAC5B042,&HB682A373 + Data &H46BBCDB6,&H5CBBBEAD,&HDA47DF5F,&HAFB5E555,&H406529A5,&H4E7D3BEF,&H2F93953C,&H3D1C7DE1 + Data &HE8BFB7E0,&H09F2EF23,&H11D05A25,&H1BF54DC7,&H9F7050AC,&H0659009D,&H27AC3D54,&HE51BDA0C + Data &H9ADC0D4C,&HB4EACCA9,&HF422268A,&H973F8337,&HF7F3A11E,&HD51A1566,&H325D1F00,&H37861FFB + Data &H9F027A6E,&H6C011C54,&H3B87F880,&HC18F82A2,&HE2188777,&HA5A17DFE,&HD8676B57,&H25D83C1D + Data &HA5269F43,&HDB2171CA,&H354FDCE0,&H710E0A0B,&H07D910F4,&H452F0092,&H8DF2DF4B,&H44C2790E + Data &H5C5F523C,&HCA5642D9,&H6AC0F332,&H002DCE23,&HDB8DCD63,&HC3A6427F,&H347127EC,&H2C6217EF + Data &HF811C169,&H955CB52B,&HFD364066,&H766289BB,&H2D75E565,&H673B02E0,&H886BFA91,&H51E5AD20 + Data &H4F58ACA7,&HF881565D,&HC291F82A,&HB2BEBBC5,&H81FDD4CA,&HC7195C2D,&HD9DE197E,&H6FC1DF12 + Data &H64CD500E,&HAB32D2D4,&HD81B96D6,&H67D8D265,&H1F2FADC5,&HC9E47AF9,&HEE405ACB,&H9D822388 + Data &H63CB2A80,&HF00C5978,&HA0FAD665,&HB9B26D5A,&HE356006A,&H2045862D,&H2C482FA7,&H9F58A486 + Data &HC3E03ABF,&H93F73F7F,&HB1017E43,&H428C7213,&H5BF5A34B,&HFDFDBFF4,&H49687A57,&H2196F8D2 + Data &HF093921D,&H22F9FC7E,&H95080046,&H5974E800,&H59701E30,&H13ECA25C,&H7946ADAC,&H5A2A8259 + Data &H79BC167F,&HFBEDE6F3,&H9CBE6E00,&H3B5978AA,&H65CE40BD,&H50D2A8E1,&H74B036D4,&H82055649 + Data &HF39598AC,&H920D4D98,&H9D31FB25,&H872A8B47,&H9BBFCF82,&HFCD473F9,&HD6210628,&H32514BC3 + Data &H0D71AAC2,&H930F085A,&HD6E99F36,&HA4CA81F7,&HA9297094,&H239FB78F,&H20FB27AB,&HE3B1E9AF + Data &H33AB48D1,&HBE2C4CF3,&H65A1B90E,&H4BDF7B1F,&H3BE73D04,&HFD0C3ED5,&HD74E19FA,&H699F6A8E + Data &H60FDC6BB,&HBABA15F2,&H7B5735E2,&H029A8686,&H10ACC150,&H510D7047,&H5443B9A5,&H4E54F27F + Data &HC53B4001,&H37E2D83F,&H00C3CA0E,&H994F12D8,&H8315312D,&H2A175FA1,&HFDC8AF27,&H3CAD546C + Data &H21FF66DB,&H2A4229B1,&HC378EA5A,&H446923CC,&H5C6C4498,&HDB43ED4A,&HCF166758,&HD4E82558 + Data &HC50B8D60,&H0EEB24CA,&HBA560B0B,&H790983FF,&HBAFC1DA4,&H961659A0,&HA7FE746B,&H2F9669F6 + Data &HBE7C9C54,&H650460A8,&H05F1A128,&H254546F6,&HA4C0640B,&H91C3C6E2,&H5F75579F,&HD6643BD9 + Data &HE5BEDE94,&H32A83371,&HF52A7034,&HCA8B5DF8,&HBE2813F1,&HBD8E52C5,&HE6EA9C97,&H2BB5E561 + Data &H9C8D77B5,&HF7A67AB6,&H7C2B3D8F,&H993F7E26,&H91B442D3,&HD5EBAE4B,&H91EAA8DD,&H04B0A8EE + Data &H1ACC7DF5,&H3A8B5AC5,&H1DFC593E,&H36948CE2,&HB5C1F1A3,&H491BF9AC,&H19A0D268,&H9BA8C408 + Data &H771540E1,&HF1339492,&H886655C2,&H9BE3CC48,&H644CF218,&H630777A2,&HC668D053,&HBAD7C7CF + Data &HB6C7C80F,&H19BF4038,&H05A1B305,&HBBCA72E6,&HC91C9536,&HC73613F4,&H55F34340,&H2FB2E51E + Data &H50F284E4,&H8F041103,&H8FB51FBC,&HBE0423CD,&HBDE65974,&H9BFE8B6E,&H5708ACC2,&H014C177B + Data &H66A60D8D,&H389738F5,&H19979BDD,&HD7943139,&HBF3C39CB,&H4EEB8E22,&H2AECBA2C,&HB1333AF6 + Data &HFC153EED,&H97D4F608,&H0F1DA75E,&H55801C9E,&H6B68C3BE,&HE6C8CC4C,&HF67E4A9B,&HC73BDF10 + Data &H78FD601A,&H3EFF5608,&HE1DB424D,&HE0B3182E,&H2F9EA4E9,&H8A096E20,&HB012F853,&HFBFDE53A + Data &H905DF4AB,&HC247C2B6,&H75D3274C,&H370C20DA,&HF8C60070,&HA9662D2C,&HC056FB90,&H04D5C0B1 + Data &H25B7BEF5,&H976E20B8,&H10375C51,&H8D734018,&HF61A5D3C,&H8F03F740,&H5E679929,&HD46CAFFD + Data &H2A7D1635,&H7DF8D407,&H6A7BEA70,&H778B2A8E,&H25F27CCE,&H8B7D524E,&H7A1CF8AA,&HBAA57778 + Data &HFC5E5FB1,&H892DD152,&H737EBE30,&H9369DEE5,&H560367ED,&H057BD34F,&H3BF70C3B,&H56D0F42D + Data &H215CF7ED,&H4C4A0231,&H5E7562F4,&H980930A6,&H87848085,&H628A4F00,&HB71B2B18,&H2D3F0CF9 + Data &H6CEBC12C,&HDBE0B967,&H68634E16,&HC978BB87,&H90B942E2,&H6729FCB6,&H01CCA17D,&H44A5DEAA + Data &H7583A720,&HD69158CF,&H69C941B1,&HD037B745,&H729DA065,&HA2F09E8F,&H31826019,&H928C0BFA + Data &H66379E69,&H6CEE55EF,&H7E1468FD,&HD7E0FA57,&HEADF65F0,&H1C052B1C,&H4AE250F0,&HBC78B8BB + Data &H4690FDF3,&HA92B231F,&HF6A0FF89,&H7A73868E,&HF6FE6A80,&H75D177C5,&H69D6ABEB,&H8D1BAF57 + Data &H1E2A479A,&H54C864E2,&HE56BF6CE,&H883C41D8,&HDB0B87EB,&H43ADD9D0,&H17592266,&H63F89306 + Data &H2E3D2C8F,&H2FEB6E66,&HC82F4DEC,&HF46A6C3E,&HE93F9B1C,&HB2E7B8D3,&H3839F030,&H15290A25 + Data &HD0161B48,&H1650AFEF,&H3956D5BE,&H677644F5,&H11F6C9FC,&HFDD2CFF1,&H9E1BC567,&H592CAAAE + Data &H8B5D0D93,&HA965F8BA,&H968825E7,&H6334CBDB,&H0D2A4830,&H0C9A471D,&H9B160643,&H2D53AA21 + Data &HD0C37D54,&H59EA4A03,&HBD1D3FD2,&H06BD10A1,&HCFE4665E,&HE580E34B,&H351B1D1B,&HEE6FD077 + Data &H1FF02607,&H189E2683,&HFA859039,&H19135D92,&H7953979B,&H43BF7CF8,&HF009C9EE,&H04A25567 + Data &HA56F620B,&H0466F92E,&HEB506CC9,&HDA606C58,&H52BBE295,&H4AB858BA,&H8AD2EB40,&H58EBC0F8 + Data &H400BCB20,&H202FEDC1,&H76E19537,&H00B5F92E,&HE86774A0,&H940FB9E6,&HC3751DF5,&H85C9C03F + Data &H4E533009,&H819B70D1,&HE294D14C,&H4F573FD9,&H4C0FC896,&HA005BF7A,&HF3F85288,&H4C45225B + Data &H2D686C06,&H3F10484B,&H2A5E0E85,&HD54ECB38,&HD8FEC087,&HD36CFEAE,&H8F8DBC6D,&HC7A3C3E3 + Data &HAE823F06,&H251B01B1,&H4A45A57D,&H0F6D056C,&H69CF1325,&HB8C5D164,&H6FBA890F,&HD65CC9CB + Data &H6E17EFD4,&HE3249CE8,&HC0F5B86C,&H77F7D279,&HD2CBCDAE,&HA282BABC,&H1103F922,&HCAC979D5 + Data &H191C3CD0,&H360298A3,&HE59251B7,&H98DE92ED,&H7D54B39D,&H67C403D5,&H0B5DC647,&H180A937C + Data &HA15C25A9,&HEB48FF41,&HAEDDC464,&H89313143,&H78DDAEC2,&H494F89A1,&HFAB675FB,&H566C4BCC + Data &H246E43F7,&HB4D8AC76,&H28FD087C,&H130459E4,&H02E546CA,&HAD89C7C2,&HC7804EA2,&HAB78A204 + Data &H6D405A5A,&HE487F521,&H42F2F686,&H055970AE,&HAFF9A7D7,&H85572C9D,&H01E750CF,&H8DB2CC42 + Data &H120A5EC6,&HB92EF0C6,&H3E6C52A1,&H55399687,&H286C9B45,&HEB0CBA7C,&H78D352A1,&HEE8C5CD9 + Data &H5CDDEB51,&H496E80FE,&H426A8163,&H63454D91,&H02F8D60E,&H988C9EA7,&HDD49DCDA,&H45FB850E + Data &H0B6527B1,&H75B1FD26,&HDFA689CD,&H65D1AA15,&H8B9843D5,&HB32AA67A,&H3975584F,&HA9ABD7CC + Data &HDF255BBA,&HAC8616A9,&HC8061A02,&HED114659,&HA70E4D28,&HFDED10A6,&H08850C03,&H1B7E7D76 + Data &HB9B4A727,&HDB9824D1,&H81685A55,&H6B5DCA07,&HD4F2FC27,&HD4651E59,&HFDB8AF82,&HA4EFB8D4 + Data &HD9306C0A,&H0899F888,&HE8C3C8CA,&HD31760D5,&H0AB2B5D5,&HE31DF2DB,&HF93837D1,&H9EDC3C1A + Data &HFBEA9E69,&H8C35C9A5,&HBBFBDD56,&H91FA0B7F,&HFC53EDE4,&H9BCEBAFA,&HD72BACC8,&H13F56C62 + Data &HB6CF4444,&HB9E92369,&HCA46BC6F,&HCB39CFD0,&HF9AD7D4C,&HFF7D6971,&HBC8765EA,&H39AACB7B + Data &HDAC1A6C4,&H9018D027,&HB94F3392,&H67ED81DE,&HC062BC84,&HA022A6E6,&H278A05EC,&H0A35455F + Data &HD43E460E,&H8D41B1AC,&H75C9FD53,&HC756FCE0,&HB4B7CEFE,&H7D2ED742,&HB5F16C68,&H7D0DC266 + Data &HDF662319,&H6AF66D40,&H04ECA6B6,&H8BCDB879,&H89E8ACF8,&HFF237461,&HDAEA484A,&H73E35A91 + Data &H14FAD30A,&HFC50D7B6,&H595E199B,&HD2370382,&HD1B598FB,&HAE03319C,&H25E0BB57,&HEFC73139 + Data &H01A9AF61,&H23DD5A1B,&HFA3E7BDF,&HB755359E,&HABE70F61,&HB0748099,&H91F9675C,&H84E3F5B6 + Data &H157B29E8,&HB6DB8676,&HA891D5E7,&HFD2501A0,&H36AB8E55,&HAD56A18B,&H5A10356F,&HB2FED86E + Data &H32CBBA65,&HEA7D652A,&H4C580D29,&HFA38BDF1,&HBA6852BC,&H544A2722,&HCD06F0B9,&H9637BE43 + Data &H2C330C57,&H1F1EA214,&HE934334D,&H696B263F,&H56DEEDB4,&H4247F542,&HFAD2EBC3,&H1350A321 + Data &H1CC5FB2D,&H2B269BDF,&H5CECA50B,&HD73D369A,&H12152EBC,&HD4DCF6ED,&HDC5C158E,&HD844420D + Data &HFE721702,&H93B50609,&H31D1BEDF,&HEDBD0846,&H1B1E179A,&HE164E7A6,&H64327778,&H5BDF526F + Data &H50D84966,&H457D3038,&HADF8333B,&H3BE78D65,&H4255BC15,&HA33482EE,&HD51AFCA4,&HF39A2ECF + Data &H641B60EC,&H387D7614,&HB574745B,&H3BD49E45,&HD39AF939,&H9F589958,&H10220444,&H143E1173 + Data &HF403158D,&HFDE467E4,&H692E5305,&HC2E2892F,&H5888C61C,&HC29C861D,&H3FB086EA,&H2610D0B1 + Data &H1B3AFB93,&H4766F80D,&HC1E5FB7D,&HCC0D971C,&H2ACCE5FE,&H077A24B2,&HAB81E837,&HBBD9330C + Data &H88DB8D4C,&HC61278D7,&HE3568A58,&H6FF9D3B5,&HFF4E4777,&H196B20DA,&HA8ABA45A,&H2CF82568 + Data &H4D9828BA,&HF4F67F15,&H4F772F96,&H541FF39A,&H2E9DC576,&HAF9E9AE7,&H77942B1F,&H3B387FB1 + Data &H87A3992F,&H866097CB,&H77E670F3,&H5751DA61,&H527A468A,&H755EAAB7,&HD7615CCF,&H7B1523BA + Data &HDDEFD439,&H9E351892,&H2C362A16,&H5DB38412,&H22339A21,&H7343DBBF,&H321B0ED4,&H489C6767 + Data &HAC7EEF64,&HE6EA8B7B,&HD3AE4670,&H176EF154,&H584F0DBD,&HE5195213,&HA2969CAE,&H7630F8E2 + Data &H03E5D92E,&H349E6D71,&H9F54E203,&HA824FEE6,&HAD137D11,&H19D08070,&HCDBD51C4,&H11A91349 + Data &H12B5F154,&HBF0ED05C,&HC6A38875,&HB8245CA5,&HF68A41F7,&H7DAEC9F4,&HADECB1AE,&HE2495E2C + Data &HFC33C137,&HD6E68646,&H05820B75,&HFDD356B8,&HB655C50C,&H0248A144,&H5BE2C5B3,&H1A027A5E + Data &HD62A5368,&H0794AA71,&H184A2B0A,&HDD7EEAE0,&H5815AEEA,&H096405A1,&H2BBD20D1,&H66C03C17 + Data &H2AD15111,&H0CD59015,&HA8AD8B5C,&H581D6F3A,&HA1E4A087,&H06F6A6AE,&H55C50F6E,&H93B9F090 + Data &HF5B57AD3,&H16726FA4,&H96D645CF,&H0BDB3BB9,&HD8D765EC,&H7BE7EABC,&H281B62F7,&H1F88A32F + Data &H12C11CD5,&H918BFEC2,&HED446CEA,&H772B1874,&HBE266FF0,&HC1A388B3,&HAD04A2FB,&H87485FD1 + Data &H1617BF72,&HF8095E2A,&H37BB5DE7,&HB38D4D11,&H859E5479,&HDC65CE90,&HC2660D45,&H25999872 + Data &H6B33A8B0,&H61DD7BE4,&H31780321,&H8D7C7DA8,&H051DCF29,&HBB35C154,&HEBC1FDA1,&H666F5B28 + Data &H564F2756,&H53897A45,&H035BAA6C,&H9F637870,&H98AE39F1,&H1F4FB91C,&HC38D5B09,&H8F6610E7 + Data &H06BCD3BD,&HBF351DAE,&HAE64D475,&H5239BCFD,&H8E37A992,&H4F5A9139,&HB67BC7EB,&HF4377583 + Data &H4A9D0DD1,&H11483BCE,&HCD9D3D91,&H4D1FB3B5,&HDF4E0E0E,&H728D3B48,&HF0DEFEFB,&H51F8C1E3 + Data &HCD4D4F4F,&HEF42ABA5,&HDE7A27A5,&HD09F11B4,&H39ED7B97,&H0FF0CA50,&H937CB35B,&H26B314CE + Data &H80C65784,&HF31D12A8,&H0C540821,&H613462FE,&HA4CB82B8,&HFDA6569B,&HDCD45BB6,&H77553694 + Data &HB06D7DCB,&H1141C691,&HE1CB34A2,&H9CE0F63A,&HBDBA4841,&HCA373111,&HFA38597F,&HE78A3D2D + Data &H2B547EA0,&H4844FDE9,&H90AAD24D,&H4409F4D3,&HFB35656F,&H4D682DF5,&H05D98C92,&HE06B4C19 + Data &H73DFC7B9,&H178A87C7,&HE7BF0796,&H20D19E58,&HB6486A9C,&H7E8479D7,&H269D8137,&HDDC92E90 + Data &HA41BE83D,&HCBF8B1D6,&HB824CE3C,&H0C41DB64,&HC9E2AB2B,&HFD6B234D,&H648B7545,&HB83E3B9F + Data &H63483705,&HF45278A4,&HA973F595,&HD05D6206,&H64E7BC44,&H57DC1DDE,&HCA3EC1CE,&H44D54161 + Data &HE349BA71,&H7D0CB217,&H7884FE1B,&H77328698,&H7F27B187,&HD7C67176,&HEFAB598F,&H6C77061A + Data &H99A1E79A,&HADBEBF9E,&H0881E752,&HF4F6CAFC,&H1F96DC61,&H4E48862D,&H66F24F82,&H73937280 + Data &HE0F12878,&HEC6C6456,&H879E76A7,&H29652E49,&H8173463C,&H8AB4D581,&H0A6363A7,&H8E8CE862 + Data &H3AE64912,&H7AC8834C,&H9DDBF05E,&H843C3E54,&HFA041259,&H38A93366,&HF3C3540A,&H66031EF6 + Data &H3B819E05,&H104C1DC8,&HFEA18AFD,&H1C734E7F,&HD4432CF0,&H5D2B5F3A,&H60247ABA,&H1C1B0C1C + Data &HB4746817,&H0A0D1387,&H4DD05C8C,&HE1083507,&HE19DE68C,&HE3B95C31,&H306D9C1D,&HDC395EA5 + Data &H2C19AD3C,&H07185ACB,&HC86DC53D,&H77C3038E,&H805FC1B0,&H88419A9C,&HE0A0515C,&H88AC2D6C + Data &HE6D90032,&HC18B4F6B,&H4A71B047,&HCBFFB96E,&H720AAFC7,&HB08CD450,&H8381EC23,&H80E5237C + Data &H2FE61162,&HBC2122BF,&H24560798,&H6245D6CC,&H423E9FC1,&H5511C2DF,&HB2D8354A,&H149029F5 + Data &H0D0335AA,&H1E672D5F,&H46766F05,&H5B8D35B7,&H887D4E88,&H0974F750,&H53755D7A,&HBEA4DF10 + Data &HE1772387,&HE704C3C4,&H8E2860A0,&H4B522598,&H582F1EFD,&HC523075A,&HA3BB13A2,&H3CB343F8 + Data &H42E8AA1B,&H1CC65129,&H3C098454,&H0BAB0668,&H18215921,&HCB62B0D0,&H0A1B4A77,&HB4A243A3 + Data &H2A3CEFEB,&H522E16E4,&HF9EDFBA3,&H6A6455C0,&H74E89758,&H8BA9D645,&HA044DB46,&H4F5E8690 + Data &H6B410DA2,&H325E8AE2,&H5A3B5E29,&H7EDF242E,&H01D6ED5D,&H06133FB5,&HEBF021B6,&H33989F30 + Data &H1FCF45E2,&H3E4E7EA5,&HDBCB12D6,&HF723B2AD,&H9FAEA8EB,&H4F345B9F,&HD9DB9813,&HEAD0BDF9 + Data &HBEF72C51,&H7D96DCE4,&HCEF06FAD,&H09A3D8AD,&H9F6E47FA,&HD0FEFDF0,&HB0FD76B6,&H56EEDDA7 + Data &HE7EEC532,&HEEE7C5EF,&H06DB35FE,&HA75E7366,&HBFAD17C0,&HADA9B2BC,&H93FF5A97,&HF99748A9 + Data &H7D7F30FE,&H87DDB7EA,&H15B9CAA0,&HCFF3B0A1,&HF4275958,&H6D4FAAF6,&H261F90FD,&HCB0173FE + Data &H47B7AF54,&HA0912FAC,&H4DFCEA12,&H5D9148DD,&HD3726BBA,&HAEDA8F42,&H8D3E47CF,&H9BF11291 + Data &H31A543EE,&HFE2A2524,&HCE2AFC44,&HC12611C4,&H2EFE993C,&HF0962AB2,&H79BB9B03,&HE5C4A388 + Data &H2648C4BA,&H5EB968C0,&HF8985065,&HF13949C2,&HB04CD38A,&H4E426C0B,&H72892BE6,&H75BCCE77 + Data &H767219EE,&H77F571DF,&HCFDE4F62,&HFC5FA290,&HA7B0514B,&H1205C1F2,&H63937192,&H5F79B65F + Data &H81D099BD,&H2A2F6701,&H266FAA89,&H07066C83,&H728B4140,&H112962A1,&H738AAEE3,&H7EB53C14 + Data &HCDC39D4E,&H6E177645,&H98F6D4D4,&HC477F292,&H246431A2,&H3776C16C,&H57E9B3D7,&H8542919E + Data &H181F9C5C,&H77C0D142,&HA489199C,&H6C8BF005,&H66DCB605,&H801F5AB9,&HE089114F,&H259DAE4F + Data &H202F4B0B,&H15765E7D,&HBF7DC0E9,&H18BCDAE7,&HEC37BA41,&H605CF986,&H71F62FCF,&H616082D5 + Data &HBDA7AD4F,&H51ECB704,&H60C768A0,&H99E65FE9,&HA2D68CBF,&H2926DF3C,&HB8C29D4F,&H114B7E63 + Data &H820BEF65,&H5B853C8A,&H011D4FCD,&HF10A0314,&H0BCDD2DF,&H81C17A8C,&HB571C858,&H636566AD + Data &H0F68D550,&H439421A7,&H912D186E,&H14DC726D,&HE63FF18E,&HB00B81C7,&H9C312512,&H188096FE + Data &H84D8EF71,&HFDBE3FEB,&HE28383A7,&HA17D89DD,&H6711320A,&H0CF72C24,&HB950C9B6,&HC4FEDC38 + Data &HA7733CAB,&HEDF0174C,&HD3DBA783,&H0EC5E10B,&H39806F4D,&H621E8FB2,&H37034B48,&H22B8E547 + Data &H26E9B835,&H8268526C,&H0EEA3B21,&HB192A522,&H7C21F6FE,&HED9ED369,&H27A6C525,&HD0AE05CA + Data &H8F4F082E,&HB25C7207,&H7DB7C7F8,&H918157BA,&HE292ED14,&H3AE7E523,&HB6ECBA29,&HB9D1B2DF + Data &H0AE3638F,&HB7CBA303,&H3F9D570C,&H025D618E,&HD40C24B2,&HB53ECF44,&H45F4B176,&H8F668723 + Data &H333D146F,&H823BBA4B,&HDE2BE65D,&H8E20FD6C,&H146489AC,&H50286259,&H16CEAF7E,&HEADC5087 + Data &H76AAB331,&H3099A8BE,&H3828DA6C,&H9D23ED45,&HD8B07F22,&H00569C15,&HA74A1B99,&HEA18485D + Data &HD277107C,&H248EC791,&HAD612DA9,&H81E3AF10,&HBF1F5F01,&H2484A403,&H5E1CCD65,&HA693B6C8 + Data &H6883F657,&HF089483A,&H23DBE147,&HE9858F50,&HE73CDD79,&H46D862DF,&H2B74E118,&HD900071A + Data &HD5B0EE22,&HFF03301E,&H2F7DA8B9,&HAAAB1055,&HC18CBF47,&H78296A68,&H578C9A9D,&H4B818D09 + Data &H6055532F,&H86894219,&HD56E03C7,&H16A58EE7,&HE50945C0,&HFA1ABC34,&H8B5B1102,&H98E233C9 + Data &HF3CA68FD,&HF73CA8A9,&H339D34B7,&HA13D471F,&H7B3CE2EA,&H7B7F6F34,&H15F20D44,&HE320D432 + Data &H8E47081D,&H7C15C964,&HE7F57892,&H396AEFFD,&H36B38FAA,&H92557FDC,&HE3746522,&H5CA36767 + Data &HE8B644A6,&HC4D167D4,&HC9412895,&HE6D52995,&H670D9A7A,&H2D69CA2D,&H55DC970B,&HD52B49F0 + Data &HEBD6FC11,&HCDD3D9D6,&H7B6A7DC0,&H17714036,&H08C4FECE,&H027C2EEB,&H74BD992A,&H15D3314B + Data &H2BD2485F,&H45D9A4E3,&H4E6D4F7B,&H5FB47A37,&H9D233585,&HF9BFDA46,&H4E265DA6,&H4AC5D251 + Data &H353877FA,&HA4BBCCAF,&H4C8697A9,&HA177819B,&H638DC483,&H00508A5B,&H8E49066E,&H2FC50179 + Data &H942598B2,&H97D6415D,&H6CACBB7D,&HE2A7E962,&H9C123AF0,&H3D019285,&H5286129D,&H6FED8CEC + Data &H016C27BC,&HD524C6C8,&H56E155BE,&HC6403D6C,&H86958062,&H7FDBD2A7,&HF5375BBA,&H4B1B8831 + Data &H58364AA3,&HEADAD8BC,&H72685BC6,&H86466DD6,&HEEA0B6A6,&H52287A21,&HA0B25625,&H2E797E4D + Data &HB867C57D,&H83ED223F,&H734CA680,&HD959DDFD,&H3A279C3F,&H7EF42B3A,&H5A9D0EEE,&HDE12E1F5 + Data &HC19E211D,&H37BFBA90,&HCA026808,&HD96579A3,&H6DC447BD,&H7E97F44F,&HAA40CDDF,&H760A2F9E + Data &H976E91D3,&H10C9A62F,&H7F67A080,&HB799F1C7,&H88AD0444,&H845E9408,&HDD6BF296,&HEBF55A7A + Data &HAC34B6E0,&H15B54CA5,&H78B04F07,&HAF45D563,&HFF2DF092,&H727B116E,&HE5DCA94F,&HDBAF91CA + Data &H3C331776,&HB1EEC394,&HEB7D1973,&HD324FB97,&HC41CFAA1,&HA67BB40D,&HA48D4EAD,&H5FD96AFD + Data &H3F97FA68,&HDB7813E8,&HE216726B,&HEEE5677D,&H3D74C4FA,&H63A559FA,&HC3379653,&HA970D6AA + Data &H6EC90B8F,&H8A5F1CE6,&H85F1E345,&HBAD9F5D4,&HB027E3EF,&H0E75DEA3,&H16F726C2,&H541CA319 + Data &H0DF10B68,&H6646132D,&HA7AFE78A,&H67080DBD,&HE9F12A15,&H5AF9920A,&H58B01C1C,&H64A81193 + Data &H0AEDF0BF,&HD519E9DC,&H5285F687,&HB9D8E2C7,&H83C8572C,&H310EEA39,&H4282F75B,&H47A7B19A + Data &HFED522F9,&HC8D734CB,&H7C553C3C,&H97005BC1,&H82273CC2,&H542F8F12,&HBA639D39,&H769899B8 + Data &H6703B4E6,&HF9E84D7A,&HB8D0A7E2,&H76E602C8,&HC76D3266,&H630BCDF5,&H4376C221,&HF79F8445 + Data &H3EB34D89,&HB5495F27,&HDEEB5378,&HD1CA23CF,&H10C37BBC,&HF464A75A,&HD2478B9F,&HFE086743 + Data &H7F4B7284,&H3952B29A,&H78467C8F,&H15F151AC,&H8BC0B8F9,&H5B69BBB0,&H279EDED2,&H4AC88716 + Data &HB9270676,&H46981C72,&H127DB652,&H24DE579C,&HC579891F,&H9AF22BA8,&H7F18E3C0,&H38159D63 + Data &H09001D6B,&H20993B3D,&H2D0E1EDC,&HED4B5617,&HA5CA6057,&H1906DA58,&H11DEBB1E,&H81020D72 + Data &HAA1E7A1C,&HAC5A6AED,&HC4F77FDE,&HF4D83B7E,&H4F36BDF5,&H7506B913,&HB70D9356,&HD4D45F06 + Data &HC96EF726,&HDBD02481,&HBD02F3E7,&HC62063FC,&HCF62CC34,&HE5169E81,&H66CE5F96,&H394BCF57 + Data &HCA082DD0,&HAF287541,&H2F4BEA94,&H3DA6F1F7,&H3FB54D19,&H6783B1C4,&H84A17F77,&H6F5BA51A + Data &H2E88F665,&H1C3AA1AB,&H900284B8,&HBF1B43BF,&HFE9224F4,&HC6E0C860,&H0E83FA28,&H52EB2F66 + Data &H25C1B344,&H23862830,&HA9DB8CE7,&HAD36E1A8,&HF8104CC8,&HF64AA6F9,&HA7697417,&H33B60D9A + Data &HE5363B9E,&HF1151378,&H9BA9C4FC,&H3353DEEB,&H9291AAED,&H9D714F4A,&H28D1D6FF,&H6F487287 + Data &H40366367,&H97C076C1,&H5B6AF3A1,&H0440B9BE,&HEC47EA0D,&HFA932785,&HB906FFE3,&H07861D2B + Data &H50F4A746,&HA9698890,&H20182F17,&HFA5DE44F,&H6687CE16,&HB3C7B767,&H5A94871C,&H4F1CB0FE + Data &HD7772867,&HECB6688E,&HD1704BF7,&H58F1304B,&H42E452AB,&HEB8683E7,&HFF2BF4FD,&HDD39E4F4 + Data &H0976BB1D,&H72EE2F2D,&H0680B8AD,&H004F5B1A,&H9033B422,&H247C6C97,&HF7875E5A,&H60BF3C90 + Data &HB7441FE3,&H69B064B2,&H21FFC86A,&HD7FE418A,&HF0FF9517,&H77735B62,&HFCB41F15,&HFF22C7CF + Data &H57,&H37,&HF5,&H5C,&H13,&H02,&HA1,&HB5,&HFB,&HBF,&HFD,&HD9,&HFF,&H71,&H1F,&HE2 + Data &H2C,&HFF,&H07,&HE7,&HFF,&H0F,&H06,&HF2,&H5F,&HB1,&H97,&H4B,&H80 +End Function + diff --git a/tests/compile_tests/audio_mem_test/sndopen_mem_test.output b/tests/compile_tests/audio_mem_test/sndopen_mem_test.output new file mode 100644 index 000000000..9d6a7bff7 --- /dev/null +++ b/tests/compile_tests/audio_mem_test/sndopen_mem_test.output @@ -0,0 +1,3 @@ +Size = 42018 +Handle = 1 +Length = 21.10694 diff --git a/tests/compile_tests/audio_out/test.lnx.license b/tests/compile_tests/audio_out/test.lnx.license index 6f2714c22..9c7018823 100644 --- a/tests/compile_tests/audio_out/test.lnx.license +++ b/tests/compile_tests/audio_out/test.lnx.license @@ -218,6 +218,38 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- License of FreeGLUT: Freeglut Copyright diff --git a/tests/compile_tests/audio_out/test.osx.license b/tests/compile_tests/audio_out/test.osx.license index e6f82cf02..1d331b7f8 100644 --- a/tests/compile_tests/audio_out/test.osx.license +++ b/tests/compile_tests/audio_out/test.osx.license @@ -217,3 +217,35 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/compile_tests/audio_out/test.win.license b/tests/compile_tests/audio_out/test.win.license index a372f3e31..85deeb48c 100644 --- a/tests/compile_tests/audio_out/test.win.license +++ b/tests/compile_tests/audio_out/test.win.license @@ -218,6 +218,38 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- License of FreeGLUT: Freeglut Copyright diff --git a/tests/compile_tests/midi/test.lnx.license b/tests/compile_tests/midi/test.lnx.license index 8baf91e6f..b318dd964 100644 --- a/tests/compile_tests/midi/test.lnx.license +++ b/tests/compile_tests/midi/test.lnx.license @@ -218,6 +218,38 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- License of TinySoundFont: Copyright (C) 2017-2018 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero) diff --git a/tests/compile_tests/midi/test.osx.license b/tests/compile_tests/midi/test.osx.license index 18a571c59..cebef3602 100644 --- a/tests/compile_tests/midi/test.osx.license +++ b/tests/compile_tests/midi/test.osx.license @@ -218,6 +218,38 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- License of TinySoundFont: Copyright (C) 2017-2018 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero) diff --git a/tests/compile_tests/midi/test.win.license b/tests/compile_tests/midi/test.win.license index 7bc682239..7b51b71b0 100644 --- a/tests/compile_tests/midi/test.win.license +++ b/tests/compile_tests/midi/test.win.license @@ -218,6 +218,38 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- +License of HivelyTracker: + +BSD 3-Clause License + +Copyright (c) 2006-2018, Pete Gordon +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- License of TinySoundFont: Copyright (C) 2017-2018 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero)