Skip to content

Commit

Permalink
Merge pull request #16 from sukumo28/reload-file
Browse files Browse the repository at this point in the history
support auto reloading when user modify audio file
  • Loading branch information
sukumo28 authored Aug 7, 2021
2 parents 543a729 + c3d889a commit 42cdb26
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 1.6.0 - 2020-08-07
### Changed
- Support auto reloading

## 1.5.0 - 2020-07-09
### Changed
- Improve performance
Expand Down
59 changes: 40 additions & 19 deletions media/audioPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,37 +65,58 @@ class Player {
}

onChange() {
// stop if plaing
if (this.isPlaying) {
this.stop();
}
// restart from selected place
this.currentSec = this.seekBar.value * this.duration / 100;
this.play();
}

dispose() {
if (this.isPlaying) {
this.stop();
}
this.button.removeEventListener("click", this.button.onclick);
this.seekBar.removeEventListener("change", this.seekBar.onchange);
this.button.textContent = "please wait...";
this.button.disabled = true;
this.seekBar.style.display = "none";
this.button = undefined;
this.seekBar = undefined;
}
}

(function () {
const vscode = acquireVsCodeApi();
let audioBuffer;
let audioBuffer, player;
const message = document.getElementById("message");
const decodeState = document.getElementById("decode-state");

// Handle messages from the extension
window.addEventListener('message', async e => {
const { type, data, isTrusted } = e.data;

switch (type) {
case "info":
if (!data) {
message.textContent = "failed to decode header: invalid";
return;
}
await showInfo(data);
// do not play audio in untrusted workspace
if (isTrusted === false) {
const message = document.getElementById("message");
message.textContent = "Cannot play audio in untrusted workspaces";
break;
}
vscode.postMessage({ type: 'prepare' });
break;

case "prepare":
if (!data) {
message.textContent = "failed to decode data: invalid";
return;
}
await showPlayer(data);
vscode.postMessage({ type: 'play', start: 0, end: 10000 });
break;
Expand All @@ -105,16 +126,16 @@ class Player {
if (audioBuffer.length <= data.end) break;
vscode.postMessage({ type: 'play', start: data.end, end: data.end + 10000 });
break;

case "reload":
await reload();
vscode.postMessage({ type: 'ready' });
break;

}
});

async function showInfo(data) {
const message = document.getElementById("message");
if (!data) {
message.textContent = "failed to decode header: undefined";
return;
}

const compressFormat = {
0: "unknown", 1: "uncompressed PCM", 2: "Microsoft ADPCM",
3: "IEEE Float", 6: "a-law", 7: "mu-law",
Expand Down Expand Up @@ -143,12 +164,6 @@ class Player {
}

async function showPlayer(data) {
const message = document.getElementById("message");
if (!data) {
message.textContent = "failed to decode data: undefined";
return;
}

try {
const ac = new AudioContext({ sampleRate: data.sampleRate });
audioBuffer = ac.createBuffer(data.numberOfChannels, data.length, data.sampleRate);
Expand All @@ -158,15 +173,15 @@ class Player {
}

// set player ui
new Player(ac, audioBuffer, data.duration);
player = new Player(ac, audioBuffer, data.duration);
} catch (err) {
message.textContent = "failed to prepare audioBufferSourceNode: " + err;
return;
}
}

async function setData(data) {
// copy passed data.samples into audioBuffer manually, because it is once stringified(?),
// copy passed data.samples into audioBuffer manually, because it is once stringified,
// and its children are not recognised as Float32Array
for (let ch = 0; ch < data.numberOfChannels; ch++) {
const f32a = new Float32Array(data.length);
Expand All @@ -177,11 +192,17 @@ class Player {
}

// show progress
const decodeState = document.getElementById("decode-state");
const progress = Math.floor(data.end * 100 / audioBuffer.length);
const progress = Math.min(Math.floor(data.end * 100 / audioBuffer.length), 100);
decodeState.textContent = "decode: " + progress + "% done";
}

async function reload() {
message.textContent = "";
decodeState.textContent = "";
player.dispose();
player = undefined;
}

// Signal to VS Code that the webview is initialized.
vscode.postMessage({ type: 'ready' });
}());
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "wav-preview",
"displayName": "wav-preview",
"description": "preview and play wav file in VS Code",
"version": "1.5.0",
"version": "1.6.0",
"engines": {
"vscode": "^1.56.2"
},
Expand Down
69 changes: 66 additions & 3 deletions src/audioPreviewEditor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from "vscode";
import { Disposable } from "./dispose";
import { Disposable, disposeAll } from "./dispose";
import * as path from "path";
import { getNonce } from "./util";
import { WaveFile } from 'wavefile';
Expand Down Expand Up @@ -31,6 +31,7 @@ class AudioPreviewDocument extends Disposable implements vscode.CustomDocument {

private readonly _uri: vscode.Uri;
private _documentData: any;
private _fsWatcher: vscode.FileSystemWatcher;

private constructor (
uri: vscode.Uri,
Expand All @@ -39,11 +40,15 @@ class AudioPreviewDocument extends Disposable implements vscode.CustomDocument {
super();
this._uri = uri;
this._documentData = initialContent;
this._fsWatcher = vscode.workspace.createFileSystemWatcher(uri.fsPath, true, false, true);
this.onDidChange = this._fsWatcher.onDidChange;
}

public get uri() { return this._uri; }

public get wavHeader(): any {
public onDidChange: vscode.Event<vscode.Uri>;

public wavHeader(): any {
return {
fmt: this._documentData.fmt
};
Expand Down Expand Up @@ -134,6 +139,11 @@ class AudioPreviewDocument extends Disposable implements vscode.CustomDocument {
}
}

public async reload() {
const fileData = await AudioPreviewDocument.readFile(this._uri);
this._documentData = new WaveFile(fileData);
}

private readonly _onDidDispose = this._register(new vscode.EventEmitter<void>());
public readonly onDidDispose = this._onDidDispose.event;

Expand Down Expand Up @@ -165,6 +175,8 @@ export class AudioPreviewEditorProvider implements vscode.CustomReadonlyEditorPr

private static readonly viewType = 'wavPreview.audioPreview';

private readonly webviews = new WebviewCollection();

constructor (
private readonly _context: vscode.ExtensionContext
) { }
Expand All @@ -179,6 +191,19 @@ export class AudioPreviewEditorProvider implements vscode.CustomReadonlyEditorPr
openContext.backupId
);

const listeners: vscode.Disposable[] = [];

listeners.push(document.onDidChange(async (e) => {
await document.reload();
for (const webviewPanel of this.webviews.get(document.uri)) {
webviewPanel.webview.postMessage({
type: "reload"
});
}
}));

document.onDidDispose(() => disposeAll(listeners));

return document;
}

Expand All @@ -187,6 +212,9 @@ export class AudioPreviewEditorProvider implements vscode.CustomReadonlyEditorPr
webviewPanel: vscode.WebviewPanel,
_token: vscode.CancellationToken
): Promise<void> {
// Add the webview to our internal set of active webviews
this.webviews.add(document.uri, webviewPanel);

// Setup initial content for the webview
webviewPanel.webview.options = {
enableScripts: true,
Expand All @@ -199,7 +227,7 @@ export class AudioPreviewEditorProvider implements vscode.CustomReadonlyEditorPr
case "ready":
webviewPanel.webview.postMessage({
type: "info",
data: document.wavHeader,
data: document.wavHeader(),
isTrusted: vscode.workspace.isTrusted
});
break;
Expand Down Expand Up @@ -271,3 +299,38 @@ export class AudioPreviewEditorProvider implements vscode.CustomReadonlyEditorPr
`;
}
}

/**
* Tracks all webviews.
*/
class WebviewCollection {

private readonly _webviews = new Set<{
readonly resource: string;
readonly webviewPanel: vscode.WebviewPanel;
}>();

/**
* Get all known webviews for a given uri.
*/
public *get(uri: vscode.Uri): Iterable<vscode.WebviewPanel> {
const key = uri.toString();
for (const entry of this._webviews) {
if (entry.resource === key) {
yield entry.webviewPanel;
}
}
}

/**
* Add a new webview to the collection.
*/
public add(uri: vscode.Uri, webviewPanel: vscode.WebviewPanel) {
const entry = { resource: uri.toString(), webviewPanel };
this._webviews.add(entry);

webviewPanel.onDidDispose(() => {
this._webviews.delete(entry);
});
}
}

0 comments on commit 42cdb26

Please sign in to comment.