2016-02-16 2 views
5

В моем веб-приложении у меня есть требование играть часть mp3-файла. Это локальное веб-приложение, поэтому мне не нужны загрузки и т. Д., Все хранится локально.Как декодировать только часть mp3 для использования с API WebAudio?

Мой случай использования заключается в следующем:

  • определить файл для воспроизведения
  • определяют начало и остановку звука
  • загрузить файл [Я ​​использую BufferLoader]
  • игра

Довольно просто.

Прямо сейчас я просто захватил mp3-файл, расшифровал его в памяти для использования с API WebAudio и воспроизвел его. К сожалению, поскольку mp3-файлы могут быть довольно длинными [30 минут аудио, например], декодированный файл в памяти может занимать до 900 МБ. Это слишком сложно справиться.

Есть ли какой-либо вариант, где я мог бы декодировать только часть файла? Как я могу определить, с чего начать и как далеко идти? Я не могу предсказать битрейт, он может быть постоянным, но я бы тоже ожидал переменную.

Вот пример того, что я сделал: http://tinyurl.com/z9vjy34

Код [Я пытался сделать его как можно компактнее]:

var MediaPlayerAudioContext = window.AudioContext || window.webkitAudioContext; 

var MediaPlayer = function() { 
    this.mediaPlayerAudioContext = new MediaPlayerAudioContext(); 
    this.currentTextItem = 0; 
    this.playing = false; 
    this.active = false; 
    this.currentPage = null; 
    this.currentAudioTrack = 0; 
}; 

MediaPlayer.prototype.setPageNumber = function (page_number) { 
    this.pageTotalNumber = page_number 
}; 

MediaPlayer.prototype.generateAudioTracks = function() { 
    var audioTracks = []; 
    var currentBegin; 
    var currentEnd; 
    var currentPath; 
    audioTracks[0] = { 
     begin: 4.300, 
     end: 10.000, 
     path: "example.mp3" 
    }; 
    this.currentPageAudioTracks = audioTracks; 
}; 

MediaPlayer.prototype.show = function() { 
    this.mediaPlayerAudioContext = new MediaPlayerAudioContext(); 
}; 

MediaPlayer.prototype.hide = function() { 
    if (this.playing) { 
     this.stop(); 
    } 
    this.mediaPlayerAudioContext = null; 
    this.active = false; 
}; 

MediaPlayer.prototype.play = function() { 
    this.stopped = false; 
    console.trace(); 

    this.playMediaPlayer(); 
}; 

MediaPlayer.prototype.playbackStarted = function() { 
    this.playing = true; 
}; 

MediaPlayer.prototype.playMediaPlayer = function() { 
    var instance = this; 

    var audioTrack = this.currentPageAudioTracks[this.currentAudioTrack]; 
    var newBufferPath = audioTrack.path; 

    if (this.mediaPlayerBufferPath && this.mediaPlayerBufferPath === newBufferPath) { 

     this.currentBufferSource = this.mediaPlayerAudioContext.createBufferSource(); 
     this.currentBufferSource.buffer = this.mediaPlayerBuffer; 
     this.currentBufferSource.connect(this.mediaPlayerAudioContext.destination); 

     this.currentBufferSource.onended = function() { 
      instance.currentBufferSource.disconnect(0); 
      instance.audioTrackFinishedPlaying() 
     }; 

     this.playing = true; 
     this.currentBufferSource.start(0, audioTrack.begin, audioTrack.end - audioTrack.begin); 

     this.currentAudioStartTimeInAudioContext = this.mediaPlayerAudioContext.currentTime; 
     this.currentAudioStartTimeOffset = audioTrack.begin; 
     this.currentTrackStartTime = this.mediaPlayerAudioContext.currentTime - (this.currentTrackResumeOffset || 0); 
     this.currentTrackResumeOffset = null; 

    } 
    else { 
     function finishedLoading(bufferList) { 
      instance.mediaPlayerBuffer = bufferList[0]; 
      instance.playMediaPlayer(); 
     } 

     if (this.currentBufferSource){ 
      this.currentBufferSource.disconnect(0); 
      this.currentBufferSource.stop(0); 
      this.currentBufferSource = null; 
     } 

     this.mediaPlayerBuffer = null; 
     this.mediaPlayerBufferPath = newBufferPath; 
     this.bufferLoader = new BufferLoader(this.mediaPlayerAudioContext, [this.mediaPlayerBufferPath], finishedLoading); 
     this.bufferLoader.load(); 
    } 
}; 

