Skip to content

Implement server-side room management, real-time waveform visualization, Docker deployment, and audio recording#4

Merged
gmij merged 7 commits into
mainfrom
copilot/start-server-version-development
Nov 23, 2025
Merged

Implement server-side room management, real-time waveform visualization, Docker deployment, and audio recording#4
gmij merged 7 commits into
mainfrom
copilot/start-server-version-development

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 23, 2025

The web application previously ran room management entirely in browser memory. This adds server-side integration, real-time audio waveform visualization during calls, Docker containerization, and audio recording/download capabilities.

Server-Side Room Management

  • IApiService interface: Unified contract for room management operations (CRUD, participants, stats)
  • RealApiService: Connects to WebAPI backend via HttpClient with proper error handling
  • MockApiService: Updated to implement interface, retained for GitHub Pages deployment
  • Configuration-based switching: appsettings.json controls UseMockApi flag to toggle between implementations

All Blazor pages (Home, Rooms, CreateRoom, RoomDetail, Call) now inject IApiService instead of direct MockApiService dependency.

{
  "ApiBaseUrl": "https://localhost:7063",
  "UseMockApi": false  // false = server-side, true = in-memory
}

Real-Time Waveform Visualization

  • WaveformVisualizer.razor: Reusable component for rendering audio waveforms via Canvas API
  • Audio data collection: audioCall.js uses ScriptProcessor to capture 2048-sample frames, downsampled to 200 points
  • Dual waveforms in Call UI:
    • Input audio (blue) - raw microphone capture
    • 3A-processed audio (green) - post-processing output
  • JavaScript Interop: Waveform data passed as byte[] through AudioCallService events, throttled to ~10 frames/update
<WaveformVisualizer @ref="_inputWaveform" 
                    Width="800" Height="100" 
                    Label="输入音频波形" Color="#1890ff" />

Docker Deployment

  • Dockerfile: Multi-stage build for WebAPI service optimized for production
  • GitHub Actions Workflow: Automated CI/CD pipeline (docker-build.yml) that builds and pushes images to Docker Hub
  • Docker Hub Repository: gmij/audio3a with support for multiple tagging strategies (latest, version tags, SHA-based tags)
  • Documentation: Complete deployment guide in docs/DOCKER.md with Docker Compose examples and production configurations
docker pull gmij/audio3a:latest
docker run -d -p 8080:80 gmij/audio3a:latest

Audio Recording and Download

  • Recording Controls: Record button in call interface with visual feedback (pulse animation during recording)
  • Dual Audio Recording: Simultaneously records both input audio (raw microphone) and 3A-processed audio using MediaRecorder API
  • Download Options: Dropdown menu to download either or both audio tracks as WebM files with automatic timestamp-based filenames
  • Documentation: Complete usage guide in docs/AUDIO_RECORDING.md with browser compatibility and troubleshooting information

Recording Workflow:

  1. Click record button (camera icon) to start recording
  2. Recording indicator shows red pulse animation
  3. Click again to stop recording
  4. Click download button and select audio type to download

Known Limitations

  • ScriptProcessor API is deprecated; production should migrate to AudioWorklet
  • InviteCode functionality only available in MockApiService (temporary workaround in RealApiService generates code from roomId prefix)
  • Current 3A processing uses browser-native features; future integration with custom WASM processing recommended
  • Audio recording produces WebM format; browser compatibility for playback may vary
Original prompt

我们已经完成了wasm客户端的版本,现在我们要开始做服务端了

  1. 真正的会议室管理,而不是运行在浏览器内存里的
  2. 在通话界面,显示声音的波形图,一个是输入的波形,一个是被3A净化后的波形。

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits November 23, 2025 08:16
…guration

Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
…cessed audio

Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement server version for wasm client Implement server-side room management and real-time waveform visualization Nov 23, 2025
Copilot AI requested a review from gmij November 23, 2025 08:31
@gmij
Copy link
Copy Markdown
Owner

gmij commented Nov 23, 2025

在actions里,添加一个打包成dockers的设置,推送到dockerhub的gmij/audio3a去@copilot

Copy link
Copy Markdown
Owner

@gmij gmij left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

通话界面里,要提供两个音频的下载(原声和净化后的)

Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 23, 2025

通话界面里,要提供两个音频的下载(原声和净化后的)

已添加音频录制和下载功能,提交 b81ff4e

新增功能

  • 录制按钮 - 控制音频录制(录制中有脉冲动画)
  • 下载按钮 - 提供两个下载选项(录制后启用)
    • 下载原声音频
    • 下载净化后音频
  • ✅ 自动生成带时间戳的文件名(WebM 格式)

使用流程

  1. 进入通话后点击录制按钮(摄像机图标)
  2. 录制中按钮变红并闪烁
  3. 再次点击停止录制
  4. 点击下载按钮,从下拉菜单选择要下载的音频

