Browse Source

merge blazor: +voice chat, +voice meeting

HOME 2 years ago
parent
commit
1e8a49b876
46 changed files with 364 additions and 1776 deletions
  1. 0 16
      SimpleVoiceChat.BlazorWasm/App.razor
  2. 0 159
      SimpleVoiceChat.BlazorWasm/Dail.razor
  3. 0 93
      SimpleVoiceChat.BlazorWasm/LocalRecordPlayback.razor
  4. 0 49
      SimpleVoiceChat.BlazorWasm/Properties/launchSettings.json
  5. 0 21
      SimpleVoiceChat.BlazorWasm/SimpleVoiceChat.BlazorWasm.csproj
  6. 0 7
      SimpleVoiceChat.BlazorWasm/SvcGlobal.cs
  7. 0 26
      SimpleVoiceChat.BlazorWasm/SvcProgram.cs
  8. 0 9
      SimpleVoiceChat.BlazorWasm/_Imports.razor
  9. BIN
      SimpleVoiceChat.BlazorWasm/wwwroot/favicon.png
  10. BIN
      SimpleVoiceChat.BlazorWasm/wwwroot/icon-192.png
  11. 0 56
      SimpleVoiceChat.BlazorWasm/wwwroot/index.html
  12. 0 7
      SimpleVoiceChat.BlazorWasm/wwwroot/lib/bootstrap/bootstrap.bundle.min.js
  13. 0 7
      SimpleVoiceChat.BlazorWasm/wwwroot/lib/bootstrap/default/bootstrap.min.css
  14. 0 397
      SimpleVoiceChat.BlazorWasm/wwwroot/loading.css
  15. 0 21
      SimpleVoiceChat.BlazorWasm/wwwroot/svc-audio-playback-module.js
  16. 0 18
      SimpleVoiceChat.BlazorWasm/wwwroot/svc.css
  17. 0 49
      SimpleVoiceMeeting.BlazorWasm/Properties/launchSettings.json
  18. 0 9
      SimpleVoiceMeeting.BlazorWasm/README.md
  19. 0 21
      SimpleVoiceMeeting.BlazorWasm/SimpleVoiceMeeting.BlazorWasm.csproj
  20. 0 46
      SimpleVoiceMeeting.BlazorWasm/SvmAudioCaptureModule.cs
  21. 0 19
      SimpleVoiceMeeting.BlazorWasm/SvmAudioPlaybackModule.cs
  22. 0 7
      SimpleVoiceMeeting.BlazorWasm/SvmGlobal.cs
  23. 0 26
      SimpleVoiceMeeting.BlazorWasm/SvmProgram.cs
  24. 0 9
      SimpleVoiceMeeting.BlazorWasm/_Imports.razor
  25. BIN
      SimpleVoiceMeeting.BlazorWasm/wwwroot/favicon.png
  26. BIN
      SimpleVoiceMeeting.BlazorWasm/wwwroot/icon-192.png
  27. 0 56
      SimpleVoiceMeeting.BlazorWasm/wwwroot/index.html
  28. 0 7
      SimpleVoiceMeeting.BlazorWasm/wwwroot/lib/bootstrap/bootstrap.bundle.min.js
  29. 0 7
      SimpleVoiceMeeting.BlazorWasm/wwwroot/lib/bootstrap/default/bootstrap.min.css
  30. 0 397
      SimpleVoiceMeeting.BlazorWasm/wwwroot/loading.css
  31. 0 49
      SimpleVoiceMeeting.BlazorWasm/wwwroot/svm-audio-capture-module.js
  32. 0 18
      SimpleVoiceMeeting.BlazorWasm/wwwroot/svm.css
  33. 24 120
      SimpleWebChat.BlazorWasm/App.razor
  34. 6 6
      SimpleVoiceChat.BlazorWasm/SvcAudioCaptureModule.cs
  35. 3 3
      SimpleVoiceChat.BlazorWasm/SvcAudioPlaybackModule.cs
  36. 7 0
      SimpleWebChat.BlazorWasm/Modules/GlobalModule.cs
  37. 1 1
      SimpleVoiceChat.BlazorWasm/README.md
  38. 2 2
      SimpleWebChat.BlazorWasm/SimpleWebChat.BlazorWasm.csproj
  39. 1 1
      SimpleWebChat.BlazorWasm/SwcProgram.cs
  40. 125 0
      SimpleWebChat.BlazorWasm/UI/TextChat.razor
  41. 164 0
      SimpleWebChat.BlazorWasm/UI/VoiceChat.razor
  42. 27 23
      SimpleVoiceMeeting.BlazorWasm/App.razor
  43. 2 0
      SimpleWebChat.BlazorWasm/_Imports.razor
  44. 2 2
      SimpleVoiceChat.BlazorWasm/wwwroot/svc-audio-capture-module.js
  45. 0 0
      SimpleWebChat.BlazorWasm/wwwroot/audio-playback-module.js
  46. 0 12
      SimpleWebChat.sln

+ 0 - 16
SimpleVoiceChat.BlazorWasm/App.razor

@@ -1,16 +0,0 @@
-<ul class="nav nav-tabs" id="myTab" role="tablist">
-    <li class="nav-item" role="presentation">
-        <button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Dial</button>
-    </li>
-    <li class="nav-item" role="presentation">
-        <button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#profile-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">Local</button>
-    </li>
-</ul>
-<div class="tab-content" id="myTabContent">
-    <div class="tab-pane fade show active" id="home-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
-        <Dail></Dail>
-    </div>
-    <div class="tab-pane fade" id="profile-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0">
-        <LocalRecordPlayback></LocalRecordPlayback>
-    </div>
-</div>

+ 0 - 159
SimpleVoiceChat.BlazorWasm/Dail.razor

@@ -1,159 +0,0 @@
-@using System.Text
-@using System.Net.WebSockets
-@inject HttpClient http
-@code {
-    private bool ready = false;
-
-    private string num = "";
-    private List<string> logs = new();
-    private ClientWebSocket sck;
-}
-
-@if (!ready)
-{
-
-    <div class="row m-2">
-        <div class="col-12">
-            <button @onclick="@Init" class="btn btn-primary">Open Microphone</button>
-        </div>
-    </div>
-}
-else
-{
-    <div class="row">
-        <div class="col-6">
-
-            <div class="row mt-1">
-                <div class="col-3">
-                    <button class="btn btn-secondary" @onclick="@(()=>num="")">X</button>
-                </div>
-                <div class="col-6">
-                    <InputText ValueExpression="()=>num" ValueChanged="(v) =>num=v" Value="@num" class="w-100"></InputText>
-                </div>
-                <div class="col-3">
-                    <button class="btn btn-danger" @onclick="@Go">→</button>
-                </div>
-            </div>
-
-            <div class="row mt-1">
-                <div class="col-3"></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "1"; StateHasChanged(); })">1</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "2"; StateHasChanged(); })">2</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "3"; StateHasChanged(); })">3</button></div>
-            </div>
-            <div class="row mt-1">
-                <div class="col-3"></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "4"; StateHasChanged(); })">4</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "5"; StateHasChanged(); })">5</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "6"; StateHasChanged(); })">6</button></div>
-            </div>
-            <div class="row mt-1">
-                <div class="col-3"></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "7"; StateHasChanged(); })">7</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "8"; StateHasChanged(); })">8</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "9"; StateHasChanged(); })">9</button></div>
-            </div>
-            <div class="row mt-1">
-                <div class="col-3"></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "*"; StateHasChanged(); })">*</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "0"; StateHasChanged(); })">0</button></div>
-                <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "#"; StateHasChanged(); })">#</button></div>
-            </div>
-        </div>
-        <div class="col-6">
-            @foreach (var s in logs)
-            {
-                <div>
-                    <pre>@s</pre>
-                </div>
-            }
-        </div>
-    </div>
-}
-
-@code {
-    private WebSocket socket;
-
-    private async Task Init()
-    {
-        ready = await SvcAudioCaptureModule.Init();
-        if (ready)
-        {
-            SvcAudioCaptureModule.ChunkArrive += SendChunk;
-        }
-    }
-
-    private void SendChunk(byte[] obj)
-    {
-        socket?.SendAsync(obj, WebSocketMessageType.Binary, true, default);
-    }
-
-    private async Task Go()
-    {
-        if (socket != null)
-        {
-            try
-            {
-                await socket.CloseAsync(WebSocketCloseStatus.Empty, "Brrrrrr", default);
-            }
-            catch
-            {
-                //FUCK ERR
-            }
-
-            try
-            {
-                socket.Dispose();
-            }
-            catch
-            {
-                //FUCK ERR
-            }
-
-            logs.Insert(0, "Disconnected");
-            StateHasChanged();
-            socket = null;
-        }
-
-        sck = new ClientWebSocket();
-        sck.Options.AddSubProtocol("svcp");
-
-        var b = http.BaseAddress;
-        try
-        {
-            await sck.ConnectAsync(new Uri($"{(b.Scheme == "https" ? "wss" : "ws")}://{b.Host}:{b.Port}/connect/voice"), default);
-            await sck.SendAsync(Encoding.UTF8.GetBytes(num), WebSocketMessageType.Text, true, default);
-            logs.Insert(0, "Connected");
-            socket = sck;
-            await SvcAudioCaptureModule.Start();
-            while (true)
-            {
-                var buf = new Byte[15384];
-                var r = await sck.ReceiveAsync(buf, default);
-                if (r.Count == 0)
-                {
-                    logs.Insert(0, "Disconnected");
-                    StateHasChanged();
-                    break;
-                }
-                if (r.MessageType == WebSocketMessageType.Text)
-                {
-                    logs.Insert(0, Encoding.UTF8.GetString(buf, 0, r.Count));
-                    StateHasChanged();
-                }
-                else
-                {
-                    var chunk = new byte[r.Count];
-                    Array.Copy(buf, 0, chunk, 0, chunk.Length);
-                    await SvcAudioPlaybackModule.PlayChunk(chunk);
-                }
-            }
-        }
-        catch (Exception e)
-        {
-            logs.Insert(0, e.ToString());
-            StateHasChanged();
-        }
-
-    }
-}

