mirror of
https://github.com/zeldaret/mm.git
synced 2026-06-27 10:02:50 -04:00
Introduce More Audio Headers (#1515)
* 4 audio headers * local first * PR Review, more cleanup * more cleanup * fix bss * PR Review * PR Suggestions
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "audio/effects.h"
|
||||
#include "audio/heap.h"
|
||||
|
||||
void* AudioHeap_SearchRegularCaches(s32 tableType, s32 cache, s32 id);
|
||||
void AudioHeap_InitSampleCaches(size_t persistentSampleCacheSize, size_t temporarySampleCacheSize);
|
||||
@@ -86,8 +87,8 @@ void AudioHeap_DiscardFont(s32 fontId) {
|
||||
}
|
||||
|
||||
AudioPlayback_NoteDisable(note);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(&gAudioCtx.noteFreeLists.disabled, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(&gAudioCtx.noteFreeLists.disabled, ¬e->listItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1034,7 +1035,7 @@ void AudioHeap_Init(void) {
|
||||
// Initialize notes
|
||||
gAudioCtx.notes = AudioHeap_AllocZeroed(&gAudioCtx.miscPool, gAudioCtx.numNotes * sizeof(Note));
|
||||
AudioPlayback_NoteInitAll();
|
||||
AudioPlayback_InitNoteFreeList();
|
||||
AudioList_InitNoteFreeList();
|
||||
gAudioCtx.sampleStateList =
|
||||
AudioHeap_AllocZeroed(&gAudioCtx.miscPool, gAudioCtx.audioBufferParameters.updatesPerFrame *
|
||||
gAudioCtx.numNotes * sizeof(NoteSampleState));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "audio/load.h"
|
||||
#include "buffers.h"
|
||||
|
||||
/**
|
||||
|
||||
+58
-43
@@ -1,10 +1,25 @@
|
||||
#include "global.h"
|
||||
#include "audio/effects.h"
|
||||
#include "audio/playback.h"
|
||||
|
||||
void AudioPlayback_NoteSetResamplingRate(NoteSampleState* sampleState, f32 resamplingRateInput);
|
||||
void AudioPlayback_AudioListPushFront(AudioListItem* list, AudioListItem* item);
|
||||
void AudioList_PushFront(AudioListItem* list, AudioListItem* item);
|
||||
void AudioPlayback_NoteInitForLayer(Note* note, SequenceLayer* layer);
|
||||
|
||||
typedef struct {
|
||||
/* 0x00 */ u8 targetReverbVol;
|
||||
/* 0x01 */ u8 gain; // Increases volume by a multiplicative scaling factor. Represented as a UQ4.4 number
|
||||
/* 0x02 */ u8 pan;
|
||||
/* 0x03 */ u8 surroundEffectIndex;
|
||||
/* 0x04 */ StereoData stereoData;
|
||||
/* 0x08 */ f32 frequency;
|
||||
/* 0x0C */ f32 velocity;
|
||||
/* 0x10 */ UNK_TYPE1 unk_10[0x4];
|
||||
/* 0x14 */ s16* filter;
|
||||
/* 0x18 */ u8 combFilterSize;
|
||||
/* 0x1A */ u16 combFilterGain;
|
||||
} NoteSubAttributes; // size = 0x1A
|
||||
|
||||
void AudioPlayback_InitSampleState(Note* note, NoteSampleState* sampleState, NoteSubAttributes* subAttrs) {
|
||||
f32 volLeft;
|
||||
f32 volRight;
|
||||
@@ -205,8 +220,8 @@ void AudioPlayback_ProcessNotes(void) {
|
||||
}
|
||||
|
||||
AudioPlayback_SeqLayerNoteRelease(playbackState->parentLayer);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioPlayback_AudioListPushFront(¬e->listItem.pool->decaying, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushFront(¬e->listItem.pool->decaying, ¬e->listItem);
|
||||
playbackState->priority = 1;
|
||||
playbackState->status = PLAYBACK_STATUS_2;
|
||||
} else if ((playbackState->status == PLAYBACK_STATUS_0) && (playbackState->priority >= 1)) {
|
||||
@@ -227,14 +242,14 @@ void AudioPlayback_ProcessNotes(void) {
|
||||
AudioPlayback_NoteInitForLayer(note, playbackState->wantedParentLayer);
|
||||
AudioEffects_InitVibrato(note);
|
||||
AudioEffects_InitPortamento(note);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(¬e->listItem.pool->active, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(¬e->listItem.pool->active, ¬e->listItem);
|
||||
playbackState->wantedParentLayer = NO_LAYER;
|
||||
// don't skip
|
||||
} else {
|
||||
AudioPlayback_NoteDisable(note);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(¬e->listItem.pool->disabled, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(¬e->listItem.pool->disabled, ¬e->listItem);
|
||||
playbackState->wantedParentLayer = NO_LAYER;
|
||||
goto skip;
|
||||
}
|
||||
@@ -243,8 +258,8 @@ void AudioPlayback_ProcessNotes(void) {
|
||||
playbackState->parentLayer->bit1 = true;
|
||||
}
|
||||
AudioPlayback_NoteDisable(note);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(¬e->listItem.pool->disabled, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(¬e->listItem.pool->disabled, ¬e->listItem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -253,8 +268,8 @@ void AudioPlayback_ProcessNotes(void) {
|
||||
playbackState->parentLayer->bit1 = true;
|
||||
}
|
||||
AudioPlayback_NoteDisable(note);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(¬e->listItem.pool->disabled, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(¬e->listItem.pool->disabled, ¬e->listItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -570,8 +585,8 @@ void AudioPlayback_SeqLayerDecayRelease(SequenceLayer* layer, s32 target) {
|
||||
}
|
||||
|
||||
if (target == ADSR_STATUS_DECAY) {
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioPlayback_AudioListPushFront(¬e->listItem.pool->decaying, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushFront(¬e->listItem.pool->decaying, ¬e->listItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,35 +664,35 @@ void AudioPlayback_InitSyntheticWave(Note* note, SequenceLayer* layer) {
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayback_InitNoteList(AudioListItem* list) {
|
||||
void AudioList_InitNoteList(AudioListItem* list) {
|
||||
list->prev = list;
|
||||
list->next = list;
|
||||
list->u.count = 0;
|
||||
}
|
||||
|
||||
void AudioPlayback_InitNoteLists(NotePool* pool) {
|
||||
AudioPlayback_InitNoteList(&pool->disabled);
|
||||
AudioPlayback_InitNoteList(&pool->decaying);
|
||||
AudioPlayback_InitNoteList(&pool->releasing);
|
||||
AudioPlayback_InitNoteList(&pool->active);
|
||||
void AudioList_InitNoteLists(NotePool* pool) {
|
||||
AudioList_InitNoteList(&pool->disabled);
|
||||
AudioList_InitNoteList(&pool->decaying);
|
||||
AudioList_InitNoteList(&pool->releasing);
|
||||
AudioList_InitNoteList(&pool->active);
|
||||
pool->disabled.pool = pool;
|
||||
pool->decaying.pool = pool;
|
||||
pool->releasing.pool = pool;
|
||||
pool->active.pool = pool;
|
||||
}
|
||||
|
||||
void AudioPlayback_InitNoteFreeList(void) {
|
||||
void AudioList_InitNoteFreeList(void) {
|
||||
s32 i;
|
||||
|
||||
AudioPlayback_InitNoteLists(&gAudioCtx.noteFreeLists);
|
||||
AudioList_InitNoteLists(&gAudioCtx.noteFreeLists);
|
||||
for (i = 0; i < gAudioCtx.numNotes; i++) {
|
||||
gAudioCtx.notes[i].listItem.u.value = &gAudioCtx.notes[i];
|
||||
gAudioCtx.notes[i].listItem.prev = NULL;
|
||||
AudioScript_AudioListPushBack(&gAudioCtx.noteFreeLists.disabled, &gAudioCtx.notes[i].listItem);
|
||||
AudioList_PushBack(&gAudioCtx.noteFreeLists.disabled, &gAudioCtx.notes[i].listItem);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayback_NotePoolClear(NotePool* pool) {
|
||||
void AudioList_ClearNotePool(NotePool* pool) {
|
||||
s32 i;
|
||||
AudioListItem* source;
|
||||
AudioListItem* cur;
|
||||
@@ -711,20 +726,20 @@ void AudioPlayback_NotePoolClear(NotePool* pool) {
|
||||
if (cur == source || cur == NULL) {
|
||||
break;
|
||||
}
|
||||
AudioPlayback_AudioListRemove(cur);
|
||||
AudioScript_AudioListPushBack(dest, cur);
|
||||
AudioList_Remove(cur);
|
||||
AudioList_PushBack(dest, cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayback_NotePoolFill(NotePool* pool, s32 count) {
|
||||
void AudioList_FillNotePool(NotePool* pool, s32 count) {
|
||||
s32 i;
|
||||
s32 j;
|
||||
Note* note;
|
||||
AudioListItem* source;
|
||||
AudioListItem* dest;
|
||||
|
||||
AudioPlayback_NotePoolClear(pool);
|
||||
AudioList_ClearNotePool(pool);
|
||||
|
||||
for (i = 0, j = 0; j < count; i++) {
|
||||
if (i == 4) {
|
||||
@@ -754,17 +769,17 @@ void AudioPlayback_NotePoolFill(NotePool* pool, s32 count) {
|
||||
}
|
||||
|
||||
while (j < count) {
|
||||
note = AudioScript_AudioListPopBack(source);
|
||||
note = AudioList_PopBack(source);
|
||||
if (note == NULL) {
|
||||
break;
|
||||
}
|
||||
AudioScript_AudioListPushBack(dest, ¬e->listItem);
|
||||
AudioList_PushBack(dest, ¬e->listItem);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayback_AudioListPushFront(AudioListItem* list, AudioListItem* item) {
|
||||
void AudioList_PushFront(AudioListItem* list, AudioListItem* item) {
|
||||
// add 'item' to the front of the list given by 'list', if it's not in any list
|
||||
if (item->prev == NULL) {
|
||||
item->prev = list;
|
||||
@@ -776,7 +791,7 @@ void AudioPlayback_AudioListPushFront(AudioListItem* list, AudioListItem* item)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayback_AudioListRemove(AudioListItem* item) {
|
||||
void AudioList_Remove(AudioListItem* item) {
|
||||
// remove 'item' from the list it's in, if any
|
||||
if (item->prev != NULL) {
|
||||
item->prev->next = item->next;
|
||||
@@ -785,7 +800,7 @@ void AudioPlayback_AudioListRemove(AudioListItem* item) {
|
||||
}
|
||||
}
|
||||
|
||||
Note* AudioPlayback_FindNodeWithPrioLessThan(AudioListItem* list, s32 limit) {
|
||||
Note* AudioList_FindNodeWithPrioLessThan(AudioListItem* list, s32 limit) {
|
||||
AudioListItem* cur = list->next;
|
||||
AudioListItem* best;
|
||||
|
||||
@@ -871,22 +886,22 @@ void AudioPlayback_NoteReleaseAndTakeOwnership(Note* note, SequenceLayer* layer)
|
||||
}
|
||||
|
||||
Note* AudioPlayback_AllocNoteFromDisabled(NotePool* pool, SequenceLayer* layer) {
|
||||
Note* note = AudioScript_AudioListPopBack(&pool->disabled);
|
||||
Note* note = AudioList_PopBack(&pool->disabled);
|
||||
|
||||
if (note != NULL) {
|
||||
AudioPlayback_NoteInitForLayer(note, layer);
|
||||
AudioPlayback_AudioListPushFront(&pool->active, ¬e->listItem);
|
||||
AudioList_PushFront(&pool->active, ¬e->listItem);
|
||||
}
|
||||
return note;
|
||||
}
|
||||
|
||||
Note* AudioPlayback_AllocNoteFromDecaying(NotePool* pool, SequenceLayer* layer) {
|
||||
Note* note = AudioPlayback_FindNodeWithPrioLessThan(&pool->decaying, layer->channel->notePriority);
|
||||
Note* note = AudioList_FindNodeWithPrioLessThan(&pool->decaying, layer->channel->notePriority);
|
||||
|
||||
if (note != NULL) {
|
||||
AudioPlayback_NoteReleaseAndTakeOwnership(note, layer);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(&pool->releasing, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(&pool->releasing, ¬e->listItem);
|
||||
}
|
||||
return note;
|
||||
}
|
||||
@@ -898,13 +913,13 @@ Note* AudioPlayback_AllocNoteFromActive(NotePool* pool, SequenceLayer* layer) {
|
||||
s32 aPriority;
|
||||
|
||||
rPriority = aPriority = 0x10;
|
||||
rNote = AudioPlayback_FindNodeWithPrioLessThan(&pool->releasing, layer->channel->notePriority);
|
||||
rNote = AudioList_FindNodeWithPrioLessThan(&pool->releasing, layer->channel->notePriority);
|
||||
|
||||
if (rNote != NULL) {
|
||||
rPriority = rNote->playbackState.priority;
|
||||
}
|
||||
|
||||
aNote = AudioPlayback_FindNodeWithPrioLessThan(&pool->active, layer->channel->notePriority);
|
||||
aNote = AudioList_FindNodeWithPrioLessThan(&pool->active, layer->channel->notePriority);
|
||||
|
||||
if (aNote != NULL) {
|
||||
aPriority = aNote->playbackState.priority;
|
||||
@@ -915,9 +930,9 @@ Note* AudioPlayback_AllocNoteFromActive(NotePool* pool, SequenceLayer* layer) {
|
||||
}
|
||||
|
||||
if (aPriority < rPriority) {
|
||||
AudioPlayback_AudioListRemove(&aNote->listItem);
|
||||
AudioList_Remove(&aNote->listItem);
|
||||
func_801963E8(aNote, layer);
|
||||
AudioScript_AudioListPushBack(&pool->releasing, &aNote->listItem);
|
||||
AudioList_PushBack(&pool->releasing, &aNote->listItem);
|
||||
aNote->playbackState.priority = layer->channel->notePriority;
|
||||
return aNote;
|
||||
}
|
||||
@@ -935,8 +950,8 @@ Note* AudioPlayback_AllocNote(SequenceLayer* layer) {
|
||||
if ((note != NULL) && (note->playbackState.prevParentLayer == layer) &&
|
||||
(note->playbackState.wantedParentLayer == NO_LAYER)) {
|
||||
AudioPlayback_NoteReleaseAndTakeOwnership(note, layer);
|
||||
AudioPlayback_AudioListRemove(¬e->listItem);
|
||||
AudioScript_AudioListPushBack(¬e->listItem.pool->releasing, ¬e->listItem);
|
||||
AudioList_Remove(¬e->listItem);
|
||||
AudioList_PushBack(¬e->listItem.pool->releasing, ¬e->listItem);
|
||||
return note;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-15
@@ -14,6 +14,7 @@
|
||||
* Otherwise, each set of instructions has its own command interpreter
|
||||
*/
|
||||
#include "global.h"
|
||||
#include "audio/seqplayer.h"
|
||||
|
||||
#define PROCESS_SCRIPT_END -1
|
||||
|
||||
@@ -307,7 +308,7 @@ void AudioScript_InitSequenceChannel(SequenceChannel* channel) {
|
||||
}
|
||||
|
||||
channel->unused = false;
|
||||
AudioPlayback_InitNoteLists(&channel->notePool);
|
||||
AudioList_InitNoteLists(&channel->notePool);
|
||||
channel->startSamplePos = 0;
|
||||
channel->unk_E0 = 0;
|
||||
channel->sfxState = NULL;
|
||||
@@ -318,7 +319,7 @@ s32 AudioScript_SeqChannelSetLayer(SequenceChannel* channel, s32 layerIndex) {
|
||||
s32 pad;
|
||||
|
||||
if (channel->layers[layerIndex] == NULL) {
|
||||
layer = AudioScript_AudioListPopBack(&gAudioCtx.layerFreeList);
|
||||
layer = AudioList_PopBack(&gAudioCtx.layerFreeList);
|
||||
channel->layers[layerIndex] = layer;
|
||||
if (layer == NULL) {
|
||||
channel->layers[layerIndex] = NULL;
|
||||
@@ -386,7 +387,7 @@ void AudioScript_SeqLayerFree(SequenceChannel* channel, s32 layerIndex) {
|
||||
SequenceLayer* layer = channel->layers[layerIndex];
|
||||
|
||||
if (layer != NULL) {
|
||||
AudioScript_AudioListPushBack(&gAudioCtx.layerFreeList, &layer->listItem);
|
||||
AudioList_PushBack(&gAudioCtx.layerFreeList, &layer->listItem);
|
||||
AudioScript_SeqLayerDisable(layer);
|
||||
channel->layers[layerIndex] = NULL;
|
||||
}
|
||||
@@ -401,7 +402,7 @@ void AudioScript_SequenceChannelDisable(SequenceChannel* channel) {
|
||||
AudioScript_SeqLayerFree(channel, i);
|
||||
}
|
||||
|
||||
AudioPlayback_NotePoolClear(&channel->notePool);
|
||||
AudioList_ClearNotePool(&channel->notePool);
|
||||
channel->enabled = false;
|
||||
}
|
||||
|
||||
@@ -456,7 +457,7 @@ void AudioScript_SequencePlayerDisableAsFinished(SequencePlayer* seqPlayer) {
|
||||
|
||||
void AudioScript_SequencePlayerDisable(SequencePlayer* seqPlayer) {
|
||||
AudioScript_SequencePlayerDisableChannels(seqPlayer, 0xFFFF);
|
||||
AudioPlayback_NotePoolClear(&seqPlayer->notePool);
|
||||
AudioList_ClearNotePool(&seqPlayer->notePool);
|
||||
if (!seqPlayer->enabled) {
|
||||
return;
|
||||
}
|
||||
@@ -479,7 +480,7 @@ void AudioScript_SequencePlayerDisable(SequencePlayer* seqPlayer) {
|
||||
}
|
||||
}
|
||||
|
||||
void AudioScript_AudioListPushBack(AudioListItem* list, AudioListItem* item) {
|
||||
void AudioList_PushBack(AudioListItem* list, AudioListItem* item) {
|
||||
if (item->prev == NULL) {
|
||||
list->prev->next = item;
|
||||
item->prev = list->prev;
|
||||
@@ -490,7 +491,7 @@ void AudioScript_AudioListPushBack(AudioListItem* list, AudioListItem* item) {
|
||||
}
|
||||
}
|
||||
|
||||
void* AudioScript_AudioListPopBack(AudioListItem* list) {
|
||||
void* AudioList_PopBack(AudioListItem* list) {
|
||||
AudioListItem* item = list->prev;
|
||||
|
||||
if (item == list) {
|
||||
@@ -516,7 +517,7 @@ void AudioScript_InitLayerFreelist(void) {
|
||||
for (i = 0; i < ARRAY_COUNT(gAudioCtx.sequenceLayers); i++) {
|
||||
gAudioCtx.sequenceLayers[i].listItem.u.value = &gAudioCtx.sequenceLayers[i];
|
||||
gAudioCtx.sequenceLayers[i].listItem.prev = NULL;
|
||||
AudioScript_AudioListPushBack(&gAudioCtx.layerFreeList, &gAudioCtx.sequenceLayers[i].listItem);
|
||||
AudioList_PushBack(&gAudioCtx.layerFreeList, &gAudioCtx.sequenceLayers[i].listItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1259,13 +1260,13 @@ void AudioScript_SequenceChannelProcessScript(SequenceChannel* channel) {
|
||||
goto exit_loop;
|
||||
|
||||
case 0xF1: // channel: reserve notes
|
||||
AudioPlayback_NotePoolClear(&channel->notePool);
|
||||
AudioList_ClearNotePool(&channel->notePool);
|
||||
cmd = (u8)cmdArgs[0];
|
||||
AudioPlayback_NotePoolFill(&channel->notePool, cmd);
|
||||
AudioList_FillNotePool(&channel->notePool, cmd);
|
||||
break;
|
||||
|
||||
case 0xF0: // channel: unreserve notes
|
||||
AudioPlayback_NotePoolClear(&channel->notePool);
|
||||
AudioList_ClearNotePool(&channel->notePool);
|
||||
break;
|
||||
|
||||
case 0xC2: // channel: set dyntable
|
||||
@@ -1913,13 +1914,13 @@ void AudioScript_SequencePlayerProcessSequence(SequencePlayer* seqPlayer) {
|
||||
if (cmd >= 0xC0) {
|
||||
switch (cmd) {
|
||||
case 0xF1: // seqPlayer: reserve notes
|
||||
AudioPlayback_NotePoolClear(&seqPlayer->notePool);
|
||||
AudioList_ClearNotePool(&seqPlayer->notePool);
|
||||
cmd = AudioScript_ScriptReadU8(seqScript);
|
||||
AudioPlayback_NotePoolFill(&seqPlayer->notePool, cmd);
|
||||
AudioList_FillNotePool(&seqPlayer->notePool, cmd);
|
||||
break;
|
||||
|
||||
case 0xF0: // seqPlayer: unreserve notes
|
||||
AudioPlayback_NotePoolClear(&seqPlayer->notePool);
|
||||
AudioList_ClearNotePool(&seqPlayer->notePool);
|
||||
break;
|
||||
|
||||
case 0xDF: // seqPlayer: transpose
|
||||
@@ -2281,7 +2282,7 @@ void AudioScript_InitSequencePlayer(SequencePlayer* seqPlayer) {
|
||||
seqPlayer->fadeVolumeScale = 1.0f;
|
||||
seqPlayer->bend = 1.0f;
|
||||
|
||||
AudioPlayback_InitNoteLists(&seqPlayer->notePool);
|
||||
AudioList_InitNoteLists(&seqPlayer->notePool);
|
||||
AudioScript_ResetSequencePlayer(seqPlayer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "audio/synthesis.h"
|
||||
|
||||
// DMEM Addresses for the RSP
|
||||
#define DMEM_TEMP 0x3B0
|
||||
|
||||
Reference in New Issue
Block a user