CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HW/SasAudio.h
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.16171819// This is not really hardware, it's a software audio mixer running on the Media Engine.20// From the perspective of a PSP app though, it might as well be.2122#pragma once2324#include "Common/CommonTypes.h"25#include "Core/HW/BufferQueue.h"26#include "Core/HW/SasReverb.h"2728class PointerWrap;2930// General constants.31enum {32PSP_SAS_VOICES_MAX = 32,33PSP_SAS_VOL_MAX = 0x1000,34PSP_SAS_MAX_GRAIN = 2048, // Matches the max value of the parameter to sceSasInit35PSP_SAS_PITCH_MIN = 0x0000,36PSP_SAS_PITCH_BASE = 0x1000,37PSP_SAS_PITCH_MASK = 0xFFF,38PSP_SAS_PITCH_BASE_SHIFT = 12,39PSP_SAS_PITCH_MAX = 0x4000,40PSP_SAS_ENVELOPE_HEIGHT_MAX = 0x40000000,41PSP_SAS_ENVELOPE_FREQ_MAX = 0x7FFFFFFF,42};4344// The type of these are baked into savestates.45enum SasADSRCurveMode : int {46PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE = 0,47PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE = 1,48PSP_SAS_ADSR_CURVE_MODE_LINEAR_BENT = 2,49PSP_SAS_ADSR_CURVE_MODE_EXPONENT_DECREASE = 3,50PSP_SAS_ADSR_CURVE_MODE_EXPONENT_INCREASE = 4,51PSP_SAS_ADSR_CURVE_MODE_DIRECT = 5,52};5354enum {55PSP_SAS_ADSR_ATTACK = 1,56PSP_SAS_ADSR_DECAY = 2,57PSP_SAS_ADSR_SUSTAIN = 4,58PSP_SAS_ADSR_RELEASE = 8,59};6061enum SasEffectType {62PSP_SAS_EFFECT_TYPE_OFF = -1,63PSP_SAS_EFFECT_TYPE_ROOM = 0,64PSP_SAS_EFFECT_TYPE_STUDIO_SMALL = 1,65PSP_SAS_EFFECT_TYPE_STUDIO_MEDIUM = 2,66PSP_SAS_EFFECT_TYPE_STUDIO_LARGE = 3,67PSP_SAS_EFFECT_TYPE_HALL = 4,68PSP_SAS_EFFECT_TYPE_SPACE = 5,69PSP_SAS_EFFECT_TYPE_ECHO = 6,70PSP_SAS_EFFECT_TYPE_DELAY = 7,71PSP_SAS_EFFECT_TYPE_PIPE = 8,72PSP_SAS_EFFECT_TYPE_MAX = 8,73};7475enum SasOutputMode {76PSP_SAS_OUTPUTMODE_MIXED = 0,77PSP_SAS_OUTPUTMODE_RAW = 1,78};7980struct WaveformEffect {81int type;82int delay;83int feedback;84int leftVol;85int rightVol;86int isDryOn;87int isWetOn;88};8990enum VoiceType {91VOICETYPE_OFF,92VOICETYPE_VAG, // default93VOICETYPE_NOISE,94VOICETYPE_TRIWAVE, // are these used? there are functions for them (sceSetTriangularWave)95VOICETYPE_PULSEWAVE,96VOICETYPE_PCM,97VOICETYPE_ATRAC3,98};99100// VAG is a Sony ADPCM audio compression format, which goes all the way back to the PSX.101// It compresses 28 16-bit samples into a block of 16 bytes.102class VagDecoder {103public:104VagDecoder() : data_(0), read_(0), end_(true) {105memset(samples, 0, sizeof(samples));106}107void Start(u32 dataPtr, u32 vagSize, bool loopEnabled);108109void GetSamples(s16 *outSamples, int numSamples);110111void DecodeBlock(const u8 *&readp);112bool End() const { return end_; }113114void DoState(PointerWrap &p);115116u32 GetReadPtr() const { return read_; }117118private:119s16 samples[28];120int curSample = 0;121122u32 data_ = 0;123u32 read_ = 0;124int curBlock_ = -1;125int loopStartBlock_ = -1;126int numBlocks_ = 0;127128// rolling state. start at 0, should probably reset to 0 on loops?129int s_1 = 0;130int s_2 = 0;131132bool loopEnabled_ = false;133bool loopAtNextBlock_ = false;134bool end_ = false;135};136137class SasAtrac3 {138public:139SasAtrac3() : contextAddr_(0), atracID_(-1), sampleQueue_(0), end_(false) {}140~SasAtrac3() { delete sampleQueue_; }141int setContext(u32 context);142void getNextSamples(s16 *outbuf, int wantedSamples);143int addStreamData(u32 bufPtr, u32 addbytes);144void DoState(PointerWrap &p);145bool End() const {146return end_;147}148149private:150u32 contextAddr_;151int atracID_;152BufferQueue *sampleQueue_;153bool end_;154};155156class ADSREnvelope {157public:158void SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2);159void SetEnvelope(int flag, int a, int d, int s, int r);160void SetRate(int flag, int a, int d, int s, int r);161void SetSustainLevel(int sl) {162sustainLevel = sl;163}164165void WalkCurve(int type, int rate);166167void KeyOn();168void KeyOff();169void End();170171inline void Step();172173int GetHeight() const {174return (int)(height_ > (s64)PSP_SAS_ENVELOPE_HEIGHT_MAX ? PSP_SAS_ENVELOPE_HEIGHT_MAX : height_);175}176bool NeedsKeyOn() const {177return state_ == STATE_KEYON;178}179bool HasEnded() const {180return state_ == STATE_OFF;181}182183void DoState(PointerWrap &p);184185int attackRate = 0;186int decayRate = 0;187int sustainRate = 0;188int sustainLevel = 0;189int releaseRate = 0;190191SasADSRCurveMode attackType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE;192SasADSRCurveMode decayType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;193SasADSRCurveMode sustainType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;194SasADSRCurveMode releaseType = PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE;195196private:197// Actual PSP values.198enum ADSRState {199// Okay, this one isn't a real value but it might be.200STATE_KEYON_STEP = -42,201202STATE_KEYON = -2,203STATE_OFF = -1,204STATE_ATTACK = 0,205STATE_DECAY = 1,206STATE_SUSTAIN = 2,207STATE_RELEASE = 3,208};209void SetState(ADSRState state);210211ADSRState state_ = STATE_OFF;212s64 height_ = 0; // s64 to avoid having to care about overflow when calculating. TODO: this should be fine as s32213};214215// A SAS voice.216// TODO: Look into pre-decoding the VAG samples on SetVoice instead of decoding them on the fly.217// It's not very likely that games encode VAG dynamically.218struct SasVoice {219SasVoice()220: playing(false),221paused(false),222on(false),223type(VOICETYPE_OFF),224vagAddr(0),225vagSize(0),226pcmAddr(0),227pcmSize(0),228pcmIndex(0),229pcmLoopPos(0),230sampleRate(44100),231sampleFrac(0),232pitch(PSP_SAS_PITCH_BASE),233loop(false),234noiseFreq(0),235volumeLeft(PSP_SAS_VOL_MAX),236volumeRight(PSP_SAS_VOL_MAX),237effectLeft(PSP_SAS_VOL_MAX),238effectRight(PSP_SAS_VOL_MAX) {239memset(resampleHist, 0, sizeof(resampleHist));240}241242void Reset();243void KeyOn();244void KeyOff();245246void DoState(PointerWrap &p);247248void ReadSamples(s16 *output, int numSamples);249bool HaveSamplesEnded() const;250251// For debugging.252u32 GetReadAddress() const {253if (type == VOICETYPE_VAG) {254return vag.GetReadPtr();255} else {256return 0; // TODO.257}258}259260bool playing;261bool paused; // a voice can be playing AND paused. In that case, it won't play.262bool on; // key-on, key-off.263264VoiceType type;265266u32 vagAddr;267u32 vagSize;268u32 pcmAddr;269int pcmSize;270int pcmIndex;271int pcmLoopPos;272int sampleRate;273274uint32_t sampleFrac;275int pitch;276bool loop;277278int noiseFreq;279280int volumeLeft;281int volumeRight;282283// volume to "Send" (audio-lingo) to the effects processing engine, like reverb284int effectLeft;285int effectRight;286s16 resampleHist[2];287288ADSREnvelope envelope;289290// TODO: Union these two?291VagDecoder vag;292SasAtrac3 atrac3;293};294295class SasInstance {296public:297SasInstance();298~SasInstance();299300void ClearGrainSize();301void SetGrainSize(int newGrainSize);302int GetGrainSize() const { return grainSize; }303int EstimateMixUs();304305int maxVoices = PSP_SAS_VOICES_MAX;306int sampleRate = 44100;307int outputMode = PSP_SAS_OUTPUTMODE_MIXED;308309int *mixBuffer = nullptr;310int *sendBuffer = nullptr;311s16 *sendBufferDownsampled = nullptr;312s16 *sendBufferProcessed = nullptr;313314FILE *audioDump = nullptr;315316void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0);317void MixVoice(SasVoice &voice);318319// Applies reverb to send buffer, according to waveformEffect.320void ApplyWaveformEffect();321void SetWaveformEffectType(int type);322void WriteMixedOutput(s16 *outp, const s16 *inp, int leftVol, int rightVol);323324void GetDebugText(char *text, size_t bufsize);325326void DoState(PointerWrap &p);327328SasVoice voices[PSP_SAS_VOICES_MAX];329WaveformEffect waveformEffect;330331private:332SasReverb reverb_;333int grainSize = 0;334int16_t mixTemp_[PSP_SAS_MAX_GRAIN * 4 + 2 + 16]; // some extra margin for very high pitches.335};336337const char *ADSRCurveModeAsString(SasADSRCurveMode mode);338339340