From a9a1931e345a249f16167f8d810354664b80bb63 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Mon, 23 Nov 2020 13:46:01 -0800 Subject: [PATCH] Hack in new audio system contributed by casey; win32 only for now --- 4ed_system_api.cpp | 6 + custom/4coder_audio.cpp | 104 ++++++++++ custom/4coder_audio.h | 11 ++ custom/4coder_default_hooks.cpp | 9 + custom/4coder_default_include.cpp | 2 + custom/4coder_types.h | 35 ++++ custom/generated/command_metadata.h | 4 +- custom/generated/system_api.cpp | 2 + custom/generated/system_api.h | 5 + custom/generated/system_api_constructor.cpp | 5 + custom/generated/system_api_master_list.h | 1 + platform_win32/win32_4ed.cpp | 28 ++- platform_win32/win32_audio.cpp | 199 ++++++++++++++++++++ 13 files changed, 399 insertions(+), 12 deletions(-) create mode 100644 custom/4coder_audio.cpp create mode 100644 custom/4coder_audio.h create mode 100644 platform_win32/win32_audio.cpp diff --git a/4ed_system_api.cpp b/4ed_system_api.cpp index 910ed9ae..81e11430 100644 --- a/4ed_system_api.cpp +++ b/4ed_system_api.cpp @@ -303,6 +303,12 @@ define_api(Arena *arena){ api_param(arena, call, "Key_Mode", "mode"); } + { + API_Call *call = api_call(arena, api, "play_clip", "void"); + api_param(arena, call, "Audio_Clip", "clip"); + api_param(arena, call, "Audio_Control*", "control"); + } + return(api); } diff --git a/custom/4coder_audio.cpp b/custom/4coder_audio.cpp new file mode 100644 index 00000000..c68ce7d3 --- /dev/null +++ b/custom/4coder_audio.cpp @@ -0,0 +1,104 @@ +//////////////////////////////// +// NOTE(allen): Load Clip + +#if !defined(FCODER_SKIP_WAV) +#define FCODER_SKIP_WAV +#pragma pack(push, 1) +struct wave_fmt_data +{ + u16 wFormatTag; + u16 wChannels; + u32 dwSamplesPerSec; + u32 dwAvgBytesPerSec; + u16 wBlockAlign; + u16 wBitsPerSample; +}; + +struct riff_header +{ + 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)){ + 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); +} + +#include + +function Audio_Clip +audio_clip_from_wav_file_name(char *file_name){ + String_Const_u8 data = {}; + FILE *file = fopen(file_name, "rb"); + if (file != 0){ + fseek(file, 0, SEEK_END); + data.size = ftell(file); + data.str = (u8*)malloc(data.size); + if (data.str != 0 && data.size > 0){ + fseek(file, 0, SEEK_SET); + fread(data.str, data.size, 1, file); + } + fclose(file); + } + + Audio_Clip result = audio_clip_from_wav_data(data); + return(result); +} diff --git a/custom/4coder_audio.h b/custom/4coder_audio.h new file mode 100644 index 00000000..328d5da9 --- /dev/null +++ b/custom/4coder_audio.h @@ -0,0 +1,11 @@ +/* date = November 23rd 2020 1:18 pm */ + +#ifndef FCODER_AUDIO_H +#define FCODER_AUDIO_H + +//////////////////////////////// +// NOTE(allen): Load Clip + +function Audio_Clip audio_clip_from_wav_data(String_Const_u8 data); + +#endif //4CODER_AUDIO_H diff --git a/custom/4coder_default_hooks.cpp b/custom/4coder_default_hooks.cpp index cadfbd72..e5f1d968 100644 --- a/custom/4coder_default_hooks.cpp +++ b/custom/4coder_default_hooks.cpp @@ -18,6 +18,15 @@ CUSTOM_DOC("Default command for responding to a startup event") load_project(app); } } + + { + Audio_Clip test_clip = audio_clip_from_wav_file_name("W:\\4ed\\audio_test\\raygun_zap.wav"); + + local_persist Audio_Control test_control = {}; + test_control.left_volume_knob = AUDIO_PRODUCER_KNOB_ONE; + test_control.right_volume_knob = AUDIO_PRODUCER_KNOB_ONE; + system_play_clip(test_clip, &test_control); + } } CUSTOM_COMMAND_SIG(default_try_exit) diff --git a/custom/4coder_default_include.cpp b/custom/4coder_default_include.cpp index dd92cc1a..4a0d5d14 100644 --- a/custom/4coder_default_include.cpp +++ b/custom/4coder_default_include.cpp @@ -28,6 +28,7 @@ #endif #include "4coder_variables.h" +#include "4coder_audio.h" #include "4coder_profile.h" #include "4coder_async_tasks.h" #include "4coder_token.h" @@ -129,6 +130,7 @@ #include "4coder_doc_commands.cpp" #include "4coder_docs.cpp" #include "4coder_variables.cpp" +#include "4coder_audio.cpp" #include "4coder_examples.cpp" diff --git a/custom/4coder_types.h b/custom/4coder_types.h index 8327ef4f..2d2c8826 100644 --- a/custom/4coder_types.h +++ b/custom/4coder_types.h @@ -787,4 +787,39 @@ struct Process_State{ i64 return_code; }; +//////////////////////////////// + +api(custom) +struct Audio_Control{ +#define AUDIO_PRODUCER_KNOB_ONE 256 + volatile u32 left_volume_knob; + volatile u32 right_volume_knob; + volatile u32 generation; + volatile u32 last_played_sample_index;; +}; + +api(custom) +struct Audio_Clip{ + i16 *samples; + Audio_Control *control; + + u32 sample_count; + u32 at_sample_index; +}; + +api(custom) +struct Audio_System{ + volatile u32 quit; + volatile u32 ticket; + volatile u32 serving; + volatile u32 generation; + + u32 next_playing_clip_index; + Audio_Clip playing_clips[64]; + + // NOTE(casey): Requests to play sounds are written to a pending array to avoid long locking + volatile u32 pending_clip_count; + Audio_Clip pending_clips[64]; +}; + #endif diff --git a/custom/generated/command_metadata.h b/custom/generated/command_metadata.h index ce8b98d6..d14706b1 100644 --- a/custom/generated/command_metadata.h +++ b/custom/generated/command_metadata.h @@ -307,8 +307,8 @@ static Command_Metadata fcoder_metacmd_table[248] = { { PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 757 }, { PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 2062 }, { PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 7 }, -{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 23 }, -{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 67 }, +{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 32 }, +{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 76 }, { PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 161 }, { PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 79 }, { PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "W:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 112 }, diff --git a/custom/generated/system_api.cpp b/custom/generated/system_api.cpp index 4e961682..724f0b82 100644 --- a/custom/generated/system_api.cpp +++ b/custom/generated/system_api.cpp @@ -54,6 +54,7 @@ vtable->set_fullscreen = system_set_fullscreen; vtable->is_fullscreen = system_is_fullscreen; vtable->get_keyboard_modifiers = system_get_keyboard_modifiers; vtable->set_key_mode = system_set_key_mode; +vtable->play_clip = system_play_clip; } #if defined(DYNAMIC_LINK_API) function void @@ -112,6 +113,7 @@ system_set_fullscreen = vtable->set_fullscreen; system_is_fullscreen = vtable->is_fullscreen; system_get_keyboard_modifiers = vtable->get_keyboard_modifiers; system_set_key_mode = vtable->set_key_mode; +system_play_clip = vtable->play_clip; } #undef DYNAMIC_LINK_API #endif diff --git a/custom/generated/system_api.h b/custom/generated/system_api.h index 7f0f7a39..5d39f00f 100644 --- a/custom/generated/system_api.h +++ b/custom/generated/system_api.h @@ -52,6 +52,7 @@ #define system_is_fullscreen_sig() b32 system_is_fullscreen(void) #define system_get_keyboard_modifiers_sig() Input_Modifier_Set system_get_keyboard_modifiers(Arena* arena) #define system_set_key_mode_sig() void system_set_key_mode(Key_Mode mode) +#define system_play_clip_sig() void system_play_clip(Audio_Clip clip, Audio_Control* control) typedef String_Const_u8 system_get_path_type(Arena* arena, System_Path_Code path_code); typedef String_Const_u8 system_get_canonical_type(Arena* arena, String_Const_u8 name); typedef File_List system_get_file_list_type(Arena* arena, String_Const_u8 directory); @@ -106,6 +107,7 @@ typedef b32 system_set_fullscreen_type(b32 full_screen); typedef b32 system_is_fullscreen_type(void); typedef Input_Modifier_Set system_get_keyboard_modifiers_type(Arena* arena); typedef void system_set_key_mode_type(Key_Mode mode); +typedef void system_play_clip_type(Audio_Clip clip, Audio_Control* control); struct API_VTable_system{ system_get_path_type *get_path; system_get_canonical_type *get_canonical; @@ -161,6 +163,7 @@ system_set_fullscreen_type *set_fullscreen; system_is_fullscreen_type *is_fullscreen; system_get_keyboard_modifiers_type *get_keyboard_modifiers; system_set_key_mode_type *set_key_mode; +system_play_clip_type *play_clip; }; #if defined(STATIC_LINK_API) internal String_Const_u8 system_get_path(Arena* arena, System_Path_Code path_code); @@ -217,6 +220,7 @@ internal b32 system_set_fullscreen(b32 full_screen); internal b32 system_is_fullscreen(void); internal Input_Modifier_Set system_get_keyboard_modifiers(Arena* arena); internal void system_set_key_mode(Key_Mode mode); +internal void system_play_clip(Audio_Clip clip, Audio_Control* control); #undef STATIC_LINK_API #elif defined(DYNAMIC_LINK_API) global system_get_path_type *system_get_path = 0; @@ -273,5 +277,6 @@ global system_set_fullscreen_type *system_set_fullscreen = 0; global system_is_fullscreen_type *system_is_fullscreen = 0; global system_get_keyboard_modifiers_type *system_get_keyboard_modifiers = 0; global system_set_key_mode_type *system_set_key_mode = 0; +global system_play_clip_type *system_play_clip = 0; #undef DYNAMIC_LINK_API #endif diff --git a/custom/generated/system_api_constructor.cpp b/custom/generated/system_api_constructor.cpp index c7fccb54..152a3c58 100644 --- a/custom/generated/system_api_constructor.cpp +++ b/custom/generated/system_api_constructor.cpp @@ -245,5 +245,10 @@ api_param(arena, call, "Arena*", "arena"); API_Call *call = api_call_with_location(arena, result, string_u8_litexpr("set_key_mode"), string_u8_litexpr("void"), string_u8_litexpr("")); api_param(arena, call, "Key_Mode", "mode"); } +{ +API_Call *call = api_call_with_location(arena, result, string_u8_litexpr("play_clip"), string_u8_litexpr("void"), string_u8_litexpr("")); +api_param(arena, call, "Audio_Clip", "clip"); +api_param(arena, call, "Audio_Control*", "control"); +} return(result); } diff --git a/custom/generated/system_api_master_list.h b/custom/generated/system_api_master_list.h index 8408ad94..3ed646ff 100644 --- a/custom/generated/system_api_master_list.h +++ b/custom/generated/system_api_master_list.h @@ -52,3 +52,4 @@ api(system) function b32 set_fullscreen(b32 full_screen); api(system) function b32 is_fullscreen(void); api(system) function Input_Modifier_Set get_keyboard_modifiers(Arena* arena); api(system) function void set_key_mode(Key_Mode mode); +api(system) function void play_clip(Audio_Clip clip, Audio_Control* control); diff --git a/platform_win32/win32_4ed.cpp b/platform_win32/win32_4ed.cpp index d52bdf9a..2b1f7774 100644 --- a/platform_win32/win32_4ed.cpp +++ b/platform_win32/win32_4ed.cpp @@ -179,6 +179,9 @@ struct Win32_Vars{ HWND window_handle; f32 screen_scale_factor; + Audio_System audio_system; + DWORD audio_thread_id; + f64 count_per_usecond; b32 first; i32 running_cli; @@ -259,6 +262,7 @@ handle_type_ptr(void *ptr){ //////////////////////////////// #include "win32_4ed_functions.cpp" +#include "win32_audio.cpp" //////////////////////////////// @@ -355,9 +359,16 @@ system_set_key_mode_sig(){ win32vars.key_mode = mode; } -// -// Clipboard -// +//////////////////////////////// +// NOTE(allen): Audio + +internal +system_play_clip_sig(){ + win32_play_clip(clip, control); +} + +//////////////////////////////// +// NOTE(allen): Clipboard internal String_Const_u8 win32_read_clipboard_contents(Thread_Context *tctx, Arena *arena){ @@ -1783,10 +1794,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS } } - // - // Window and GL Initialization - // - + // NOTE(allen): Window Init RECT window_rect = {}; if (plat_settings.set_window_size){ window_rect.right = plat_settings.window_w; @@ -1811,10 +1819,10 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS GetClientRect(win32vars.window_handle, &window_rect); win32_resize(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top); - // - // Misc System Initializations - // + // NOTE(allen): Audio Init + win32vars.audio_thread_id = win32_audio_init(&win32vars.audio_system); + // NOTE(allen): Misc Init if (!AddClipboardFormatListener(win32vars.window_handle)){ Scratch_Block scratch(win32vars.tctx); win32_output_error_string(scratch, ErrorString_UseLog); diff --git a/platform_win32/win32_audio.cpp b/platform_win32/win32_audio.cpp new file mode 100644 index 00000000..9d1e3ca2 --- /dev/null +++ b/platform_win32/win32_audio.cpp @@ -0,0 +1,199 @@ +//////////////////////////////// +// NOTE(allen): Win32 Audio Functions + +function u32 +AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend) +{ + // NOTE(casey): Returns the original value _prior_ to adding + u32 Result = _InterlockedExchangeAdd(Value, Addend); + return(Result); +} + +function void +win32_begin_ticket_mutex(Audio_System *Crunky) +{ + u32 Ticket = AtomicAddU32AndReturnOriginal(&Crunky->ticket, 1); + while(Ticket != Crunky->serving) {_mm_pause();} +} + +function void +win32_end_ticket_mutex(Audio_System *Crunky) +{ + AtomicAddU32AndReturnOriginal(&Crunky->serving, 1); +} + +function void +win32_play_clip(Audio_Clip clip, Audio_Control *control){ + clip.control = control; + Audio_System *Crunky = &win32vars.audio_system; + win32_begin_ticket_mutex(Crunky); + if (Crunky->pending_clip_count < ArrayCount(Crunky->pending_clips)) + { + Crunky->pending_clips[Crunky->pending_clip_count++] = clip; + } + win32_end_ticket_mutex(Crunky); +} + +function void +win32_mix_audio(Audio_System *Crunky, u32 SampleCount, i16 *Dest){ + // NOTE(casey): Move pending sounds into the playing list + win32_begin_ticket_mutex(Crunky); + ++Crunky->generation; + for(u32 PendIndex = 0; + PendIndex < Crunky->pending_clip_count; + ++PendIndex) + { + u32 DestIndex = Crunky->next_playing_clip_index++ % ArrayCount(Crunky->playing_clips); + Crunky->playing_clips[DestIndex] = Crunky->pending_clips[PendIndex]; + } + Crunky->pending_clip_count = 0; + win32_end_ticket_mutex(Crunky); + + memset(Dest, 0, SampleCount*4); + for(u32 SoundIndex = 0; + SoundIndex < ArrayCount(Crunky->playing_clips); + ++SoundIndex) + { + Audio_Clip Sound = Crunky->playing_clips[SoundIndex]; + u32 SamplesToMix = Min((Sound.sample_count - Sound.at_sample_index), SampleCount); + Crunky->playing_clips[SoundIndex].at_sample_index += SamplesToMix; + + i32 LeftVol = AUDIO_PRODUCER_KNOB_ONE; + i32 RightVol = AUDIO_PRODUCER_KNOB_ONE; + if(SamplesToMix && Sound.control) + { + LeftVol = Sound.control->left_volume_knob; + RightVol = Sound.control->right_volume_knob; + Sound.control->generation = Crunky->generation; + Sound.control->last_played_sample_index = Crunky->playing_clips[SoundIndex].at_sample_index; + } + + + for(u32 SampleIndex = 0; + SampleIndex < SamplesToMix; + ++SampleIndex) + { + u32 src_base_indx = 2*(Sound.at_sample_index + SampleIndex); + u32 dst_base_indx = 2*SampleIndex; + Dest[dst_base_indx + 0] += (i16)(LeftVol*(i32)Sound.samples[src_base_indx + 0]/AUDIO_PRODUCER_KNOB_ONE); + Dest[dst_base_indx + 1] += (i16)(RightVol*(i32)Sound.samples[src_base_indx + 1]/AUDIO_PRODUCER_KNOB_ONE); + } + } +} + +function b32 +win32_submit_audio(Audio_System *Crunky, HWAVEOUT WaveOut, WAVEHDR *Header){ + b32 Result = false; + + u32 SampleCount = Header->dwBufferLength/4; + i16 *Samples = (i16 *)Header->lpData; + win32_mix_audio(Crunky, SampleCount, Samples); + + DWORD Error = waveOutPrepareHeader(WaveOut, Header, sizeof(*Header)); + if(Error == MMSYSERR_NOERROR) + { + Error = waveOutWrite(WaveOut, Header, sizeof(*Header)); + if(Error == MMSYSERR_NOERROR) + { + // NOTE(casey): Success + Result = true; + } + } + + return(Result); +} + +function DWORD WINAPI +win32_audio_loop(void *Passthrough){ + Audio_System *Crunky = (Audio_System *)Passthrough; + + // + // NOTE(casey): Set up our audio output buffer + // + u32 SamplesPerSecond = 48000; + u32 SamplesPerBuffer = 16*SamplesPerSecond/1000; + u32 ChannelCount = 2; + u32 BytesPerChannelValue = 2; + u32 BytesPerSample = ChannelCount*BytesPerChannelValue; + + u32 BufferCount = 3; + u32 BufferSize = SamplesPerBuffer*BytesPerSample; + u32 HeaderSize = sizeof(WAVEHDR); + u32 TotalBufferSize = (BufferSize+HeaderSize); + u32 TotalAudioMemorySize = BufferCount*TotalBufferSize; + + // + // NOTE(casey): Initialize audio out + // + HWAVEOUT WaveOut = {}; + + WAVEFORMATEX Format = {}; + Format.wFormatTag = WAVE_FORMAT_PCM; + Format.nChannels = (WORD)ChannelCount; + Format.wBitsPerSample = (WORD)(8*BytesPerChannelValue); + Format.nSamplesPerSec = SamplesPerSecond; + Format.nBlockAlign = (Format.nChannels*Format.wBitsPerSample)/8; + Format.nAvgBytesPerSec = Format.nBlockAlign * Format.nSamplesPerSec; + + if(waveOutOpen(&WaveOut, WAVE_MAPPER, &Format, GetCurrentThreadId(), 0, CALLBACK_THREAD) == MMSYSERR_NOERROR) + { + void *AudioBufferMemory = VirtualAlloc(0, TotalAudioMemorySize, MEM_COMMIT, PAGE_READWRITE); + if(AudioBufferMemory) + { + u8 *At = (u8 *)AudioBufferMemory; + for(u32 BufferIndex = 0; + BufferIndex < BufferCount; + ++BufferIndex) + { + WAVEHDR *Header = (WAVEHDR *)At; + At += sizeof(WAVEHDR); + Header->lpData = (char *)At; + Header->dwBufferLength = TotalBufferSize; + + At += TotalBufferSize; + + win32_submit_audio(Crunky, WaveOut, Header); + } + } + else + { + Crunky->quit = true; + } + } + else + { + Crunky->quit = true; + } + + // + // NOTE(casey): Serve audio forever (until we are told to stop) + // + // SetTimer(0, 0, 100, 0); + while(!Crunky->quit) + { + MSG Message = {}; + GetMessage(&Message, 0, 0, 0); + if(Message.message == MM_WOM_DONE) + { + WAVEHDR *Header = (WAVEHDR *)Message.lParam; + if(Header->dwFlags & WHDR_DONE) + { + Header->dwFlags &= ~WHDR_DONE; + } + + waveOutUnprepareHeader(WaveOut, Header, sizeof(*Header)); + + win32_submit_audio(Crunky, WaveOut, Header); + } + } + + return(0); +} + +function DWORD +win32_audio_init(Audio_System *audio_system){ + DWORD thread_id = 0; + HANDLE handle = CreateThread(0, 0, win32_audio_loop, audio_system, 0, &thread_id); + CloseHandle(handle); + return(thread_id); +}