MediaPlayer.prototype.stop = function() { 
    this.stopped = true; 
    if (this.currentBufferSource) { 
     this.currentBufferSource.onended = null; 
     this.currentBufferSource.disconnect(0); 
     this.currentBufferSource.stop(0); 
     this.currentBufferSource = null; 

    } 
    this.bufferLoader = null; 
    this.mediaPlayerBuffer = null; 
    this.mediaPlayerBufferPath = null; 
    this.currentTrackStartTime = null; 
    this.currentTrackResumeOffset = null; 
    this.currentAudioTrack = 0; 

    if (this.currentTextTimeout) { 
     clearTimeout(this.currentTextTimeout); 
     this.textHighlightFinished(); 
     this.currentTextTimeout = null; 
     this.currentTextItem = null; 
    } 

    this.playing = false; 
}; 

MediaPlayer.prototype.getNumberOfPages = function() { 
    return this.pageTotalNumber; 
}; 

MediaPlayer.prototype.playbackFinished = function() { 

    this.currentAudioTrack = 0; 
    this.playing = false; 

}; 

MediaPlayer.prototype.audioTrackFinishedPlaying = function() { 

    this.currentAudioTrack++; 

    if (this.currentAudioTrack >= this.currentPageAudioTracks.length) { 
     this.playbackFinished(); 
    } else { 
     this.playMediaPlayer(); 
    } 
}; 

// 
// 
// Buffered Loader 
// 
// Class used to get the sound files 
// 
function BufferLoader(context, urlList, callback) { 
    this.context = context; 
    this.urlList = urlList; 
    this.onload = callback; 
    this.bufferList = []; 
    this.loadCount = 0; 
} 

// this allows us to handle media files with embedded artwork/id3 tags 
function syncStream(node) { // should be done by api itself. and hopefully will. 
    var buf8 = new Uint8Array(node.buf); 
    buf8.indexOf = Array.prototype.indexOf; 
    var i = node.sync, b = buf8; 
    while (1) { 
     node.retry++; 
     i = b.indexOf(0xFF, i); 
     if (i == -1 || (b[i + 1] & 0xE0 == 0xE0)) break; 
     i++; 
    } 
    if (i != -1) { 
     var tmp = node.buf.slice(i); //carefull there it returns copy 
     delete(node.buf); 
     node.buf = null; 
     node.buf = tmp; 
     node.sync = i; 
     return true; 
    } 
    return false; 
} 

BufferLoader.prototype.loadBuffer = function (url, index) { 
    // Load buffer asynchronously 
    var request = new XMLHttpRequest(); 
    request.open("GET", url, true); 
    request.responseType = "arraybuffer"; 

    var loader = this; 

    function decode(sound) { 
     loader.context.decodeAudioData(
       sound.buf, 
       function (buffer) { 
        if (!buffer) { 
         alert('error decoding file data'); 
         return 
        } 
        loader.bufferList[index] = buffer; 
        if (++loader.loadCount == loader.urlList.length) 
         loader.onload(loader.bufferList); 
       }, 
       function (error) { 
        if (syncStream(sound)) { 
         decode(sound); 
        } else { 
         console.error('decodeAudioData error', error); 
        } 
       } 
     ); 
    } 

    request.onload = function() { 
     // Asynchronously decode the audio file data in request.response 
     var sound = {}; 
     sound.buf = request.response; 
     sound.sync = 0; 
     sound.retry = 0; 
     decode(sound); 
    }; 
    request.onerror = function() { 
     alert('BufferLoader: XHR error'); 
    }; 
    request.send(); 
}; 

BufferLoader.prototype.load = function() { 
    for (var i = 0; i < this.urlList.length; ++i) 
     this.loadBuffer(this.urlList[i], i); 
}; 
+0

Вы конвертируете MP3 в WAV-файл? 900 МБ - орехи. – zer00ne

+0

Я не нашел способ использовать mp3 прямо, всегда нужно его декодировать. – Krystian

+0

Если вы просто хотите воспроизвести его, почему бы не использовать тег 'audio'? –

ответ

1

Там нет никакого способа потоковой передачи с decodeAudioData (), вам нужно использовать MediaElement с createMediaStreamSource и запускать ваши вещи. decodeAudioData() не может проигрывать на части. @ zre00ne И mp3 будет расшифрован большой !!! Очень большой!!!

+0

Правильно, вот что происходит. 20MB mp3 становится 900MB PCM: / – Krystian