技术实现

  • 使用 MediaRecorder API 同时录制两路音频
  • 原声:直接录制麦克风输入
  • 净化后:录制经过 Web Audio API 处理的音频流
  • 输出格式:audio/webm (Opus codec)

在actions里,添加一个打包成dockers的设置,推送到dockerhub的gmij/audio3a去@copilot

已添加 Docker 打包功能,提交 b81ff4e

实现内容

  • ✅ 创建 samples/Audio3A.WebApi/Dockerfile - 多阶段构建,基于 .NET 8
  • ✅ 添加 .github/workflows/docker-build.yml - 自动构建和推送
  • ✅ 推送目标:gmij/audio3a

使用方式
推送到 main 分支或创建版本标签(如 v1.0.0)时自动触发。需要在 GitHub Settings → Secrets 中配置:

  • DOCKERHUB_USERNAME
  • DOCKERHUB_TOKEN

Docker 镜像标签

  • latest - main 分支最新版本
  • v1.0.0 - 版本号标签
  • main-abc1234 - 分支名+SHA

本地测试

docker build -t gmij/audio3a:latest -f samples/Audio3A.WebApi/Dockerfile .
docker run -p 8080:80 gmij/audio3a:latest

Copilot AI and others added 2 commits November 23, 2025 09:05
Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
…ic, use constants for magic numbers

Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
Copilot AI changed the title Implement server-side room management and real-time waveform visualization Implement server-side room management, real-time waveform visualization, Docker deployment, and audio recording Nov 23, 2025
Copilot AI requested a review from gmij November 23, 2025 09:15
@gmij gmij marked this pull request as ready for review November 23, 2025 10:49
Copilot AI review requested due to automatic review settings November 23, 2025 10:49
@gmij gmij merged commit 04a5a91 into main Nov 23, 2025
5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

此 PR 将 Audio3A Web 应用从纯浏览器内存模拟升级为支持服务端集成的架构,同时添加了实时音频波形可视化、Docker 容器化部署和音频录制下载功能。