+ 0 - 93
SimpleVoiceChat.BlazorWasm/LocalRecordPlayback.razor

@@ -1,93 +0,0 @@
-@code
-{
-    private bool _isInit = false;
-    private List<byte[]> _chunks = new();
-
-    private List<string> _logs = new();
-
-    private string _playUrl;
-
-}
-
-<div class="row m-2">
-    <div class="col-12">
-
-        @if (_isInit == false)
-        {
-            <button @onclick="Init" class="btn btn-primary">Init</button>
-        }
-        else
-        {
-
-            <button @onclick="Start" class="btn btn-primary">Start</button>
-
-            <button @onclick="Stop" class="btn btn-primary">Stop</button>
-
-            <button @onclick="()=>_chunks.Clear()" class="btn btn-primary">Clear</button>
-
-            for (var index = 0; index < _chunks.Count; index++)
-            {
-                var chunk = _chunks[index];
-                <button @onclick="() => Play(chunk)"> CHUNK-@index-@(chunk.Length)</button>
-            }
-        }
-    </div>
-</div>
-
-<hr />
-@foreach (var s in _logs)
-{
-    <div>
-        <pre>@s</pre>
-    </div>
-}
-
-@code
-{
-    protected override async Task OnAfterRenderAsync(bool firstRender)
-    {
-        await base.OnAfterRenderAsync(firstRender);
-    }
-
-    async Task Init()
-    {
-        try
-        {
-            _isInit = await SvcAudioCaptureModule.Init();
-            SvcAudioCaptureModule.ChunkArrive += ChunkArrive;
-        }
-        catch (Exception e)
-        {
-            _logs.Add(e.ToString());
-        }
-        StateHasChanged();
-    }
-
-    async Task Start()
-    {
-        try
-        {
-            await SvcAudioCaptureModule.Start();
-        }
-        catch (Exception e)
-        {
-            _logs.Add(e.ToString());
-        }
-        StateHasChanged();
-    }
-
-    private void ChunkArrive(byte[] obj)
-    {
-        _chunks.Add(obj);
-        StateHasChanged();
-    }
-
-    async Task Stop()
-    {
-        SvcAudioCaptureModule.ChunkArrive -= ChunkArrive;
-        await SvcAudioCaptureModule.Stop();
-    }
-
-    private async Task Play(byte[] chunk) => await SvcAudioPlaybackModule.PlayChunk(chunk);
-
-}

+ 0 - 49
SimpleVoiceChat.BlazorWasm/Properties/launchSettings.json

@@ -1,49 +0,0 @@
-{
-  "profiles": {
-    "http": {
-      "commandName": "Project",
-      "launchBrowser": true,
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
-      },
-      "dotnetRunMessages": true,
-      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
-      "applicationUrl": "https://0.0.0.0:5201"
-    },
-    "HotLoad": {
-      "commandName": "Executable",
-      "executablePath": "dotnet",
-      "commandLineArgs": "watch",
-      "workingDirectory": ".",
-      "environmentVariables": {
-        "DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER": "1"
-      }
-    },
-    "WSL": {
-      "commandName": "WSL2",
-      "launchBrowser": true,
-      "launchUrl": "http://localhost:5201",
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development",
-        "ASPNETCORE_URLS": "http://localhost:5201"
-      },
-      "distributionName": ""
-    },
-    "IIS Express": {
-      "commandName": "IISExpress",
-      "launchBrowser": true,
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
-      },
-      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
-    }
-  },
-  "iisSettings": {
-    "windowsAuthentication": false,
-    "anonymousAuthentication": true,
-    "iisExpress": {
-      "applicationUrl": "http://localhost:63687",
-      "sslPort": 0
-    }
-  }
-}

+ 0 - 21
SimpleVoiceChat.BlazorWasm/SimpleVoiceChat.BlazorWasm.csproj

@@ -1,21 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
-
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <allowUnsafeBlocks>true</allowUnsafeBlocks>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0" PrivateAssets="all" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Content Update="wwwroot\index.html">
-      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
-      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-    </Content>
-  </ItemGroup>
-
-</Project>

+ 0 - 7
SimpleVoiceChat.BlazorWasm/SvcGlobal.cs

@@ -1,7 +0,0 @@
-namespace SimpleVoiceChat.BlazorWasm
-{
-    public static class SvcGlobal
-    {
-        public static string BasePath { get; set; }
-    }
-}

+ 0 - 26
SimpleVoiceChat.BlazorWasm/SvcProgram.cs

@@ -1,26 +0,0 @@
-using Microsoft.AspNetCore.Components.Web;
-using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
-using SimpleVoiceChat.BlazorWasm;
-
-var builder = WebAssemblyHostBuilder.CreateDefault(args);
-builder.RootComponents.Add<App>("#app");
-builder.RootComponents.Add<HeadOutlet>("head::after");
-
-builder.Services.AddScoped(sp =>
-{
-    var http = new HttpClient();
-
-#if DEBUG
-    http.BaseAddress = new Uri("https://svc-local-dev.topcl.net");
-#else
-    var NavManager = sp.GetService<Microsoft.AspNetCore.Components.NavigationManager>();
-    var ba = new Uri($"{NavManager.ToAbsoluteUri("/").GetLeftPart(UriPartial.Scheme | UriPartial.Authority)}");
-    http.BaseAddress = ba;
-    SvcGlobal.BasePath = NavManager.BaseUri;
-#endif
-
-    return http;
-});
-
-await builder.Build().RunAsync();
- 

+ 0 - 9
SimpleVoiceChat.BlazorWasm/_Imports.razor

@@ -1,9 +0,0 @@
-@using System.Net.Http
-@using System.Net.Http.Json
-@using Microsoft.AspNetCore.Components.Forms
-@using Microsoft.AspNetCore.Components.Routing
-@using Microsoft.AspNetCore.Components.Web
-@using Microsoft.AspNetCore.Components.Web.Virtualization
-@using Microsoft.AspNetCore.Components.WebAssembly.Http
-@using Microsoft.JSInterop
-@using SimpleVoiceChat.BlazorWasm

BIN
SimpleVoiceChat.BlazorWasm/wwwroot/favicon.png


BIN
SimpleVoiceChat.BlazorWasm/wwwroot/icon-192.png


+ 0 - 56
SimpleVoiceChat.BlazorWasm/wwwroot/index.html

@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <link rel="icon" type="image/x-icon" href="favicon.ico">
-    <title>Simple Web Chat</title>
-
-    <base href="/" />
-
-    <link href="lib/bootstrap/default/bootstrap.min.css" rel="stylesheet" />
-
-    <link href="svc.css" rel="stylesheet" />
-</head>
-
-<body>
-    <div id="app">
-        <div class="loader text-center" id="loading">
-            <link href="loading.css" rel="stylesheet" />
-            <div class="logo">
-                <div class="one common"></div>
-                <div class="two common"></div>
-                <div class="three common"></div>
-                <div class="four common"></div>
-                <div class="five common"></div>
-                <div class="six common"></div>
-                <div class="seven common"></div>
-                <div class="eight common"></div>
-            </div>
-            <div class="intro">
-                <img src="icon-192.png" />
-                <span>精彩即将呈现</span>
-            </div>
-            <div class="bar">
-                <div class="progress"></div>
-            </div>
-        </div>
-    </div>
-
-    <div id="blazor-error-ui">
-        <environment include="Staging,Production">
-            An error has occurred. This application may no longer respond until reloaded.
-        </environment>
-        <environment include="Development">
-            An unhandled exception has occurred. See browser dev tools for details.
-        </environment>
-        <a href="" class="reload">Reload</a>
-        <a class="dismiss"><i class="fa fa-times"></i></a>
-    </div>
-
-    <script src="_framework/blazor.webassembly.js"></script>
-    <script src="lib/bootstrap/bootstrap.bundle.min.js"></script>
-
-</body>
-</html>

