From 82b2711cdf47b2b2b1bb34fd54d93c8e813dc120 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 19 Sep 2026 23:01:52 +0400 Subject: [PATCH] audio: play ATRAC9 sampler blocks --- src/libs/audio.cpp | 111 +++++++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 30 deletions(-) diff --git a/src/libs/audio.cpp b/src/libs/audio.cpp index db8a25cc5..cf4ae70c2 100644 --- a/src/libs/audio.cpp +++ b/src/libs/audio.cpp @@ -9,6 +9,7 @@ #include "common/threads.h" #include "kernel/pthread.h" #include "kernel/semaphore.h" +#include "libs/ajm/atrac9_decoder.h" #include "libs/audio_internal.h" #include "libs/errno.h" #include "libs/libs.h" @@ -1888,7 +1889,9 @@ struct Ngs2VoiceInternal { struct Block { const uint8_t* data; Ngs2WaveformBlock info; - uint32_t cursor = 0; + uint32_t cursor = 0; + size_t data_cursor = 0; + uint32_t skip_remaining = info.num_skip_samples; }; struct Module { std::vector param, work, state; @@ -1912,6 +1915,35 @@ struct Ngs2VoiceInternal { bool rendered = false; bool has_samples = false; + std::unique_ptr decoder; + std::vector decoded_frame; + bool accepts_blocks = true; + + void SetupSampler(const Ngs2WaveformFormat& format) { + EXIT_NOT_IMPLEMENTED(format.sample_rate != rack->ngs->option.sample_rate || + format.frame_margin != 0 || format.frame_offset != 0); + channels = format.num_channels; + blocks.clear(); + decoder.reset(); + decoded_frame.clear(); + accepts_blocks = true; + if (format.waveform_type == NGS2_WAVEFORM_TYPE_ATRAC9) { + decoder = std::make_unique(channels, format.sample_rate, + Ajm::AjmSampleEncoding::Float, 0); + const auto result = + decoder->Initialize(&format.config_data, sizeof(format.config_data)); + EXIT_NOT_IMPLEMENTED(result.result != OK); + EXIT_NOT_IMPLEMENTED(result.format.channel_num != channels || + result.format.sampling_frequency != format.sample_rate); + Ajm::AjmSidebandDecAt9CodecInfo info {}; + decoder->WriteCodecInfo(&info, sizeof(info), result); + EXIT_NOT_IMPLEMENTED(info.frame_samples > std::numeric_limits::max()); + decoded_frame.resize(info.frame_samples * channels); + } else { + EXIT_NOT_IMPLEMENTED(format.waveform_type != 0x12); + } + } + void SetMatrix(uint32_t index, const float* levels, uint32_t count) { EXIT_NOT_IMPLEMENTED(index >= matrices.size()); matrices[index].assign(levels, levels + count); @@ -2534,22 +2566,9 @@ int KYTY_SYSV_ABI Ngs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t EXIT_NOT_IMPLEMENTED(option->size < sizeof(Ngs2RackOption)); - LOGF("\t rack_id = 0x%" PRIx32 "\n" - "\t name = %.64s\n" - "\t flags = %u\n" - "\t max_grain_samples = %u\n" - "\t max_voices = %u\n" - "\t max_input_delay_blocks = %u\n" - "\t max_matrices = %u\n" - "\t max_ports = %u\n" - "\t max_voice_channels = %u\n" - "\t max_output_channels = %u\n" - "\t alloc_handler = 0x%016" PRIx64 "\n" + LOGF("\t alloc_handler = 0x%016" PRIx64 "\n" "\t free_handler = 0x%016" PRIx64 "\n" "\t user_data = 0x%016" PRIx64 "\n", - rack_id, option->name, option->flags, option->max_grain_samples, option->max_voices, - option->max_input_delay_blocks, option->max_matrices, option->max_ports, - option->max_voice_channels, option->max_output_channels, reinterpret_cast(allocator->alloc_handler), reinterpret_cast(allocator->free_handler), static_cast(allocator->user_data)); @@ -2711,16 +2730,44 @@ static void Ngs2ApplyEvent(Ngs2VoiceInternal& voice) { voice.event = Ngs2VoicePlayEvent::None; } -static void Ngs2ConsumePcm(Ngs2VoiceInternal& voice, uint32_t grain) { +static void Ngs2ConsumeSamples(Ngs2VoiceInternal& voice, uint32_t grain) { uint32_t output = 0; while (output < grain && !voice.blocks.empty()) { - auto& block = voice.blocks.front(); - const auto count = std::min(grain - output, block.info.num_samples - block.cursor); - const auto* pcm = reinterpret_cast(block.data) + - (block.info.num_skip_samples + block.cursor) * voice.channels; + auto& block = voice.blocks.front(); + auto count = std::min(grain - output, block.info.num_samples - block.cursor); + const int16_t* pcm = nullptr; + const float* decoded = nullptr; + if (voice.decoder != nullptr) { + const auto skip = + std::min(block.skip_remaining, + static_cast(voice.decoded_frame.size() / voice.channels)); + Ajm::AjmGaplessState gapless; + gapless.Set({block.info.num_samples - block.cursor, static_cast(skip), 0}, + true); + const auto result = voice.decoder->Decode( + block.data + block.data_cursor, block.info.data_size - block.data_cursor, + voice.decoded_frame.data(), voice.decoded_frame.size() * sizeof(float), false, + &gapless); + EXIT_NOT_IMPLEMENTED(result.result != OK || result.input_consumed == 0); + block.data_cursor += result.input_consumed; + block.skip_remaining -= skip - gapless.current.skip_samples; + const auto decoded_samples = + static_cast(result.output_written / (sizeof(float) * voice.channels)); + if (decoded_samples == 0) { + continue; + } + EXIT_NOT_IMPLEMENTED(decoded_samples > count); + count = decoded_samples; + decoded = voice.decoded_frame.data(); + } else { + pcm = reinterpret_cast(block.data) + + (block.info.num_skip_samples + block.cursor) * voice.channels; + } for (uint32_t c = 0; c < voice.channels; ++c) { for (uint32_t i = 0; i < count; ++i) { - voice.samples[c * grain + output + i] = pcm[i * voice.channels + c] / 32768.0f; + const auto index = i * voice.channels + c; + voice.samples[c * grain + output + i] = + decoded != nullptr ? decoded[index] : pcm[index] / 32768.0f; } } output += count; @@ -2744,6 +2791,9 @@ static void Ngs2ConsumePcm(Ngs2VoiceInternal& voice, uint32_t grain) { 0}; static_assert(sizeof(CallbackInfo) == 56); voice.blocks.pop_front(); + if (voice.blocks.empty() && !voice.accepts_blocks) { + voice.state = Ngs2VoicePlayState::Empty; + } if (voice.callback != 0 && (voice.callback_flags & 1u) != 0) { reinterpret_cast(voice.callback)( &info); @@ -2764,11 +2814,11 @@ static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vectortype == Ngs2RackType::CustomSampler) { - Ngs2ConsumePcm(voice, grain); + Ngs2ConsumeSamples(voice, grain); } for (auto* source: voices) { for (const auto& port: source->ports) { - if (port.dest != &voice) { + if (port.dest != &voice || port.volume == 0.0f) { continue; } Ngs2RenderVoice(*source, voices, grain); @@ -3327,11 +3377,9 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::CustomSampler); switch (param->id & 0xffffu) { case 0: { - const auto& format = *reinterpret_cast(param + 1); - EXIT_NOT_IMPLEMENTED(format.waveform_type != 0x12 || - format.sample_rate != voice->rack->ngs->option.sample_rate); - voice->channels = format.num_channels; - voice->blocks.clear(); + const auto& format = + *reinterpret_cast(param + 1); + voice->SetupSampler(format); break; } case 1: { @@ -3342,11 +3390,14 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH const Ngs2WaveformBlock* blocks; }; const auto& blocks = *reinterpret_cast(param); - EXIT_NOT_IMPLEMENTED(blocks.flags != 0x11); + EXIT_NOT_IMPLEMENTED(!voice->accepts_blocks || + (blocks.flags != 0 && blocks.flags != 0x11)); + voice->accepts_blocks = (blocks.flags & 1u) != 0; for (uint32_t i = 0; i < blocks.count; ++i) { const auto& block = blocks.blocks[i]; + EXIT_NOT_IMPLEMENTED(block.num_repeats != 0 || block.num_samples == 0); EXIT_NOT_IMPLEMENTED( - block.num_repeats != 0 || block.num_samples == 0 || + voice->decoder == nullptr && (uint64_t(block.num_skip_samples) + block.num_samples) * voice->channels * 2 > block.data_size);