主要变更:

  • 引入 IApiService 接口统一 Mock 和 Real API 实现,通过配置文件切换
  • 实时波形可视化:在通话界面显示输入音频和 3A 处理后的波形对比
  • Docker 部署:完整的 Dockerfile、GitHub Actions CI/CD 流程和 Docker Hub 集成
  • 音频录制:支持同时录制原声和处理后音频,并提供下载功能

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
samples/Audio3A.WebApi/Dockerfile 多阶段构建的 Docker 镜像,用于 WebAPI 服务生产部署
samples/Audio3A.WebApi/.dockerignore Docker 构建排除文件列表
samples/Audio3A.Web/wwwroot/js/waveform.js 波形可视化 Canvas 渲染逻辑
samples/Audio3A.Web/wwwroot/js/audioCall.js 音频采集、波形数据收集、录制功能实现
samples/Audio3A.Web/wwwroot/appsettings.json 添加 UseMockApi 配置标志
samples/Audio3A.Web/_Imports.razor 导入 Components 命名空间
samples/Audio3A.Web/Services/IApiService.cs API 服务统一接口和共享数据模型
samples/Audio3A.Web/Services/RealApiService.cs 连接 WebAPI 后端的真实 API 实现
samples/Audio3A.Web/Services/MockApiService.cs 重构为实现 IApiService 接口,内部数据模型改为私有
samples/Audio3A.Web/Services/AudioCallService.cs 添加波形数据事件和录制相关方法
samples/Audio3A.Web/Program.cs 基于配置条件注册 API 服务实现
samples/Audio3A.Web/Pages/*.razor 更新为注入 IApiService 而非 MockApiService
samples/Audio3A.Web/Pages/Call.razor 集成波形可视化组件和录制控制 UI
samples/Audio3A.Web/Components/WaveformVisualizer.razor 可复用的波形可视化 Blazor 组件
docs/NEW_FEATURES.md 新功能使用说明文档
docs/DOCKER.md Docker 部署完整指南
docs/AUDIO_RECORDING.md 音频录制功能详细文档
.github/workflows/docker-build.yml 自动化 Docker 镜像构建和推送流程

public string Name { get; set; } = string.Empty;
public string RoomId { get; set; } = string.Empty;
public string State { get; set; } = string.Empty;
public DateTime JoinedAt { get; set; } = DateTime.Now;
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在数据传输对象 (DTO) 中使用 DateTime.Now 作为默认值是不正确的。每次创建新实例时都会设置为创建时间,而非实际加入时间。应该移除默认值,让服务层设置正确的时间戳,或使用 DateTime 的默认值。

Suggested change
public DateTime JoinedAt { get; set; } = DateTime.Now;
public DateTime JoinedAt { get; set; }

Copilot uses AI. Check for mistakes.
Comment on lines +119 to +122
// WebAPI 不返回 InviteCode,生成一个临时的
// 注意:这是一个临时解决方案,生产环境应在服务端实现 InviteCode 功能
// 或者考虑移除此功能以保持与 API 的一致性
var inviteCode = roomId.Length >= 6 ? roomId.Substring(0, 6).ToUpper() : roomId.ToUpper();
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

临时方案在客户端生成 InviteCode 违反了单一数据源原则。建议在 RoomDetailInfo 中将 InviteCode 改为可空类型 string?,当 Real API 不支持时返回 null,让 UI 层处理显示逻辑。这样避免了在服务层生成虚假数据。

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +135
EnableAec = response.Participants.Any(p => p.Enable3A), // 从参与者推断
EnableAgc = response.Participants.Any(p => p.Enable3A),
EnableAns = response.Participants.Any(p => p.Enable3A),
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

从参与者的 Enable3A 推断房间的 3A 配置是不准确的。不同参与者可能有不同的 3A 设置,且房间本身的 3A 配置应该是房间级别的属性。如果 WebAPI 不返回这些字段,应该在 API 层添加这些字段,或在客户端明确标记为"未知"状态 (使用可空布尔类型)。

Suggested change
EnableAec = response.Participants.Any(p => p.Enable3A), // 从参与者推断
EnableAgc = response.Participants.Any(p => p.Enable3A),
EnableAns = response.Participants.Any(p => p.Enable3A),
// 房间级别的 3A 配置 WebAPI 未返回,标记为未知(null)
EnableAec = null,
EnableAgc = null,
EnableAns = null,

Copilot uses AI. Check for mistakes.
// 注意:ScriptProcessor 已被弃用,但在这里我们用它来演示
// 生产环境应该使用 AudioWorklet
// TODO: 迁移到 AudioWorklet API 以获得更好的性能
scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1);
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ScriptProcessor API 已被弃用,虽然代码中有 TODO 注释。建议创建一个 GitHub issue 跟踪迁移到 AudioWorklet 的计划,因为 ScriptProcessor 在未来的浏览器版本中可能被移除,会导致功能失效。

Copilot uses AI. Check for mistakes.
Comment on lines +240 to +243
// 每隔一定帧数发送波形数据
if (!collectWaveformData.counter) collectWaveformData.counter = {};
if (!collectWaveformData.counter[type]) collectWaveformData.counter[type] = 0;
collectWaveformData.counter[type]++;
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用函数的静态属性存储状态 (collectWaveformData.counter) 使代码难以测试和重置。建议将 counter 作为模块级变量,或使用闭包/类封装这些状态,以提高可维护性和可测试性。

Copilot uses AI. Check for mistakes.

// 读取 API 配置
var apiConfig = new ApiConfiguration();
var useMockApi = false; // 默认使用真实 API
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在代码中硬编码默认值 false,然后在后续通过配置覆盖,这种模式可能导致混淆。建议直接从配置读取,如果配置不存在则使用默认值,使用 bool.TryParse(config.GetValueOrDefault("UseMockApi", "false"), out var useMockApi) 模式更清晰。

Copilot uses AI. Check for mistakes.
Comment on lines 163 to +165
}
// 注意:Real API 不支持通过 InviteCode 查找房间
// InviteCode 功能仅在 Mock API 中可用(用于 GitHub Pages 演示)
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删除了 InviteCode 查找逻辑,但页面路由 @page "/join/{InviteCode}" 仍然存在。当用户通过 InviteCode URL 访问时,InviteCode 参数会被忽略,导致 _room 为 null,页面显示"房间不存在"。应该添加逻辑处理这种情况或移除该路由。

Copilot uses AI. Check for mistakes.
Comment on lines +113 to +120
public async ValueTask DisposeAsync()
{
if (_module != null)
{
await _module.DisposeAsync();
}
_objRef?.Dispose();
}
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

组件实现了 DisposeAsync 但没有实现 IAsyncDisposable 接口。虽然 Blazor 会调用该方法,但显式实现接口可以提高代码清晰度,并在编译时检查实现是否正确。建议添加 @implements IAsyncDisposable

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +20
RUN dotnet build "Audio3A.WebApi.csproj" -c Release -o /app/build

Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在 Dockerfile 中同时执行 dotnet builddotnet publish 是冗余的。dotnet publish 会自动执行构建步骤。建议删除第 19 行的 build 步骤以减少镜像构建时间和层数。

Suggested change
RUN dotnet build "Audio3A.WebApi.csproj" -c Release -o /app/build

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +56
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

步骤引用了 steps.docker_build.outputs.digest,但构建步骤的 id 没有设置为 docker_build。第 44 行的 "Build and push Docker image" 步骤需要添加 id: docker_build,否则这个输出步骤会失败。

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants