涉及网络音频时更改检测不起作用
Change detection not working when web audio involved
类似于Angular change detection with HTMLAudioElement,但它的解决方案对我不起作用。
在我的 angular 应用程序中,我想使用网络音频播放短 wav 文件。播放时播放按钮变成停止按钮,播放结束后停止按钮又变成播放按钮。
我做了一个简单的音频服务,它在 AudioBufferSourceNode.start(0)
之后触发一个 observable,也在 AudioBufferSourceNode.onended
触发一个 observable。我可以在控制台中看到事件触发,但 UI 没有改变。我做了一个 stackblitz 演示我的问题 https://stackblitz.com/edit/angular-x5ytjt - 当实际使用音频 api (复选框设置)时 UI 没有更新,当只触发可观察的(复选框未设置) UI 得到更新。
在这种情况下,我怎样才能更新我的 UI?
组件:
@Component({
selector: 'hello',
template: `
<input type="checkbox" [(ngModel)]="playReal">Real playback (vs. fake-events)<br>
Currently {{playing?"playing":"stopped"}}
<button type="button" *ngIf="!playing" (click)="play()">play</button>
<button type="button" *ngIf="playing" (click)="stop()">stop</button>
`,
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent {
@Input() name: string;
playing: boolean = false;
subscription: Subscription;
playReal: boolean = true;
constructor(public audio: AudioService, public dataSvc: DataService, private ref: ChangeDetectorRef) { }
ngOnInit() {
this.subscription = this.audio.playing$.subscribe(value => {
this.playing = value;
console.debug('observer has fired. new value: ', value);
// solution from
this.ref.markForCheck();
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
play() {
this.dataSvc.getAudio().subscribe(
data => {
if (this.playReal) {
// do real playback
this.audio.playBlob(data);
} else {
// only fake playing (tell the audio service to emit the event)
this.audio.hackSetPlaying(true);
}
}
)
}
stop() {
if (this.playReal) {
this.audio.stopPlay();
} else {
this.audio.hackSetPlaying(false);
}
}
}
音频服务:
public playBlob( data: Blob ) {
// playBlob and play inspired by
// create audio context if necessary
if (!this.audioCtx) {
this.audioCtx = new AudioContext();
}
// use file reader to convert blob to ArrayBuffer
let fr: FileReader = new FileReader();
fr.onload = () => {
// after loading decode and play the wav file
this.audioCtx.decodeAudioData(<ArrayBuffer>fr.result, (res) => {this.play(res);});
}
fr.readAsArrayBuffer(data);
}
private play(audioBuff: AudioBuffer) {
if (!this.audioSrc) {
this.audioSrc = this.audioCtx.createBufferSource();
}
this.audioSrc.buffer = audioBuff;
this.audioSrc.connect(this.audioCtx.destination);
this.audioSrc.start(0);
this.playing = true;
this.playingSubject.next(this.playing);
console.debug('audioService has set playing to true');
this.audioSrc.onended = () => {
this.playing = false;
this.playingSubject.next(this.playing);
console.debug('audioService set playing to false');
this.audioSrc = null; // audioSrc can only be used once
}
}
编辑:我刚刚了解到,我显然使用的是网络音频 api 而不是 html5 音频。更正标签、标题等
我在
中找到了答案
我必须调用 ChangeDetectorRef
的方法 detectChanges()
而不是 markForCheck()
。我已经更新了 stackblitz。
类似于Angular change detection with HTMLAudioElement,但它的解决方案对我不起作用。
在我的 angular 应用程序中,我想使用网络音频播放短 wav 文件。播放时播放按钮变成停止按钮,播放结束后停止按钮又变成播放按钮。
我做了一个简单的音频服务,它在 AudioBufferSourceNode.start(0)
之后触发一个 observable,也在 AudioBufferSourceNode.onended
触发一个 observable。我可以在控制台中看到事件触发,但 UI 没有改变。我做了一个 stackblitz 演示我的问题 https://stackblitz.com/edit/angular-x5ytjt - 当实际使用音频 api (复选框设置)时 UI 没有更新,当只触发可观察的(复选框未设置) UI 得到更新。
在这种情况下,我怎样才能更新我的 UI?
组件:
@Component({
selector: 'hello',
template: `
<input type="checkbox" [(ngModel)]="playReal">Real playback (vs. fake-events)<br>
Currently {{playing?"playing":"stopped"}}
<button type="button" *ngIf="!playing" (click)="play()">play</button>
<button type="button" *ngIf="playing" (click)="stop()">stop</button>
`,
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent {
@Input() name: string;
playing: boolean = false;
subscription: Subscription;
playReal: boolean = true;
constructor(public audio: AudioService, public dataSvc: DataService, private ref: ChangeDetectorRef) { }
ngOnInit() {
this.subscription = this.audio.playing$.subscribe(value => {
this.playing = value;
console.debug('observer has fired. new value: ', value);
// solution from
this.ref.markForCheck();
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
play() {
this.dataSvc.getAudio().subscribe(
data => {
if (this.playReal) {
// do real playback
this.audio.playBlob(data);
} else {
// only fake playing (tell the audio service to emit the event)
this.audio.hackSetPlaying(true);
}
}
)
}
stop() {
if (this.playReal) {
this.audio.stopPlay();
} else {
this.audio.hackSetPlaying(false);
}
}
}
音频服务:
public playBlob( data: Blob ) {
// playBlob and play inspired by
// create audio context if necessary
if (!this.audioCtx) {
this.audioCtx = new AudioContext();
}
// use file reader to convert blob to ArrayBuffer
let fr: FileReader = new FileReader();
fr.onload = () => {
// after loading decode and play the wav file
this.audioCtx.decodeAudioData(<ArrayBuffer>fr.result, (res) => {this.play(res);});
}
fr.readAsArrayBuffer(data);
}
private play(audioBuff: AudioBuffer) {
if (!this.audioSrc) {
this.audioSrc = this.audioCtx.createBufferSource();
}
this.audioSrc.buffer = audioBuff;
this.audioSrc.connect(this.audioCtx.destination);
this.audioSrc.start(0);
this.playing = true;
this.playingSubject.next(this.playing);
console.debug('audioService has set playing to true');
this.audioSrc.onended = () => {
this.playing = false;
this.playingSubject.next(this.playing);
console.debug('audioService set playing to false');
this.audioSrc = null; // audioSrc can only be used once
}
}
编辑:我刚刚了解到,我显然使用的是网络音频 api 而不是 html5 音频。更正标签、标题等
我在
我必须调用 ChangeDetectorRef
的方法 detectChanges()
而不是 markForCheck()
。我已经更新了 stackblitz。