diff --git a/custom/4coder_audio.cpp b/custom/4coder_audio.cpp index c1b406b5..0027cd88 100644 --- a/custom/4coder_audio.cpp +++ b/custom/4coder_audio.cpp @@ -12,22 +12,22 @@ function u32 AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend) { - // NOTE(casey): Returns the original value _prior_ to adding - u32 Result = _InterlockedExchangeAdd((long volatile*)Value, (long)Addend); - return(Result); + // NOTE(casey): Returns the original value _prior_ to adding + u32 Result = _InterlockedExchangeAdd((long volatile*)Value, (long)Addend); + return(Result); } function void def_audio_begin_ticket_mutex(Audio_System *Crunky) { - u32 Ticket = AtomicAddU32AndReturnOriginal(&Crunky->ticket, 1); - while(Ticket != Crunky->serving) {_mm_pause();} + u32 Ticket = AtomicAddU32AndReturnOriginal(&Crunky->ticket, 1); + while(Ticket != Crunky->serving) {_mm_pause();} } function void def_audio_end_ticket_mutex(Audio_System *Crunky) { - AtomicAddU32AndReturnOriginal(&Crunky->serving, 1); + AtomicAddU32AndReturnOriginal(&Crunky->serving, 1); } @@ -38,131 +38,131 @@ global Audio_System def_audio_system = {}; function void def_audio_init(void){ - block_zero_struct(&def_audio_system); - system_set_source_mixer(&def_audio_system, def_audio_mix_sources); - system_set_destination_mixer(def_audio_mix_destination); + block_zero_struct(&def_audio_system); + system_set_source_mixer(&def_audio_system, def_audio_mix_sources); + system_set_destination_mixer(def_audio_mix_destination); } function void def_audio_play_clip(Audio_Clip clip, Audio_Control *control){ - clip.control = control; - Audio_System *Crunky = &def_audio_system; - def_audio_begin_ticket_mutex(Crunky); - if (Crunky->pending_clip_count < ArrayCount(Crunky->pending_clips)) - { - Crunky->pending_clips[Crunky->pending_clip_count++] = clip; - } - def_audio_end_ticket_mutex(Crunky); + clip.control = control; + Audio_System *Crunky = &def_audio_system; + def_audio_begin_ticket_mutex(Crunky); + if (Crunky->pending_clip_count < ArrayCount(Crunky->pending_clips)) + { + Crunky->pending_clips[Crunky->pending_clip_count++] = clip; + } + def_audio_end_ticket_mutex(Crunky); } internal b32 def_audio_is_playing(Audio_Control *control){ - Audio_System *Crunky = &def_audio_system; - b32 result = (Crunky->generation - control->generation < 2); - return(result); + Audio_System *Crunky = &def_audio_system; + b32 result = (Crunky->generation - control->generation < 2); + return(result); } internal void def_audio_stop(Audio_Control *control){ - Audio_System *Crunky = &def_audio_system; - def_audio_begin_ticket_mutex(Crunky); - - Audio_Clip *clip = Crunky->playing_clips; - for(u32 i = 0; - i < ArrayCount(Crunky->playing_clips); - i += 1, clip += 1){ - if (clip->control == control){ - clip->at_sample_index = clip->sample_count; - clip->control = 0; - } - } - control->loop = false; - - def_audio_end_ticket_mutex(Crunky); + Audio_System *Crunky = &def_audio_system; + def_audio_begin_ticket_mutex(Crunky); + + Audio_Clip *clip = Crunky->playing_clips; + for(u32 i = 0; + i < ArrayCount(Crunky->playing_clips); + i += 1, clip += 1){ + if (clip->control == control){ + clip->at_sample_index = clip->sample_count; + clip->control = 0; + } + } + control->loop = false; + + def_audio_end_ticket_mutex(Crunky); } function void def_audio_mix_sources(void *ctx, f32 *mix_buffer, u32 sample_count){ - Audio_System *Crunky = (Audio_System*)ctx; - def_audio_begin_ticket_mutex(Crunky); - // NOTE(casey): Move pending sounds into the playing list - { - Crunky->generation += 1; - u32 PendIndex = 0; - Audio_Clip *clip = Crunky->playing_clips; - for(u32 DestIndex = 0; - (DestIndex < ArrayCount(Crunky->playing_clips)) && (PendIndex < Crunky->pending_clip_count); - DestIndex += 1, clip += 1) - { - if (clip->at_sample_index == clip->sample_count) - { - Audio_Control *control = clip->control; - if (control == 0 || !control->loop){ - *clip = Crunky->pending_clips[PendIndex++]; - } - } - } - Crunky->pending_clip_count = 0; + Audio_System *Crunky = (Audio_System*)ctx; + def_audio_begin_ticket_mutex(Crunky); + // NOTE(casey): Move pending sounds into the playing list + { + Crunky->generation += 1; + u32 PendIndex = 0; + Audio_Clip *clip = Crunky->playing_clips; + for(u32 DestIndex = 0; + (DestIndex < ArrayCount(Crunky->playing_clips)) && (PendIndex < Crunky->pending_clip_count); + DestIndex += 1, clip += 1) + { + if (clip->at_sample_index == clip->sample_count) + { + Audio_Control *control = clip->control; + if (control == 0 || !control->loop){ + *clip = Crunky->pending_clips[PendIndex++]; } - def_audio_end_ticket_mutex(Crunky); + } + } + Crunky->pending_clip_count = 0; + } + def_audio_end_ticket_mutex(Crunky); + + // NOTE(casey): Mix all sounds into the output buffer + { + Audio_Clip *clip = Crunky->playing_clips; + for(u32 SoundIndex = 0; + SoundIndex < ArrayCount(Crunky->playing_clips); + SoundIndex += 1, clip += 1) + { + // NOTE(allen): Determine starting point + Audio_Control *control = clip->control; + if (control != 0 && control->loop && clip->at_sample_index == clip->sample_count){ + clip->at_sample_index = 0; + } + u32 base_sample_index = clip->at_sample_index; + + // NOTE(casey): Determine how many samples are left to play in this + // sound (possible none) + u32 SamplesToMix = clamp_top((clip->sample_count - clip->at_sample_index), sample_count); + clip->at_sample_index += SamplesToMix; + + // NOTE(casey): Load the volume out of the control if there is one, + // and if there is, update the generation and sample index so + // external controllers can take action + f32 LeftVol = clip->channel_volume[0]; + f32 RightVol = clip->channel_volume[1]; + if(SamplesToMix && control != 0) + { + LeftVol *= control->channel_volume[0]; + RightVol *= control->channel_volume[1]; + control->generation = Crunky->generation; + control->last_played_sample_index = clip->at_sample_index; + } + + // NOTE(casey): Mix samples + for(u32 SampleIndex = 0; + SampleIndex < SamplesToMix; + ++SampleIndex) + { + u32 src_index = 2*(base_sample_index + SampleIndex); + f32 Left = LeftVol *(f32)clip->samples[src_index + 0]; + f32 Right = RightVol*(f32)clip->samples[src_index + 1]; - // NOTE(casey): Mix all sounds into the output buffer - { - Audio_Clip *clip = Crunky->playing_clips; - for(u32 SoundIndex = 0; - SoundIndex < ArrayCount(Crunky->playing_clips); - SoundIndex += 1, clip += 1) - { - // NOTE(allen): Determine starting point - Audio_Control *control = clip->control; - if (control != 0 && control->loop && clip->at_sample_index == clip->sample_count){ - clip->at_sample_index = 0; - } - u32 base_sample_index = clip->at_sample_index; - - // NOTE(casey): Determine how many samples are left to play in this - // sound (possible none) - u32 SamplesToMix = clamp_top((clip->sample_count - clip->at_sample_index), sample_count); - clip->at_sample_index += SamplesToMix; - - // NOTE(casey): Load the volume out of the control if there is one, - // and if there is, update the generation and sample index so - // external controllers can take action - f32 LeftVol = clip->channel_volume[0]; - f32 RightVol = clip->channel_volume[1]; - if(SamplesToMix && control != 0) - { - LeftVol *= control->channel_volume[0]; - RightVol *= control->channel_volume[1]; - control->generation = Crunky->generation; - control->last_played_sample_index = clip->at_sample_index; - } - - // NOTE(casey): Mix samples - for(u32 SampleIndex = 0; - SampleIndex < SamplesToMix; - ++SampleIndex) - { - u32 src_index = 2*(base_sample_index + SampleIndex); - f32 Left = LeftVol *(f32)clip->samples[src_index + 0]; - f32 Right = RightVol*(f32)clip->samples[src_index + 1]; - - u32 dst_index = 2*SampleIndex; - mix_buffer[dst_index + 0] += Left; - mix_buffer[dst_index + 1] += Right; - } - } - } + u32 dst_index = 2*SampleIndex; + mix_buffer[dst_index + 0] += Left; + mix_buffer[dst_index + 1] += Right; + } + } + } } function void def_audio_mix_destination(i16 *dst, f32 *src, u32 sample_count){ - u32 opl = sample_count*2; - for(u32 i = 0; i < opl; i += 1){ - f32 sample = src[i]; - f32 sat_sample = clamp(-32768.f, sample, 32767.f); - dst[i] = (i16)sat_sample; - } + u32 opl = sample_count*2; + for(u32 i = 0; i < opl; i += 1){ + f32 sample = src[i]; + f32 sat_sample = clamp(-32768.f, sample, 32767.f); + dst[i] = (i16)sat_sample; + } } @@ -174,97 +174,100 @@ def_audio_mix_destination(i16 *dst, f32 *src, u32 sample_count){ #pragma pack(push, 1) struct wave_fmt_data { - u16 wFormatTag; - u16 wChannels; - u32 dwSamplesPerSec; - u32 dwAvgBytesPerSec; - u16 wBlockAlign; - u16 wBitsPerSample; + u16 wFormatTag; + u16 wChannels; + u32 dwSamplesPerSec; + u32 dwAvgBytesPerSec; + u16 wBlockAlign; + u16 wBitsPerSample; }; struct riff_header { - u32 ID; - u32 DataSize; + u32 ID; + u32 DataSize; }; #pragma pack(pop) #endif function Audio_Clip audio_clip_from_wav_data(String_Const_u8 data){ - Audio_Clip Result = {}; - - if (data.size >= 4 && *(u32 *)data.str == *(u32 *)"RIFF"){ - // NOTE(casey): This ROM is in WAV format - - riff_header *RootHeader = (riff_header *)data.str; - - wave_fmt_data *Format = 0; - u32 SampleDataSize = 0; - i16 *Samples = 0; - - u32 At = sizeof(riff_header); - u32 LastAt = At + ((RootHeader->DataSize + 1) & ~1); - if ((*(u32 *)(data.str + At) == *(u32 *)"WAVE") && - (LastAt <= data.size)){ - At += sizeof(u32); - while (At < LastAt){ - riff_header *Header = (riff_header *)(data.str + At); - u32 DataAt = At + sizeof(riff_header); - u32 EndAt = DataAt + ((Header->DataSize + 1) & ~1); - if(EndAt <= data.size) - { - void *Data = (data.str + DataAt); - if(Header->ID == *(u32 *)"fmt ") - { - Format = (wave_fmt_data *)Data; - } - else if(Header->ID == *(u32 *)"data") - { - SampleDataSize = Header->DataSize; - Samples = (i16 *)Data; - } - } - - At = EndAt; - } - } - - if (Format && - Samples && - (Format->wFormatTag == 1) && - (Format->wChannels == 2) && - (Format->wBitsPerSample == 16) && - (Format->dwSamplesPerSec == 48000)){ - for (u32 i = 0; i < 2; i += 1){ - Result.channel_volume[i] = 1.f; - } - Result.sample_count = SampleDataSize / (Format->wChannels*Format->wBitsPerSample/8); - Result.samples = (i16 *)Samples; - } - else{ - // TODO(casey): This is where you would output an error - to 4coder somehow? - } + Audio_Clip Result = {}; + + if (data.size >= 4 && *(u32 *)data.str == *(u32 *)"RIFF"){ + // NOTE(casey): This ROM is in WAV format + + riff_header *RootHeader = (riff_header *)data.str; + + wave_fmt_data *Format = 0; + u32 SampleDataSize = 0; + i16 *Samples = 0; + + u32 At = sizeof(riff_header); + u32 LastAt = At + ((RootHeader->DataSize + 1) & ~1); + if ((*(u32 *)(data.str + At) == *(u32 *)"WAVE") && + (LastAt <= data.size)){ + At += sizeof(u32); + while (At < LastAt){ + riff_header *Header = (riff_header *)(data.str + At); + u32 DataAt = At + sizeof(riff_header); + u32 EndAt = DataAt + ((Header->DataSize + 1) & ~1); + if(EndAt <= data.size) + { + void *Data = (data.str + DataAt); + if(Header->ID == *(u32 *)"fmt ") + { + Format = (wave_fmt_data *)Data; + } + else if(Header->ID == *(u32 *)"data") + { + SampleDataSize = Header->DataSize; + Samples = (i16 *)Data; + } } - else{ - // TODO(casey): This is where you would output an error - to 4coder somehow? - } - return(Result); + At = EndAt; + } + } + + if (Format && + Samples && + (Format->wFormatTag == 1) && + (Format->wChannels == 2) && + (Format->wBitsPerSample == 16) && + (Format->dwSamplesPerSec == 48000)){ + for (u32 i = 0; i < 2; i += 1){ + Result.channel_volume[i] = 1.f; + } + Result.sample_count = SampleDataSize / (Format->wChannels*Format->wBitsPerSample/8); + Result.samples = (i16 *)Samples; + } + else{ + // TODO(casey): This is where you would output an error - to 4coder somehow? + } + } + else{ + // TODO(casey): This is where you would output an error - to 4coder somehow? + } + + return(Result); } function Audio_Clip audio_clip_from_wav_FILE(Arena *arena, FILE *file){ - String_Const_u8 data = data_from_file(arena, file); - Audio_Clip result = audio_clip_from_wav_data(data); - return(result); + String_Const_u8 data = data_from_file(arena, file); + Audio_Clip result = audio_clip_from_wav_data(data); + return(result); } function Audio_Clip audio_clip_from_wav_file_name(Arena *arena, char *file_name){ - String_Const_u8 data = {}; - FILE *file = fopen(file_name, "rb"); - Audio_Clip result = audio_clip_from_wav_FILE(arena, file); - fclose(file); - return(result); + Audio_Clip result = {}; + String_Const_u8 data = {}; + FILE *file = fopen(file_name, "rb"); + if (file != 0){ + result = audio_clip_from_wav_FILE(arena, file); + fclose(file); + } + return(result); } diff --git a/custom/4coder_examples.cpp b/custom/4coder_examples.cpp index 04bb7c34..20843ce0 100644 --- a/custom/4coder_examples.cpp +++ b/custom/4coder_examples.cpp @@ -10,18 +10,18 @@ customization writers. CUSTOM_COMMAND_SIG(double_backspace) CUSTOM_DOC("Example of history group helpers") { - /* History_Group is a wrapper around the history API that makes it easy to + /* History_Group is a wrapper around the history API that makes it easy to group any series of edits into a single undo/redo record in the buffer's history. Before any edits call history_group_begin and afterwards call history_group_end. After history_group_end all of the edits to the buffer supplied in history_group_begin will be merged, including all edits from function and command calls. */ - - View_ID view = get_active_view(app, Access_ReadWriteVisible); - Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible); - History_Group group = history_group_begin(app, buffer); - backspace_char(app); - backspace_char(app); - history_group_end(group); + + View_ID view = get_active_view(app, Access_ReadWriteVisible); + Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible); + History_Group group = history_group_begin(app, buffer); + backspace_char(app); + backspace_char(app); + history_group_end(group); } // tags: query; bar @@ -29,8 +29,8 @@ will be merged, including all edits from function and command calls. */ CUSTOM_COMMAND_SIG(play_with_a_counter) CUSTOM_DOC("Example of query bar") { - /* Query bars make a quick lightweight display of a single line of text for interactive - commands, while still showing the buffer. Query bars are convenient because they don't + /* Query bars make a quick lightweight display of a single line of text for interactive +commands, while still showing the buffer. Query bars are convenient because they don't require any complex UI setup, or extra rendering work inside your command. First, constructing a Query_Bar_Group is a convenient way to make sure the query bar @@ -40,49 +40,49 @@ Second, we make our query bar and start showing it with start_query_bar. Until w call end_query_bar on the same bar, or the group's destructor runs, the bar struct needs to remain in memory. The easy way to accomplish this is to just let the bar be on the commands stack frame. */ - - i32 counter = 0; - - Query_Bar_Group group(app); - Query_Bar dumb_bar = {}; - dumb_bar.prompt = SCu8("Goes away at >= 10"); - if (!start_query_bar(app, &dumb_bar, 0)){ - return; - } - - Query_Bar bar = {}; - bar.prompt = SCu8("Counter = "); - bar.string = SCu8(""); - if (!start_query_bar(app, &bar, 0)){ - return; - } - - for (;;){ - /* Notice here, we set the string of the query bar BEFORE we call get_next_input. - get_next_input blocks this command until the next input is sent from the core. Whatever + + i32 counter = 0; + + Query_Bar_Group group(app); + Query_Bar dumb_bar = {}; + dumb_bar.prompt = SCu8("Goes away at >= 10"); + if (!start_query_bar(app, &dumb_bar, 0)){ + return; + } + + Query_Bar bar = {}; + bar.prompt = SCu8("Counter = "); + bar.string = SCu8(""); + if (!start_query_bar(app, &bar, 0)){ + return; + } + + for (;;){ + /* Notice here, we set the string of the query bar BEFORE we call get_next_input. + get_next_input blocks this command until the next input is sent from the core. Whatever string we put in the bar now will be shown and remain in the bar until an event wakes up this command and we get a chance to modify the bar again. */ - Scratch_Block scratch(app); - bar.string = push_stringf(scratch, "%d", counter); - if (counter >= 10){ - end_query_bar(app, &dumb_bar, 0); - } - - User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape); - if (in.abort){ - break; - } - - if (match_key_code(&in.event, KeyCode_Up)){ - counter += 1; - } - else if (match_key_code(&in.event, KeyCode_Down)){ - counter -= 1; - } - else{ - leave_current_input_unhandled(app); - } - } + Scratch_Block scratch(app); + bar.string = push_stringf(scratch, "%d", counter); + if (counter >= 10){ + end_query_bar(app, &dumb_bar, 0); + } + + User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape); + if (in.abort){ + break; + } + + if (match_key_code(&in.event, KeyCode_Up)){ + counter += 1; + } + else if (match_key_code(&in.event, KeyCode_Down)){ + counter -= 1; + } + else{ + leave_current_input_unhandled(app); + } + } } // tags: input; loop @@ -90,46 +90,46 @@ up this command and we get a chance to modify the bar again. */ CUSTOM_COMMAND_SIG(display_key_codes) CUSTOM_DOC("Example of input handling loop") { - /* In the 4coder custom layer, inputs are handled by a view context. A view context is a + /* In the 4coder custom layer, inputs are handled by a view context. A view context is a thread that hands off control with the main thread of the 4coder core. When a command is running in a view context thread, it can wait for inputs from the core by calling get_next_input. If your command gets inputs from the core, then default input handling isn't happening, so command bindings don't trigger unless you trigger them yourself. */ - - Query_Bar_Group group(app); - Query_Bar bar = {}; - bar.prompt = SCu8("KeyCode = "); - if (!start_query_bar(app, &bar, 0)){ - return; - } - - Key_Code code = 0; - b32 is_dead_key = false; - - for (;;){ - Scratch_Block scratch(app); - if (code == 0){ - bar.string = SCu8("..."); - } - else{ - bar.string = push_stringf(scratch, "KeyCode_%s (%d)%s", key_code_name[code], code, - is_dead_key?" dead-key":""); - } - User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape); - if (in.abort){ - break; - } - if (in.event.kind == InputEventKind_KeyStroke){ - code = in.event.key.code; - is_dead_key = event_is_dead_key(&in.event); - } - else{ - /* Marking inputs as handled lets the core determine if certain inputs should - be passed to additional handlers. This is especially important for text input, - which is explained more in the example display_text_input. */ - leave_current_input_unhandled(app); - } - } + + Query_Bar_Group group(app); + Query_Bar bar = {}; + bar.prompt = SCu8("KeyCode = "); + if (!start_query_bar(app, &bar, 0)){ + return; + } + + Key_Code code = 0; + b32 is_dead_key = false; + + for (;;){ + Scratch_Block scratch(app); + if (code == 0){ + bar.string = SCu8("..."); + } + else{ + bar.string = push_stringf(scratch, "KeyCode_%s (%d)%s", key_code_name[code], code, + is_dead_key?" dead-key":""); + } + User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape); + if (in.abort){ + break; + } + if (in.event.kind == InputEventKind_KeyStroke){ + code = in.event.key.code; + is_dead_key = event_is_dead_key(&in.event); + } + else{ + /* Marking inputs as handled lets the core determine if certain inputs should + be passed to additional handlers. This is especially important for text input, + which is explained more in the example display_text_input. */ + leave_current_input_unhandled(app); + } + } } // tags: text; input @@ -137,41 +137,41 @@ isn't happening, so command bindings don't trigger unless you trigger them yours CUSTOM_COMMAND_SIG(display_text_input) CUSTOM_DOC("Example of to_writable and leave_current_input_unhandled") { - /* In the 4coder custom layer, inputs are handled by a view context. A view context is a + /* In the 4coder custom layer, inputs are handled by a view context. A view context is a thread that hands off control with the main thread of the 4coder core. When a command is running in a view context thread, it can wait for inputs from the core by calling get_next_input. If your command gets inputs from the core, then default input handling isn't happening, so command bindings don't trigger unless you trigger them yourself. */ - - Query_Bar_Group group(app); - Query_Bar bar = {}; - bar.prompt = SCu8("Weird String: "); - if (!start_query_bar(app, &bar, 0)){ - return; - } - - u8 buffer[256]; - u64 size = 0; - - for (;;){ - User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape); - if (in.abort){ - break; - } - - String_Const_u8 in_string = to_writable(&in); - if (in_string.size > 0){ - size = clamp_top(in_string.size, sizeof(buffer)); - block_copy(buffer, in_string.str, size); - bar.string = SCu8(buffer, size); - } - else if (in.event.kind == InputEventKind_KeyStroke){ - /* If we handle a key stroke then the core marks any text input generated from that + + Query_Bar_Group group(app); + Query_Bar bar = {}; + bar.prompt = SCu8("Weird String: "); + if (!start_query_bar(app, &bar, 0)){ + return; + } + + u8 buffer[256]; + u64 size = 0; + + for (;;){ + User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape); + if (in.abort){ + break; + } + + String_Const_u8 in_string = to_writable(&in); + if (in_string.size > 0){ + size = clamp_top(in_string.size, sizeof(buffer)); + block_copy(buffer, in_string.str, size); + bar.string = SCu8(buffer, size); + } + else if (in.event.kind == InputEventKind_KeyStroke){ + /* If we handle a key stroke then the core marks any text input generated from that key stroke as handled too, and the text input is never passed. By marking key strokes as unhandled, we ensure we get text input events. */ - leave_current_input_unhandled(app); - } - } + leave_current_input_unhandled(app); + } + } } // tags: string; number; query; user @@ -179,33 +179,33 @@ as unhandled, we ensure we get text input events. */ CUSTOM_COMMAND_SIG(string_repeat) CUSTOM_DOC("Example of query_user_string and query_user_number") { - Query_Bar_Group group(app); - Query_Bar string_bar = {}; - string_bar.prompt = SCu8("String: "); - u8 string_buffer[KB(1)]; - string_bar.string.str = string_buffer; - string_bar.string_capacity = sizeof(string_buffer); - Query_Bar number_bar = {}; - number_bar.prompt = SCu8("Repeat Count: "); - u8 number_buffer[KB(1)]; - number_bar.string.str = number_buffer; - number_bar.string_capacity = sizeof(number_buffer); - - if (query_user_string(app, &string_bar)){ - if (string_bar.string.size > 0){ - if (query_user_number(app, &number_bar)){ - if (number_bar.string.size > 0){ - i32 repeats = (i32)string_to_integer(number_bar.string, 10); - repeats = clamp_top(repeats, 1000); - Scratch_Block scratch(app); - String_Const_u8 msg = push_stringf(scratch, "%.*s\n", string_expand(string_bar.string)); - for (i32 i = 0; i < repeats; i += 1){ - print_message(app, msg); - } - } - } - } + Query_Bar_Group group(app); + Query_Bar string_bar = {}; + string_bar.prompt = SCu8("String: "); + u8 string_buffer[KB(1)]; + string_bar.string.str = string_buffer; + string_bar.string_capacity = sizeof(string_buffer); + Query_Bar number_bar = {}; + number_bar.prompt = SCu8("Repeat Count: "); + u8 number_buffer[KB(1)]; + number_bar.string.str = number_buffer; + number_bar.string_capacity = sizeof(number_buffer); + + if (query_user_string(app, &string_bar)){ + if (string_bar.string.size > 0){ + if (query_user_number(app, &number_bar)){ + if (number_bar.string.size > 0){ + i32 repeats = (i32)string_to_integer(number_bar.string, 10); + repeats = clamp_top(repeats, 1000); + Scratch_Block scratch(app); + String_Const_u8 msg = push_stringf(scratch, "%.*s\n", string_expand(string_bar.string)); + for (i32 i = 0; i < repeats; i += 1){ + print_message(app, msg); + } } + } + } + } } global Audio_Control the_music_control = {}; @@ -213,50 +213,54 @@ global Audio_Control the_music_control = {}; CUSTOM_COMMAND_SIG(music_start) CUSTOM_DOC("Starts the music.") { - local_persist Audio_Clip the_music_clip = {}; - if (the_music_clip.sample_count == 0){ - Scratch_Block scratch(app); - FILE *file = def_search_normal_fopen(scratch, "audio_test/chtulthu.wav", "rb"); - the_music_clip = audio_clip_from_wav_FILE(&global_permanent_arena, file); - fclose(file); - } - - if (!def_audio_is_playing(&the_music_control)){ - the_music_control.loop = true; - the_music_control.channel_volume[0] = 1.f; - the_music_control.channel_volume[1] = 1.f; - def_audio_play_clip(the_music_clip, &the_music_control); - } + local_persist Audio_Clip the_music_clip = {}; + if (the_music_clip.sample_count == 0){ + Scratch_Block scratch(app); + FILE *file = def_search_normal_fopen(scratch, "audio_test/chtulthu.wav", "rb"); + if (file != 0){ + the_music_clip = audio_clip_from_wav_FILE(&global_permanent_arena, file); + fclose(file); + } + } + + if (!def_audio_is_playing(&the_music_control)){ + the_music_control.loop = true; + the_music_control.channel_volume[0] = 1.f; + the_music_control.channel_volume[1] = 1.f; + def_audio_play_clip(the_music_clip, &the_music_control); + } } CUSTOM_COMMAND_SIG(music_stop) CUSTOM_DOC("Stops the music.") { - def_audio_stop(&the_music_control); + def_audio_stop(&the_music_control); } CUSTOM_COMMAND_SIG(hit_sfx) CUSTOM_DOC("Play the hit sound effect") { - local_persist Audio_Clip the_hit_clip = {}; - if (the_hit_clip.sample_count == 0){ - Scratch_Block scratch(app); - FILE *file = def_search_normal_fopen(scratch, "audio_test/hit.wav", "rb"); - the_hit_clip = audio_clip_from_wav_FILE(&global_permanent_arena, file); - fclose(file); - } - - local_persist u32 index = 0; - local_persist Audio_Control controls[8] = {}; - - Audio_Control *control = &controls[index%8]; - if (!def_audio_is_playing(control)){ - control->loop = false; - control->channel_volume[0] = 1.f; - control->channel_volume[1] = 1.f; - def_audio_play_clip(the_hit_clip, control); - index += 1; - } + local_persist Audio_Clip the_hit_clip = {}; + if (the_hit_clip.sample_count == 0){ + Scratch_Block scratch(app); + FILE *file = def_search_normal_fopen(scratch, "audio_test/hit.wav", "rb"); + if (file != 0){ + the_hit_clip = audio_clip_from_wav_FILE(&global_permanent_arena, file); + fclose(file); + } + } + + local_persist u32 index = 0; + local_persist Audio_Control controls[8] = {}; + + Audio_Control *control = &controls[index%8]; + if (!def_audio_is_playing(control)){ + control->loop = false; + control->channel_volume[0] = 1.f; + control->channel_volume[1] = 1.f; + def_audio_play_clip(the_hit_clip, control); + index += 1; + } } diff --git a/custom/generated/command_metadata.h b/custom/generated/command_metadata.h index 0357884c..0265f2ce 100644 --- a/custom/generated/command_metadata.h +++ b/custom/generated/command_metadata.h @@ -340,7 +340,7 @@ static Command_Metadata fcoder_metacmd_table[252] = { { PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "../code/custom/4coder_jump_sticky.cpp", 37, 511 }, { PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "../code/custom/4coder_base_commands.cpp", 39, 704 }, { PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "../code/custom/4coder_base_commands.cpp", 39, 690 }, -{ PROC_LINKS(hit_sfx, 0), false, "hit_sfx", 7, "Play the hit sound effect", 25, "../code/custom/4coder_examples.cpp", 34, 238 }, +{ PROC_LINKS(hit_sfx, 0), false, "hit_sfx", 7, "Play the hit sound effect", 25, "../code/custom/4coder_examples.cpp", 34, 240 }, { PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "../code/custom/4coder_tutorial.cpp", 34, 869 }, { PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "../code/custom/4coder_combined_write_commands.cpp", 49, 70 }, { PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "../code/custom/4coder_jump_sticky.cpp", 37, 564 }, @@ -416,7 +416,7 @@ static Command_Metadata fcoder_metacmd_table[252] = { { PROC_LINKS(multi_paste_interactive, 0), false, "multi_paste_interactive", 23, "Paste multiple lines from the clipboard history, controlled with arrow keys", 75, "../code/custom/4coder_clipboard.cpp", 35, 371 }, { PROC_LINKS(multi_paste_interactive_quick, 0), false, "multi_paste_interactive_quick", 29, "Paste multiple lines from the clipboard history, controlled by inputing the number of lines to paste", 100, "../code/custom/4coder_clipboard.cpp", 35, 380 }, { PROC_LINKS(music_start, 0), false, "music_start", 11, "Starts the music.", 17, "../code/custom/4coder_examples.cpp", 34, 213 }, -{ PROC_LINKS(music_stop, 0), false, "music_stop", 10, "Stops the music.", 16, "../code/custom/4coder_examples.cpp", 34, 232 }, +{ PROC_LINKS(music_stop, 0), false, "music_stop", 10, "Stops the music.", 16, "../code/custom/4coder_examples.cpp", 34, 234 }, { PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "../code/custom/4coder_project_commands.cpp", 42, 805 }, { PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "../code/custom/4coder_project_commands.cpp", 42, 814 }, { PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "../code/custom/4coder_base_commands.cpp", 39, 1576 }, diff --git a/custom/metadata_generator b/custom/metadata_generator new file mode 100755 index 00000000..e165e0b7 Binary files /dev/null and b/custom/metadata_generator differ diff --git a/project.4coder b/project.4coder index b6079103..de392f9d 100644 --- a/project.4coder +++ b/project.4coder @@ -26,35 +26,39 @@ load_paths = { commands = { .build_x64 = { -.win = "echo build: x64 & bin\build.bat", +.win = "echo build: x64 & bin\\build.bat", +.linux = "echo build: x64 & bin/build-linux.sh", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .build_x86 = { -.win = "echo build: x86 & bin\build.bat /DDEV_BUILD_X86", +.win = "echo build: x86 & bin\\build.bat /DDEV_BUILD_X86", +.linux = "echo build: x86 & bin/build-linux.sh /DDEV_BUILD_X86", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .package = { -.win = "echo package & bin\package.bat", +.win = "echo package & bin\\package.bat", +.linux = "echo package & bin/package.sh", .out = "*compilation*", .footer_panel = false, .save_dirty_files = true, .cursor_at_end = false, }, .run_one_time = { -.win = "pushd ..\build & one_time", +.win = "pushd ..\\build & one_time", +.linux = "pushd ../build & one_time", .out = "*run*", .footer_panel = false, .save_dirty_files = false, .cursor_at_end = false, }, .build_custom_api_docs = { -.win = "custom\bin\build_one_time docs\4ed_doc_custom_api_main.cpp ..\build", +.win = "custom\\bin\\build_one_time docs\\4ed_doc_custom_api_main.cpp ..\\build", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true,