File diff suppressed because it is too large
+ 0 - 7
SimpleVoiceChat.BlazorWasm/wwwroot/lib/bootstrap/bootstrap.bundle.min.js


File diff suppressed because it is too large
+ 0 - 7
SimpleVoiceChat.BlazorWasm/wwwroot/lib/bootstrap/default/bootstrap.min.css


+ 0 - 397
SimpleVoiceChat.BlazorWasm/wwwroot/loading.css

@@ -1,397 +0,0 @@
-.loader {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background-color: #161B29;
-    transition: opacity .5s linear;
-    z-index: 2050;
-    display: flex;
-    flex-flow: column;
-}
-
-    .loader.is-done {
-        opacity: 0;
-    }
-
-    .loader .logo {
-        width: 200px;
-        height: 200px;
-        position: relative;
-        margin: 0 auto;
-    }
-
-@keyframes logo {
-    0%, 100% {
-        box-shadow: 1px 1px 25px 10px rgba(146, 148, 248, 0.4);
-    }
-
-    50% {
-        box-shadow: none;
-    }
-}
-
-.loader .intro {
-    width: 250px;
-    color: #fff;
-    font-size: 1.5rem;
-    text-align: center;
-    margin: 3rem auto;
-    display: inline-flex;
-    justify-content: center;
-    align-items: center;
-    padding: 0.5rem 1rem;
-    position: relative;
-    overflow: hidden;
-    border: 1px solid rgb(146, 148, 248);
-    animation: intro 3s linear infinite
-}
-
-@keyframes intro {
-    0%, 100% {
-        box-shadow: 0px 0px 25px 10px rgba(146, 148, 248, 0.4);
-    }
-
-    40%, 60% {
-        box-shadow: 0px 0px 25px 0px rgba(146, 148, 248, 0.4);
-    }
-}
-
-.loader .intro:before {
-    content: "";
-    position: absolute;
-    top: 0;
-    left: -100%;
-    width: 100%;
-    height: 100%;
-    background: linear-gradient( 120deg, transparent, rgba(146, 148, 248, 0.4), transparent );
-    animation: flash 2.5s linear infinite;
-}
-
-@keyframes flash {
-    0%, 100% {
-    }
-
-    10%, 90% {
-        left: 100%;
-    }
-}
-
-.loader .intro img {
-    border-radius: 3px;
-    width: 40px;
-    margin-right: 1rem;
-}
-
-.loader .intro span {
-    color: #fff;
-    animation: title 3s linear infinite;
-}
-
-@keyframes title {
-    0%, 100% {
-        color: #fff;
-    }
-
-    60% {
-        color: #666;
-    }
-}
-
-.loader .bar {
-    width: 50%;
-    height: 4px;
-    border-radius: 2px;
-    margin: auto;
-    background: #E645D0;
-}
-
-    .loader .bar .progress {
-        width: 0%;
-        height: 4px;
-        margin: auto;
-        background: #17E1E6;
-    }
-
-.loader .logo {
-    animation: logo 5s linear infinite;
-    -moz-animation: logo 5s linear infinite;
-    /* Firefox */
-    -webkit-animation: logo 5s linear infinite;
-    /* Safari and Chrome */
-    -o-animation: logo 5s linear infinite;
-    /* Opera */
-}
-
-@keyframes logo {
-    from {
-        transform: rotate(0deg);
-    }
-
-    to {
-        transform: rotate(-360deg);
-    }
-}
-
-.loader .progress {
-    animation: progress 12s linear;
-    -moz-animation: progress 12s linear;
-    /* Firefox */
-    -webkit-animation: progress 12s linear;
-    /* Safari and Chrome */
-    -o-animation: progress 12s linear;
-    /* Opera */
-    animation: progress 12s linear infinite;
-}
-
-@keyframes progress {
-    0%, 100% {
-        width: 0%;
-        background-color: #17e1e6;
-    }
-
-    50% {
-        width: 100%;
-        background-color: #28a745;
-    }
-}
-
-.loader .common {
-    height: 5vw;
-    max-height: 100%;
-    overflow: auto;
-    width: 2vw;
-    margin: auto;
-    max-width: 100%;
-    position: absolute;
-    border-radius: 0vw 10vw 0vw 10vw;
-    box-shadow: inset 0vw 0vw 0vw .1vw #E645D0, 0vw 0vw 1.5vw 0vw #E645D0;
-}
-
-.loader .one {
-    transform: rotate(45deg);
-    left: 0;
-    right: 0;
-    top: 0;
-    bottom: 7.5vw;
-}
-
-.loader .two {
-    transform: rotate(90deg);
-    left: 5.5vw;
-    right: 0;
-    top: 0;
-    bottom: 5.5vw;
-}
-
-.loader .three {
-    transform: rotate(135deg);
-    left: 7.5vw;
-    right: 0;
-    top: 0;
-    bottom: 0;
-}
-
-.loader .four {
-    transform: rotate(180deg);
-    left: 5.5vw;
-    right: 0;
-    top: 5.5vw;
-    bottom: 0;
-}
-
-.loader .five {
-    transform: rotate(225deg);
-    left: 0;
-    right: 0;
-    top: 7.5vw;
-    bottom: 0;
-}
-
-.loader .six {
-    transform: rotate(270deg);
-    left: 0;
-    right: 5.5vw;
-    top: 5.5vw;
-    bottom: 0;
-}
-
-.loader .seven {
-    transform: rotate(315deg);
-    left: 0;
-    right: 7.5vw;
-    top: 0;
-    bottom: 0;
-}
-
-.loader .eight {
-    transform: rotate(360deg);
-    left: 0;
-    right: 5.5vw;
-    top: 0;
-    bottom: 5.5vw;
-}
-
-.loader .one {
-    animation: one 1s ease infinite;
-    -moz-animation: one 1s ease infinite;
-    /* Firefox */
-    -webkit-animation: one 1s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: one 1s ease infinite;
-    /* Opera */
-}
-
-@keyframes one {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .two {
-    animation: two 1s .125s ease infinite;
-    -moz-animation: two 1s .125s ease infinite;
-    /* Firefox */
-    -webkit-animation: two 1s .125s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: two 1s .125s ease infinite;
-    /* Opera */
-}
-
-@keyframes two {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .three {
-    animation: three 1s .25s ease infinite;
-    -moz-animation: three 1s .25s ease infinite;
-    /* Firefox */
-    -webkit-animation: three 1s .25s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: three 1s .25s ease infinite;
-    /* Opera */
-}
-
-@keyframes three {
-    0%, 100% {
-    }
-
-    50% {
-        background:;
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .four {
-    animation: four 1s .375s ease infinite;
-    -moz-animation: four 1s .375s ease infinite;
-    /* Firefox */
-    -webkit-animation: four 1s .375s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: four 1s .375s ease infinite;
-    /* Opera */
-}
-
-@keyframes four {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .five {
-    animation: five 1s .5s ease infinite;
-    -moz-animation: five 1s .5s ease infinite;
-    /* Firefox */
-    -webkit-animation: five 1s .5s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: five 1s .5s ease infinite;
-    /* Opera */
-}
-
-@keyframes five {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .six {
-    animation: six 1s .625s ease infinite;
-    -moz-animation: six 1s .625s ease infinite;
-    /* Firefox */
-    -webkit-animation: six 1s .625s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: six 1s .625s ease infinite;
-    /* Opera */
-}
-
-@keyframes six {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .seven {
-    animation: seven 1s .750s ease infinite;
-    -moz-animation: seven 1s .750s ease infinite;
-    /* Firefox */
-    -webkit-animation: seven 1s .750s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: seven 1s .750s ease infinite;
-    /* Opera */
-}
-
-@keyframes seven {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .eight {
-    animation: eight 1s .875s ease infinite;
-    -moz-animation: eight 1s .875s ease infinite;
-    /* Firefox */
-    -webkit-animation: eight 1s .875s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: eight 1s .875s ease infinite;
-    /* Opera */
-}
-
-@keyframes eight {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-@media (min-width: 768px) {
-    .loader {
-        padding-top: 8rem;
-    }
-
-        .loader .intro {
-            margin-top: 6rem;
-        }
-}

+ 0 - 21
SimpleVoiceChat.BlazorWasm/wwwroot/svc-audio-playback-module.js

@@ -1,21 +0,0 @@
-var AudioContext = window.AudioContext || window.webkitAudioContext;
-
-//const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
-//var nameSpace = (await getAssemblyExports("SimpleVoiceChat.BlazorWasm.dll")).SimpleVoiceChat.BlazorWasm;
-//var DotNetInterop = nameSpace.SvcAudioPlaybackModule;
-
-var ctx;
-
-
-export async function play(chunk) {
-    if (!ctx) {
-        ctx = new AudioContext();
-    }
-
-    const source = ctx.createBufferSource();
-    source.buffer = await ctx.decodeAudioData(chunk.buffer);
-    source.connect(ctx.destination);
-    source.ended = () => { source.disconnect(ctx.destination); }
-    source.start(0);
-
-}

+ 0 - 18
SimpleVoiceChat.BlazorWasm/wwwroot/svc.css

@@ -1,18 +0,0 @@
-#blazor-error-ui {
-    background: lightyellow;
-    bottom: 0;
-    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
-    display: none;
-    left: 0;
-    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
-    position: fixed;
-    width: 100%;
-    z-index: 1080;
-}
-
-    #blazor-error-ui .dismiss {
-        cursor: pointer;
-        position: absolute;
-        right: 0.75rem;
-        top: 0.5rem;
-    }

+ 0 - 49
SimpleVoiceMeeting.BlazorWasm/Properties/launchSettings.json

@@ -1,49 +0,0 @@
-{
-  "profiles": {
-    "http": {
-      "commandName": "Project",
-      "launchBrowser": true,
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
-      },
-      "dotnetRunMessages": true,
-      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
-      "applicationUrl": "https://0.0.0.0:5201"
-    },
-    "HotLoad": {
-      "commandName": "Executable",
-      "executablePath": "dotnet",
-      "commandLineArgs": "watch",
-      "workingDirectory": ".",
-      "environmentVariables": {
-        "DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER": "1"
-      }
-    },
-    "WSL": {
-      "commandName": "WSL2",
-      "launchBrowser": true,
-      "launchUrl": "http://localhost:5201",
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development",
-        "ASPNETCORE_URLS": "http://localhost:5201"
-      },
-      "distributionName": ""
-    },
-    "IIS Express": {
-      "commandName": "IISExpress",
-      "launchBrowser": true,
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
-      },
-      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
-    }
-  },
-  "iisSettings": {
-    "windowsAuthentication": false,
-    "anonymousAuthentication": true,
-    "iisExpress": {
-      "applicationUrl": "http://localhost:63687",
-      "sslPort": 0
-    }
-  }
-}

+ 0 - 9
SimpleVoiceMeeting.BlazorWasm/README.md

@@ -1,9 +0,0 @@
-# SimpleVoiceChat.BlazorWasm 
-press <kbd>Ctrl</kbd>+<kbd>`~</kbd> in vs2022
-
-```powershell
-cd SimpleVoiceMeeting.BlazorWasm
-$env:DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER=1
-dotnet watch
-
-```

+ 0 - 21
SimpleVoiceMeeting.BlazorWasm/SimpleVoiceMeeting.BlazorWasm.csproj

@@ -1,21 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
-
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <allowUnsafeBlocks>true</allowUnsafeBlocks>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0" PrivateAssets="all" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Content Update="wwwroot\index.html">
-      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
-      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-    </Content>
-  </ItemGroup>
-
-</Project>

+ 0 - 46
SimpleVoiceMeeting.BlazorWasm/SvmAudioCaptureModule.cs

@@ -1,46 +0,0 @@
-using System.Runtime.InteropServices.JavaScript;
-using System.Runtime.Versioning;
-
-namespace SimpleVoiceMeeting.BlazorWasm
-{
-    [SupportedOSPlatform("browser")]
-    public static partial class SvmAudioCaptureModule
-    {
-        public static Action OnInit;
-        public static Action<byte[]> ChunkArrive;
-
-        private static JSObject _jsModule;
-
-        public static async Task<bool> Init()
-        {
-            _jsModule ??= await JSHost.ImportAsync("SvmAudioCaptureModule", $"{SvmGlobal.BasePath ?? "/"}svm-audio-capture-module.js");
-            return await JsInit();
-        }
-
-        public static async Task Start() => await JsStart();
-
-        public static async Task Stop() => await JsStop();
-
-        [JSImport("init", "SvmAudioCaptureModule")]
-        private static partial Task<bool> JsInit();
-
-        [JSImport("start", "SvmAudioCaptureModule")]
-        private static partial Task JsStart();
-
-        [JSImport("stop", "SvmAudioCaptureModule")]
-        private static partial Task JsStop();
-
-        [JSExport]
-        private static void JsFeedChunk(byte[] chunk)
-        {
-            Console.WriteLine("chunk");
-            if (ChunkArrive != null) ChunkArrive(chunk);
-        }
-
-        [JSExport]
-        private static void JsInitCallBack(bool value)
-        {
-            if (OnInit != null) OnInit();
-        }
-    }
-}

+ 0 - 19
SimpleVoiceMeeting.BlazorWasm/SvmAudioPlaybackModule.cs

@@ -1,19 +0,0 @@
-using System.Runtime.InteropServices.JavaScript;
-using System.Runtime.Versioning;
-
-namespace SimpleVoiceMeeting.BlazorWasm;
-
-[SupportedOSPlatform("browser")]
-public static partial class SvmAudioPlaybackModule
-{
-    private static JSObject _jsModule;
-
-    public static async Task PlayChunk(byte[] chunk)
-    {
-        _jsModule ??= await JSHost.ImportAsync("SvmAudioPlaybackModule", $"{SvmGlobal.BasePath ?? "/"}svm-audio-playback-module.js");
-        await JsPlay(chunk);
-    }
-
-    [JSImport("play", "SvmAudioPlaybackModule")]
-    private static partial Task JsPlay(byte[] chunk);
-}

+ 0 - 7
SimpleVoiceMeeting.BlazorWasm/SvmGlobal.cs

@@ -1,7 +0,0 @@
-namespace SimpleVoiceMeeting.BlazorWasm
-{
-    public static class SvmGlobal
-    {
-        public static string BasePath { get; set; }
-    }
-}

+ 0 - 26
SimpleVoiceMeeting.BlazorWasm/SvmProgram.cs

@@ -1,26 +0,0 @@
-using Microsoft.AspNetCore.Components.Web;
-using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
-using SimpleVoiceMeeting.BlazorWasm;
-
-var builder = WebAssemblyHostBuilder.CreateDefault(args);
-builder.RootComponents.Add<App>("#app");
-builder.RootComponents.Add<HeadOutlet>("head::after");
-
-builder.Services.AddScoped(sp =>
-{
-    var http = new HttpClient();
-
-#if DEBUG
-    http.BaseAddress = new Uri("https://svc-local-dev.topcl.net");
-#else
-    var NavManager = sp.GetService<Microsoft.AspNetCore.Components.NavigationManager>();
-    var ba = new Uri($"{NavManager.ToAbsoluteUri("/").GetLeftPart(UriPartial.Scheme | UriPartial.Authority)}");
-    http.BaseAddress = ba;
-    SvmGlobal.BasePath = NavManager.BaseUri;
-#endif
-
-    return http;
-});
-
-await builder.Build().RunAsync();
- 

+ 0 - 9
SimpleVoiceMeeting.BlazorWasm/_Imports.razor

@@ -1,9 +0,0 @@
-@using System.Net.Http
-@using System.Net.Http.Json
-@using Microsoft.AspNetCore.Components.Forms
-@using Microsoft.AspNetCore.Components.Routing
-@using Microsoft.AspNetCore.Components.Web
-@using Microsoft.AspNetCore.Components.Web.Virtualization
-@using Microsoft.AspNetCore.Components.WebAssembly.Http
-@using Microsoft.JSInterop
-@using SimpleVoiceMeeting.BlazorWasm

BIN
SimpleVoiceMeeting.BlazorWasm/wwwroot/favicon.png


BIN
SimpleVoiceMeeting.BlazorWasm/wwwroot/icon-192.png


+ 0 - 56
SimpleVoiceMeeting.BlazorWasm/wwwroot/index.html

@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <link rel="icon" type="image/x-icon" href="favicon.ico">
-    <title>Simple Web Chat</title>
-
-    <base href="/" />
-
-    <link href="lib/bootstrap/default/bootstrap.min.css" rel="stylesheet" />
-
-    <link href="svm.css" rel="stylesheet" />
-</head>
-
-<body>
-    <div id="app">
-        <div class="loader text-center" id="loading">
-            <link href="loading.css" rel="stylesheet" />
-            <div class="logo">
-                <div class="one common"></div>
-                <div class="two common"></div>
-                <div class="three common"></div>
-                <div class="four common"></div>
-                <div class="five common"></div>
-                <div class="six common"></div>
-                <div class="seven common"></div>
-                <div class="eight common"></div>
-            </div>
-            <div class="intro">
-                <img src="icon-192.png" />
-                <span>精彩即将呈现</span>
-            </div>
-            <div class="bar">
-                <div class="progress"></div>
-            </div>
-        </div>
-    </div>
-
-    <div id="blazor-error-ui">
-        <environment include="Staging,Production">
-            An error has occurred. This application may no longer respond until reloaded.
-        </environment>
-        <environment include="Development">
-            An unhandled exception has occurred. See browser dev tools for details.
-        </environment>
-        <a href="" class="reload">Reload</a>
-        <a class="dismiss"><i class="fa fa-times"></i></a>
-    </div>
-
-    <script src="_framework/blazor.webassembly.js"></script>
-    <script src="lib/bootstrap/bootstrap.bundle.min.js"></script>
-
-</body>
-</html>

File diff suppressed because it is too large
+ 0 - 7
SimpleVoiceMeeting.BlazorWasm/wwwroot/lib/bootstrap/bootstrap.bundle.min.js


File diff suppressed because it is too large
+ 0 - 7
SimpleVoiceMeeting.BlazorWasm/wwwroot/lib/bootstrap/default/bootstrap.min.css


+ 0 - 397
SimpleVoiceMeeting.BlazorWasm/wwwroot/loading.css

@@ -1,397 +0,0 @@
-.loader {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background-color: #161B29;
-    transition: opacity .5s linear;
-    z-index: 2050;
-    display: flex;
-    flex-flow: column;
-}
-
-    .loader.is-done {
-        opacity: 0;
-    }
-
-    .loader .logo {
-        width: 200px;
-        height: 200px;
-        position: relative;
-        margin: 0 auto;
-    }
-
-@keyframes logo {
-    0%, 100% {
-        box-shadow: 1px 1px 25px 10px rgba(146, 148, 248, 0.4);
-    }
-
-    50% {
-        box-shadow: none;
-    }
-}
-
-.loader .intro {
-    width: 250px;
-    color: #fff;
-    font-size: 1.5rem;
-    text-align: center;
-    margin: 3rem auto;
-    display: inline-flex;
-    justify-content: center;
-    align-items: center;
-    padding: 0.5rem 1rem;
-    position: relative;
-    overflow: hidden;
-    border: 1px solid rgb(146, 148, 248);
-    animation: intro 3s linear infinite
-}
-
-@keyframes intro {
-    0%, 100% {
-        box-shadow: 0px 0px 25px 10px rgba(146, 148, 248, 0.4);
-    }
-
-    40%, 60% {
-        box-shadow: 0px 0px 25px 0px rgba(146, 148, 248, 0.4);
-    }
-}
-
-.loader .intro:before {
-    content: "";
-    position: absolute;
-    top: 0;
-    left: -100%;
-    width: 100%;
-    height: 100%;
-    background: linear-gradient( 120deg, transparent, rgba(146, 148, 248, 0.4), transparent );
-    animation: flash 2.5s linear infinite;
-}
-
-@keyframes flash {
-    0%, 100% {
-    }
-
-    10%, 90% {
-        left: 100%;
-    }
-}
-
-.loader .intro img {
-    border-radius: 3px;
-    width: 40px;
-    margin-right: 1rem;
-}
-
-.loader .intro span {
-    color: #fff;
-    animation: title 3s linear infinite;
-}
-
-@keyframes title {
-    0%, 100% {
-        color: #fff;
-    }
-
-    60% {
-        color: #666;
-    }
-}
-
-.loader .bar {
-    width: 50%;
-    height: 4px;
-    border-radius: 2px;
-    margin: auto;
-    background: #E645D0;
-}
-
-    .loader .bar .progress {
-        width: 0%;
-        height: 4px;
-        margin: auto;
-        background: #17E1E6;
-    }
-
-.loader .logo {
-    animation: logo 5s linear infinite;
-    -moz-animation: logo 5s linear infinite;
-    /* Firefox */
-    -webkit-animation: logo 5s linear infinite;
-    /* Safari and Chrome */
-    -o-animation: logo 5s linear infinite;
-    /* Opera */
-}
-
-@keyframes logo {
-    from {
-        transform: rotate(0deg);
-    }
-
-    to {
-        transform: rotate(-360deg);
-    }
-}
-
-.loader .progress {
-    animation: progress 12s linear;
-    -moz-animation: progress 12s linear;
-    /* Firefox */
-    -webkit-animation: progress 12s linear;
-    /* Safari and Chrome */
-    -o-animation: progress 12s linear;
-    /* Opera */
-    animation: progress 12s linear infinite;
-}
-
-@keyframes progress {
-    0%, 100% {
-        width: 0%;
-        background-color: #17e1e6;
-    }
-
-    50% {
-        width: 100%;
-        background-color: #28a745;
-    }
-}
-
-.loader .common {
-    height: 5vw;
-    max-height: 100%;
-    overflow: auto;
-    width: 2vw;
-    margin: auto;
-    max-width: 100%;
-    position: absolute;
-    border-radius: 0vw 10vw 0vw 10vw;
-    box-shadow: inset 0vw 0vw 0vw .1vw #E645D0, 0vw 0vw 1.5vw 0vw #E645D0;
-}
-
-.loader .one {
-    transform: rotate(45deg);
-    left: 0;
-    right: 0;
-    top: 0;
-    bottom: 7.5vw;
-}
-
-.loader .two {
-    transform: rotate(90deg);
-    left: 5.5vw;
-    right: 0;
-    top: 0;
-    bottom: 5.5vw;
-}
-
-.loader .three {
-    transform: rotate(135deg);
-    left: 7.5vw;
-    right: 0;
-    top: 0;
-    bottom: 0;
-}
-
-.loader .four {
-    transform: rotate(180deg);
-    left: 5.5vw;
-    right: 0;
-    top: 5.5vw;
-    bottom: 0;
-}
-
-.loader .five {
-    transform: rotate(225deg);
-    left: 0;
-    right: 0;
-    top: 7.5vw;
-    bottom: 0;
-}
-
-.loader .six {
-    transform: rotate(270deg);
-    left: 0;
-    right: 5.5vw;
-    top: 5.5vw;
-    bottom: 0;
-}
-
-.loader .seven {
-    transform: rotate(315deg);
-    left: 0;
-    right: 7.5vw;
-    top: 0;
-    bottom: 0;
-}
-
-.loader .eight {
-    transform: rotate(360deg);
-    left: 0;
-    right: 5.5vw;
-    top: 0;
-    bottom: 5.5vw;
-}
-
-.loader .one {
-    animation: one 1s ease infinite;
-    -moz-animation: one 1s ease infinite;
-    /* Firefox */
-    -webkit-animation: one 1s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: one 1s ease infinite;
-    /* Opera */
-}
-
-@keyframes one {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .two {
-    animation: two 1s .125s ease infinite;
-    -moz-animation: two 1s .125s ease infinite;
-    /* Firefox */
-    -webkit-animation: two 1s .125s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: two 1s .125s ease infinite;
-    /* Opera */
-}
-
-@keyframes two {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .three {
-    animation: three 1s .25s ease infinite;
-    -moz-animation: three 1s .25s ease infinite;
-    /* Firefox */
-    -webkit-animation: three 1s .25s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: three 1s .25s ease infinite;
-    /* Opera */
-}
-
-@keyframes three {
-    0%, 100% {
-    }
-
-    50% {
-        background:;
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .four {
-    animation: four 1s .375s ease infinite;
-    -moz-animation: four 1s .375s ease infinite;
-    /* Firefox */
-    -webkit-animation: four 1s .375s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: four 1s .375s ease infinite;
-    /* Opera */
-}
-
-@keyframes four {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .five {
-    animation: five 1s .5s ease infinite;
-    -moz-animation: five 1s .5s ease infinite;
-    /* Firefox */
-    -webkit-animation: five 1s .5s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: five 1s .5s ease infinite;
-    /* Opera */
-}
-
-@keyframes five {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .six {
-    animation: six 1s .625s ease infinite;
-    -moz-animation: six 1s .625s ease infinite;
-    /* Firefox */
-    -webkit-animation: six 1s .625s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: six 1s .625s ease infinite;
-    /* Opera */
-}
-
-@keyframes six {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .seven {
-    animation: seven 1s .750s ease infinite;
-    -moz-animation: seven 1s .750s ease infinite;
-    /* Firefox */
-    -webkit-animation: seven 1s .750s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: seven 1s .750s ease infinite;
-    /* Opera */
-}
-
-@keyframes seven {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-.loader .eight {
-    animation: eight 1s .875s ease infinite;
-    -moz-animation: eight 1s .875s ease infinite;
-    /* Firefox */
-    -webkit-animation: eight 1s .875s ease infinite;
-    /* Safari and Chrome */
-    -o-animation: eight 1s .875s ease infinite;
-    /* Opera */
-}
-
-@keyframes eight {
-    0%, 100% {
-    }
-
-    50% {
-        box-shadow: inset 0vw 0vw 0vw .1vw #17E1E6, 0vw 0vw 1.5vw 0vw #17E1E6;
-    }
-}
-
-@media (min-width: 768px) {
-    .loader {
-        padding-top: 8rem;
-    }
-
-        .loader .intro {
-            margin-top: 6rem;
-        }
-}

+ 0 - 49
SimpleVoiceMeeting.BlazorWasm/wwwroot/svm-audio-capture-module.js

@@ -1,49 +0,0 @@
-const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
-var nameSpace = (await getAssemblyExports("SimpleVoiceMeeting.BlazorWasm.dll")).SimpleVoiceMeeting.BlazorWasm;
-var DotNetInterop = nameSpace.SvmAudioCaptureModule;
-
-var isRunning = false;
-var stream;
-var recorder;
-
-export async function init() {
-    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
-        stream = await navigator.mediaDevices.getUserMedia({ audio: true });
-    else throw "failure to getUserMedia";
-
-    if (stream) {
-        recorder = new MediaRecorder(stream, {
-            audioBitsPerSecond: 64000,
-            mimeType: 'audio/webm;codecs=OPUS'
-        });
-
-        recorder.ondataavailable = async (r) => {
-            DotNetInterop.JsFeedChunk(new Uint8Array(await r.data.arrayBuffer()));
-        };
-
-        recorder.onstop = () => {
-            if (isRunning) {
-                recorder.start();
-                setTimeout(() => {
-                    if (isRunning) recorder.stop();
-                }, 1000);
-            }
-        };
-    }
-    return !!stream;
-}
-
-export async function start() {
-    isRunning = true;
-    if (recorder.state != 'recording') {
-        recorder.start();
-        setTimeout(() => {
-            if (isRunning) recorder.stop();
-        }, 1000);
-    }
-}
-
-export async function stop() {
-    isRunning = false;
-    recorder.stop();
-}

+ 0 - 18
SimpleVoiceMeeting.BlazorWasm/wwwroot/svm.css

@@ -1,18 +0,0 @@
-#blazor-error-ui {
-    background: lightyellow;
-    bottom: 0;
-    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
-    display: none;
-    left: 0;
-    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
-    position: fixed;
-    width: 100%;
-    z-index: 1080;
-}
-
-    #blazor-error-ui .dismiss {
-        cursor: pointer;
-        position: absolute;
-        right: 0.75rem;
-        top: 0.5rem;
-    }

+ 24 - 120
SimpleWebChat.BlazorWasm/App.razor

@@ -1,125 +1,29 @@
-@inject HttpClient http
-@code {
-    private string nickName;
-    private string nickNameClass => string.IsNullOrWhiteSpace(nickName) ? "is-invalid form-control" : "form-control";
-
-    private string message;
-    private string messageClass => string.IsNullOrWhiteSpace(message) ? "is-invalid form-control" : "form-control";
-
-    private int connState = 0;
-    private const int maxDisplayMessage = 1000;
-    private List<string> displayingMessages = new List<string>(maxDisplayMessage);
+@code {
+    public int Type { get; set; }
 }
 
-<div class="container">
-    <h2>Simple Web Chat</h2>
 
-    @if (connState == 0)
-    {
-        <div class="input-group mb-3">
-            <div class="form-floating">
-                <InputText class="@nickNameClass" placeholder="Nick name" type="search" onsearch="@Connect" @oninput="NickNameChanged" ValueExpression="()=>nickName"></InputText>
-                @if (string.IsNullOrEmpty(nickName))
-                {
-                    <label>Please enter nick name</label>
-                }
-                else
-                {
-                    <label>Nick name</label>
-                }
+@switch (Type)
+{
+    default:
+        <div class="container">
+            <h2>Simple Web Chat</h2>
+            <div class="row m-2">
+                <div class="col-12">
+                    <button @onclick="@(()=>Type=1)" class="btn btn-primary">Text chat</button>
+                    <button @onclick="@(()=>Type=2)" class="btn btn-primary">Voice chat</button>
+                    <button @onclick="@(()=>Type=3)" class="btn btn-primary">Voice meeting</button>
+                </div>
             </div>
-            <button @onclick="@Connect" class="btn btn-outline-primary" type="button">Connect</button>
         </div>
-    }
-    else if (connState == 1)
-    {
-        <span>Conecting...</span>
-    }
-    else if (connState == 2)
-    {
-        <div class="input-group mb-3">
-            <div class="form-floating">
-                <InputText class="@messageClass" placeholder="Message to send" type="search" onsearch="@Send" @oninput="MessageChanged" Value="@message" ValueExpression="()=>message"></InputText>
-                @if (string.IsNullOrEmpty(message))
-                {
-                    <label>Please enter message</label>
-                }
-                else
-                {
-                    <label>Message to send</label>
-                }
-            </div>
-            <button @onclick="@Send" class="btn btn-outline-secondary" type="button">Send</button>
-        </div>
-
-        @foreach (var item in displayingMessages)
-        {
-            <pre class="font-monospace" style="white-space:pre-wrap">@item</pre>
-        }
-    }
-    else
-    {
-        <span>Error</span>
-    }
-</div>
-
-@code {
-    private System.Net.WebSockets.ClientWebSocket sck;
-
-    private void NickNameChanged(ChangeEventArgs e)
-    {
-        nickName = e.Value.ToString().Trim();
-        StateHasChanged();
-    }
-
-    private void MessageChanged(ChangeEventArgs e)
-    {
-        message = e.Value.ToString().Trim();
-        StateHasChanged();
-    }
-
-    private async Task Connect()
-    {
-        if (string.IsNullOrEmpty(nickName)) return;
-
-        connState = 1;
-        StateHasChanged();
-
-        try
-        {
-            sck = new System.Net.WebSockets.ClientWebSocket();
-            sck.Options.AddSubProtocol("swcp");
-
-            var b = http.BaseAddress;
-
-            await sck.ConnectAsync(new Uri($"{(b.Scheme == "https" ? "wss" : "ws")}://{b.Host}:{b.Port}/connect"), default);
-
-            connState = 2;
-            StateHasChanged();
-
-            var buf = System.Text.Encoding.UTF8.GetBytes(nickName);
-            await sck.SendAsync(buf, System.Net.WebSockets.WebSocketMessageType.Text, true, default);
-            while (true)
-            {
-                buf = new Byte[1024];
-                var r = await sck.ReceiveAsync(buf, default);
-                displayingMessages.Insert(0, System.Text.Encoding.UTF8.GetString(buf, 0, r.Count));
-                while (displayingMessages.Count >= maxDisplayMessage) displayingMessages.RemoveAt(maxDisplayMessage - 1);
-                StateHasChanged();
-            }
-        }
-        catch (Exception)
-        {
-            connState = -1;
-        }
-    }
-
-    private async Task Send()
-    {
-        if (string.IsNullOrEmpty(message)) return;
-        var msg = $"{nickName}: {message}";
-        await sck.SendAsync(System.Text.Encoding.UTF8.GetBytes(msg), System.Net.WebSockets.WebSocketMessageType.Text, true, default);
-        message = "";
-        StateHasChanged();
-    }
-}
+        break;
+    case 1:
+        <TextChat></TextChat>
+        break;
+    case 2:
+        <VoiceChat></VoiceChat>
+        break;
+    case 3:
+        <VoiceMeeting></VoiceMeeting>
+        break;
+}

+ 6 - 6
SimpleVoiceChat.BlazorWasm/SvcAudioCaptureModule.cs

@@ -1,10 +1,10 @@
 using System.Runtime.InteropServices.JavaScript;
 using System.Runtime.Versioning;
 
-namespace SimpleVoiceChat.BlazorWasm
+namespace SimpleWebChat.BlazorWasm.Modules
 {
     [SupportedOSPlatform("browser")]
-    public static partial class SvcAudioCaptureModule
+    public static partial class AudioCaptureModule
     {
         public static Action OnInit;
         public static Action<byte[]> ChunkArrive;
@@ -13,7 +13,7 @@ namespace SimpleVoiceChat.BlazorWasm
 
         public static async Task<bool> Init()
         {
-            _jsModule ??= await JSHost.ImportAsync("SvcAudioCaptureModule", $"{SvcGlobal.BasePath ?? "/"}svc-audio-capture-module.js");
+            _jsModule ??= await JSHost.ImportAsync("AudioCaptureModule", $"{GlobalModule.BasePath ?? "/"}audio-capture-module.js");
             return await JsInit();
         }
 
@@ -21,13 +21,13 @@ namespace SimpleVoiceChat.BlazorWasm
 
         public static async Task Stop() => await JsStop();
 
-        [JSImport("init", "SvcAudioCaptureModule")]
+        [JSImport("init", "AudioCaptureModule")]
         private static partial Task<bool> JsInit();
 
-        [JSImport("start", "SvcAudioCaptureModule")]
+        [JSImport("start", "AudioCaptureModule")]
         private static partial Task JsStart();
 
-        [JSImport("stop", "SvcAudioCaptureModule")]
+        [JSImport("stop", "AudioCaptureModule")]
         private static partial Task JsStop();
 
         [JSExport]

+ 3 - 3
SimpleVoiceChat.BlazorWasm/SvcAudioPlaybackModule.cs

@@ -1,16 +1,16 @@
 using System.Runtime.InteropServices.JavaScript;
 using System.Runtime.Versioning;
 
-namespace SimpleVoiceChat.BlazorWasm;
+namespace SimpleWebChat.BlazorWasm.Modules;
 
 [SupportedOSPlatform("browser")]
-public static partial class SvcAudioPlaybackModule
+public static partial class AudioPlaybackModule
 {
     private static JSObject _jsModule;
 
     public static async Task PlayChunk(byte[] chunk)
     {
-        _jsModule ??= await JSHost.ImportAsync("SvcAudioPlaybackModule", $"{SvcGlobal.BasePath ?? "/"}svc-audio-playback-module.js");
+        _jsModule ??= await JSHost.ImportAsync("SvcAudioPlaybackModule", $"{GlobalModule.BasePath ?? "/"}svc-audio-playback-module.js");
         await JsPlay(chunk);
     }
 

+ 7 - 0
SimpleWebChat.BlazorWasm/Modules/GlobalModule.cs

@@ -0,0 +1,7 @@
+namespace SimpleWebChat.BlazorWasm.Modules
+{
+    public static class GlobalModule
+    {
+        public static string BasePath { get; set; }
+    }
+}

+ 1 - 1
SimpleVoiceChat.BlazorWasm/README.md

@@ -2,7 +2,7 @@
 press <kbd>Ctrl</kbd>+<kbd>`~</kbd> in vs2022
 
 ```powershell
-cd SimpleVoiceChat.BlazorWasm
+cd SimpleWebChat.BlazorWasm
 $env:DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER=1
 dotnet watch
 

+ 2 - 2
SimpleWebChat.BlazorWasm/SimpleWebChat.BlazorWasm.csproj

@@ -1,9 +1,9 @@
-<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
 
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
-    <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
+    <allowUnsafeBlocks>true</allowUnsafeBlocks>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 1
SimpleWebChat.BlazorWasm/SwcProgram.cs

@@ -11,7 +11,7 @@ builder.Services.AddScoped(sp =>
     var http = new HttpClient();
 
 #if DEBUG
-    http.BaseAddress = new Uri("http://swc-server");
+    http.BaseAddress = new Uri("https://swc-local-dev.topcl.net");
 #else
     var NavManager = sp.GetService<Microsoft.AspNetCore.Components.NavigationManager>();
     var ba = new Uri($"{NavManager.ToAbsoluteUri("/").GetLeftPart(UriPartial.Scheme | UriPartial.Authority)}");

+ 125 - 0
SimpleWebChat.BlazorWasm/UI/TextChat.razor

@@ -0,0 +1,125 @@
+@inject HttpClient http
+@code {
+    private string nickName;
+    private string nickNameClass => string.IsNullOrWhiteSpace(nickName) ? "is-invalid form-control" : "form-control";
+
+    private string message;
+    private string messageClass => string.IsNullOrWhiteSpace(message) ? "is-invalid form-control" : "form-control";
+
+    private int connState = 0;
+    private const int maxDisplayMessage = 1000;
+    private List<string> displayingMessages = new List<string>(maxDisplayMessage);
+}
+
+<div class="container">
+    <h2>Simple Web Chat(Text)</h2>
+
+    @if (connState == 0)
+    {
+        <div class="input-group mb-3">
+            <div class="form-floating">
+                <InputText class="@nickNameClass" placeholder="Nick name" type="search" onsearch="@Connect" @oninput="NickNameChanged" ValueExpression="()=>nickName"></InputText>
+                @if (string.IsNullOrEmpty(nickName))
+                {
+                    <label>Please enter nick name</label>
+                }
+                else
+                {
+                    <label>Nick name</label>
+                }
+            </div>
+            <button @onclick="@Connect" class="btn btn-outline-primary" type="button">Connect</button>
+        </div>
+    }
+    else if (connState == 1)
+    {
+        <span>Connecting...</span>
+    }
+    else if (connState == 2)
+    {
+        <div class="input-group mb-3">
+            <div class="form-floating">
+                <InputText class="@messageClass" placeholder="Message to send" type="search" onsearch="@Send" @oninput="MessageChanged" Value="@message" ValueExpression="()=>message"></InputText>
+                @if (string.IsNullOrEmpty(message))
+                {
+                    <label>Please enter message</label>
+                }
+                else
+                {
+                    <label>Message to send</label>
+                }
+            </div>
+            <button @onclick="@Send" class="btn btn-outline-secondary" type="button">Send</button>
+        </div>
+
+        @foreach (var item in displayingMessages)
+        {
+            <pre class="font-monospace" style="white-space:pre-wrap">@item</pre>
+        }
+    }
+    else
+    {
+        <span>Error</span>
+    }
+</div>
+
+@code {
+    private System.Net.WebSockets.ClientWebSocket sck;
+
+    private void NickNameChanged(ChangeEventArgs e)
+    {
+        nickName = e.Value.ToString().Trim();
+        StateHasChanged();
+    }
+
+    private void MessageChanged(ChangeEventArgs e)
+    {
+        message = e.Value.ToString().Trim();
+        StateHasChanged();
+    }
+
+    private async Task Connect()
+    {
+        if (string.IsNullOrEmpty(nickName)) return;
+
+        connState = 1;
+        StateHasChanged();
+
+        try
+        {
+            sck = new System.Net.WebSockets.ClientWebSocket();
+            sck.Options.AddSubProtocol("swcp");
+
+            var b = http.BaseAddress;
+
+            await sck.ConnectAsync(new Uri($"{(b.Scheme == "https" ? "wss" : "ws")}://{b.Host}:{b.Port}/connect"), default);
+
+            connState = 2;
+            StateHasChanged();
+
+            var buf = System.Text.Encoding.UTF8.GetBytes(nickName);
+            await sck.SendAsync(buf, System.Net.WebSockets.WebSocketMessageType.Text, true, default);
+            while (true)
+            {
+                buf = new Byte[1024];
+                var r = await sck.ReceiveAsync(buf, default);
+                displayingMessages.Insert(0, System.Text.Encoding.UTF8.GetString(buf, 0, r.Count));
+                while (displayingMessages.Count >= maxDisplayMessage) displayingMessages.RemoveAt(maxDisplayMessage - 1);
+                StateHasChanged();
+            }
+        }
+        catch (Exception)
+        {
+            connState = -1;
+        }
+    }
+
+    private async Task Send()
+    {
+        if (string.IsNullOrEmpty(message)) return;
+        var msg = $"{nickName}: {message}";
+        await sck.SendAsync(System.Text.Encoding.UTF8.GetBytes(msg), System.Net.WebSockets.WebSocketMessageType.Text, true, default);
+        message = "";
+        StateHasChanged();
+    }
+}

+ 164 - 0
SimpleWebChat.BlazorWasm/UI/VoiceChat.razor

@@ -0,0 +1,164 @@
+@using System.Text
+@using System.Net.WebSockets
+@inject HttpClient http
+@code {
+    private bool ready = false;
+
+    private string num = "";
+    private List<string> logs = new();
+    private ClientWebSocket sck;
+}
+
+
+<div class="container">
+    <h2>Simple Web Chat(Voice)</h2>
+
+    @if (!ready)
+    {
+        <div class="row m-2">
+            <div class="col-12">
+                <button @onclick="@Init" class="btn btn-primary">Open Microphone</button>
+            </div>
+        </div>
+    }
+    else
+    {
+        <div class="row">
+            <div class="col-6">
+
+                <div class="row mt-1">
+                    <div class="col-3">
+                        <button class="btn btn-secondary" @onclick="@(()=>num="")">X</button>
+                    </div>
+                    <div class="col-6">
+                        <InputText ValueExpression="()=>num" ValueChanged="(v) =>num=v" Value="@num" class="w-100"></InputText>
+                    </div>
+                    <div class="col-3">
+                        <button class="btn btn-danger" @onclick="@Go">→</button>
+                    </div>
+                </div>
+
+                <div class="row mt-1">
+                    <div class="col-3"></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "1"; StateHasChanged(); })">1</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "2"; StateHasChanged(); })">2</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "3"; StateHasChanged(); })">3</button></div>
+                </div>
+                <div class="row mt-1">
+                    <div class="col-3"></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "4"; StateHasChanged(); })">4</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "5"; StateHasChanged(); })">5</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "6"; StateHasChanged(); })">6</button></div>
+                </div>
+                <div class="row mt-1">
+                    <div class="col-3"></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "7"; StateHasChanged(); })">7</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "8"; StateHasChanged(); })">8</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "9"; StateHasChanged(); })">9</button></div>
+                </div>
+                <div class="row mt-1">
+                    <div class="col-3"></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "*"; StateHasChanged(); })">*</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "0"; StateHasChanged(); })">0</button></div>
+                    <div class="col-3"><button class="btn btn-primary" @onclick="@(()=> { num += "#"; StateHasChanged(); })">#</button></div>
+                </div>
+            </div>
+            <div class="col-6">
+                @foreach (var s in logs)
+                {
+                    <div>
+                        <pre>@s</pre>
+                    </div>
+                }
+            </div>
+        </div>
+    }
+
+    @code {
+        private WebSocket socket;
+
+        private async Task Init()
+        {
+            ready = await AudioCaptureModule.Init();
+            if (ready)
+            {
+                AudioCaptureModule.ChunkArrive += SendChunk;
+            }
+        }
+
+        private void SendChunk(byte[] obj)
+        {
+            socket?.SendAsync(obj, WebSocketMessageType.Binary, true, default);
+        }
+
+        private async Task Go()
+        {
+            if (socket != null)
+            {
+                try
+                {
+                    await socket.CloseAsync(WebSocketCloseStatus.Empty, "Brrrrrr", default);
+                }
+                catch
+                {
+                    //FUCK ERR
+                }
+
+                try
+                {
+                    socket.Dispose();
+                }
+                catch
+                {
+                    //FUCK ERR
+                }
+
+                logs.Insert(0, "Disconnected");
+                StateHasChanged();
+                socket = null;
+            }
+
+            sck = new ClientWebSocket();
+            sck.Options.AddSubProtocol("svcp");
+
+            var b = http.BaseAddress;
+            try
+            {
+                await sck.ConnectAsync(new Uri($"{(b.Scheme == "https" ? "wss" : "ws")}://{b.Host}:{b.Port}/connect/voice"), default);
+                await sck.SendAsync(Encoding.UTF8.GetBytes(num), WebSocketMessageType.Text, true, default);
+                logs.Insert(0, "Connected");
+                socket = sck;
+                await AudioCaptureModule.Start();
+                while (true)
+                {
+                    var buf = new Byte[15384];
+                    var r = await sck.ReceiveAsync(buf, default);
+                    if (r.Count == 0)
+                    {
+                        logs.Insert(0, "Disconnected");
+                        StateHasChanged();
+                        break;
+                    }
+                    if (r.MessageType == WebSocketMessageType.Text)
+                    {
+                        logs.Insert(0, Encoding.UTF8.GetString(buf, 0, r.Count));
+                        StateHasChanged();
+                    }
+                    else
+                    {
+                        var chunk = new byte[r.Count];
+                        Array.Copy(buf, 0, chunk, 0, chunk.Length);
+                        await AudioPlaybackModule.PlayChunk(chunk);
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                logs.Insert(0, e.ToString());
+                StateHasChanged();
+            }
+
+        }
+    }
+
+</div>

+ 27 - 23
SimpleVoiceMeeting.BlazorWasm/App.razor

@@ -8,38 +8,42 @@
     private ClientWebSocket sck;
 }
 
-@if (!ready)
-{
+<div class="container">
+    <h2>Simple Web Chat(Voice meeting)</h2>
 
-    <div class="row m-2">
-        <div class="col-12">
-            <button @onclick="@Init" class="btn btn-primary">Open Microphone and Connect</button>
+    @if (!ready)
+    {
+
+        <div class="row m-2">
+            <div class="col-12">
+                <button @onclick="@Init" class="btn btn-primary">Open Microphone and Connect</button>
+            </div>
         </div>
-    </div>
-}
-else
-{
-    <div class="row">
-        <div class="col-12">
-            @foreach (var s in logs)
-            {
-                <div>
-                    <pre>@s</pre>
-                </div>
-            }
+    }
+    else
+    {
+        <div class="row">
+            <div class="col-12">
+                @foreach (var s in logs)
+                {
+                    <div>
+                        <pre>@s</pre>
+                    </div>
+                }
+            </div>
         </div>
-    </div>
-}
+    }
+</div>
 
 @code {
     private WebSocket socket;
 
     private async Task Init()
     {
-        ready = await SvmAudioCaptureModule.Init();
+        ready = await AudioCaptureModule.Init();
         if (ready)
         {
-            SvmAudioCaptureModule.ChunkArrive += SendChunk;
+            AudioCaptureModule.ChunkArrive += SendChunk;
             await Go();
         }
     }
@@ -87,7 +91,7 @@ else
             StateHasChanged();
 
             socket = sck;
-            await SvmAudioCaptureModule.Start();
+            await AudioCaptureModule.Start();
             while (true)
             {
                 var buf = new Byte[15384];
@@ -107,7 +111,7 @@ else
                 {
                     var chunk = new byte[r.Count];
                     Array.Copy(buf, 0, chunk, 0, chunk.Length);
-                    await SvmAudioPlaybackModule.PlayChunk(chunk);
+                    await AudioPlaybackModule.PlayChunk(chunk);
                 }
             }
         }

+ 2 - 0
SimpleWebChat.BlazorWasm/_Imports.razor

@@ -7,3 +7,5 @@
 @using Microsoft.AspNetCore.Components.WebAssembly.Http
 @using Microsoft.JSInterop
 @using SimpleWebChat.BlazorWasm
+@using SimpleWebChat.BlazorWasm.UI
+@using SimpleWebChat.BlazorWasm.Modules

+ 2 - 2
SimpleVoiceChat.BlazorWasm/wwwroot/svc-audio-capture-module.js

@@ -1,6 +1,6 @@
 const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
-var nameSpace = (await getAssemblyExports("SimpleVoiceChat.BlazorWasm.dll")).SimpleVoiceChat.BlazorWasm;
-var DotNetInterop = nameSpace.SvcAudioCaptureModule;
+var nameSpace = (await getAssemblyExports("SimpleWebChat.BlazorWasm.dll")).SimpleWebChat.BlazorWasm.Modules;
+var DotNetInterop = nameSpace.AudioCaptureModule;
 
 var isRunning = false;
 var stream;

SimpleVoiceMeeting.BlazorWasm/wwwroot/svm-audio-playback-module.js → SimpleWebChat.BlazorWasm/wwwroot/audio-playback-module.js


+ 0 - 12
SimpleWebChat.sln

@@ -7,10 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleWebChat.ConHost", "Si
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleWebChat.BlazorWasm", "SimpleWebChat.BlazorWasm\SimpleWebChat.BlazorWasm.csproj", "{EEBDF24C-F814-44B2-A20C-812737AA313B}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleVoiceChat.BlazorWasm", "SimpleVoiceChat.BlazorWasm\SimpleVoiceChat.BlazorWasm.csproj", "{012A093B-33A9-405E-979E-321C0E83336D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleVoiceMeeting.BlazorWasm", "SimpleVoiceMeeting.BlazorWasm\SimpleVoiceMeeting.BlazorWasm.csproj", "{F44631F3-8189-4616-B82C-C17D208D87DD}"
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -25,14 +21,6 @@ Global
 		{EEBDF24C-F814-44B2-A20C-812737AA313B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{EEBDF24C-F814-44B2-A20C-812737AA313B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{EEBDF24C-F814-44B2-A20C-812737AA313B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{012A093B-33A9-405E-979E-321C0E83336D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{012A093B-33A9-405E-979E-321C0E83336D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{012A093B-33A9-405E-979E-321C0E83336D}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{012A093B-33A9-405E-979E-321C0E83336D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F44631F3-8189-4616-B82C-C17D208D87DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F44631F3-8189-4616-B82C-C17D208D87DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F44631F3-8189-4616-B82C-C17D208D87DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F44631F3-8189-4616-B82C-C17D208D87DD}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE