Changeset 1007 for trunk/src/org/apollo/audio/ApolloPlaybackMixer.java
- Timestamp:
- 03/08/16 23:22:09 (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/apollo/audio/ApolloPlaybackMixer.java
r375 r1007 324 324 if (playbackThread == null) { 325 325 326 playbackThread = new PlaybackThread(); 326 AudioFormat audio_format = SampledAudioManager.getInstance().getDefaultPlaybackFormat(); 327 if (audio_format.getChannels()==2) { 328 playbackThread = new StereoPlaybackThread(); 329 } 330 else { 331 playbackThread = new MonoPlaybackThread(); 332 } 327 333 playbackThread.start(); 328 334 … … 451 457 * 452 458 */ 453 private class PlaybackThread extends Thread {454 455 pr ivateSourceDataLine srcDataLine; // never null456 457 pr ivateboolean isStopping = false;458 459 pr ivateint bufferFrameLength;460 pr ivateboolean isOutputBigEndian;459 private abstract class PlaybackThread extends Thread { 460 461 protected SourceDataLine srcDataLine; // never null 462 463 protected boolean isStopping = false; 464 465 protected int bufferFrameLength; 466 protected boolean isOutputBigEndian; 461 467 462 468 /** … … 465 471 * @throws LineUnavailableException 466 472 */ 467 PlaybackThread() throws LineUnavailableException {468 super( "Apollo Playback Mixer Thread");473 protected PlaybackThread(String threadName) throws LineUnavailableException { 474 super(threadName); 469 475 super.setPriority(Thread.MAX_PRIORITY); 470 476 … … 494 500 * If failed to acquire the source data line. 495 501 */ 496 pr ivatevoid aquireSourceDataLine() throws LineUnavailableException {502 protected void aquireSourceDataLine() throws LineUnavailableException { 497 503 498 504 // Select an audio output format … … 508 514 509 515 // Cache useful data 510 bufferFrameLength = srcDataLine.getBufferSize() / 2;516 bufferFrameLength = srcDataLine.getBufferSize() / (2*2); // 2=stereo, 2=16-bit 511 517 isOutputBigEndian = srcDataLine.getFormat().isBigEndian(); 512 518 … … 527 533 528 534 /** 529 * Note: even if all tracks have been proc cessed in the audio pipeline, it will535 * Note: even if all tracks have been processed in the audio pipeline, it will 530 536 * commence another pass to check for new tracks added to the graph before finishing. 531 537 * … … 540 546 * @return the best audio format for playback... 541 547 */ 542 pr ivateAudioFormat getAudioFormat() {548 protected AudioFormat getAudioFormat() { 543 549 return SampledAudioManager.getInstance().getDefaultPlaybackFormat(); 544 550 } 545 551 552 /** 553 * The audio mixing pipeline 554 */ 555 public abstract void run(); 556 557 558 559 } 560 561 private class StereoPlaybackThread extends PlaybackThread { 562 563 564 565 /** 566 * Initantly prepares for audio playback: Opens the (stereo) source data line for output 567 * 568 * @throws LineUnavailableException 569 */ 570 StereoPlaybackThread() throws LineUnavailableException { 571 super("Apollo Stereo Playback Mixer Thread"); 572 573 } 574 575 576 /** 577 * The audio mixing pipeline 578 */ 579 public void run() { 580 581 // Notify observers that some audio has started playing 582 ApolloPlaybackMixer.this.fireSubjectChangedLaterOnSwingThread( 583 new SubjectChangedEvent(ApolloSubjectChangedEvent.PLAYBACK_STARTED)); 584 585 // All tracks to play per pass 586 List<TrackSequence> tracksToPlay = new LinkedList<TrackSequence>(); 587 588 // Keeps track of tracks to remove 589 List<TrackSequence> completedTracks = new LinkedList<TrackSequence>(); 590 591 // The buffer written directly to the source data line 592 byte[] sampleBuffer = new byte[2 * 2 * bufferFrameLength]; // 2=stereo, 2=16-bit samples 593 594 // The mixed frames, where each element refers to a frame 595 int[] mixedFrameBufferL = new int[bufferFrameLength]; 596 int[] mixedFrameBufferR = new int[bufferFrameLength]; 597 598 // Helpers declared outside loop for mz efficiency 599 int msbL, lsbL; 600 int msbR, lsbR; 601 int sampleL; 602 int sampleR; 603 int totalFramesMixed; 604 int trackCount; // tracks to play at a given pass 605 boolean isMoreQueued; // True if there are more tracks queued. 606 int frameIndex; 607 int i; 608 609 // Begin writing to the source data line 610 if (srcDataLine.isOpen()) 611 srcDataLine.start(); 612 else return; 613 614 // keep playing as long as line is open (and there is something to play) 615 try 616 { 617 while (srcDataLine.isOpen()) { // The audio mixing pipline 618 619 // First decide on which tracks to play ... and remove any finished tracks. 620 synchronized(sequenceGraph) { 621 622 // If there are no more tracks queued for playing, then exit the 623 // playback thread. 624 if (sequenceGraph.isEmpty()) 625 return; 626 627 isMoreQueued = false; 628 completedTracks.clear(); 629 tracksToPlay.clear(); 630 631 for (TrackSequence ts : sequenceGraph) { 632 633 // Has this track sequence finished? 634 if (ts.currentFrame > ts.endFrame || ts.stopPending) 635 completedTracks.add(ts); 636 637 // Is this track playing / is meant to start laying in this pass? 638 else if (ts.initiationFrame <= (timelineFrame + bufferFrameLength)) 639 tracksToPlay.add(ts); 640 641 // If it is not time to play the track yet, then 642 // neither will it for all proceeding tracks either 643 // since they are ordered by their initiation time. 644 else break; 645 646 } 647 648 // Get rid of tracks that have finished playing. Notify models that they have stopped 649 for (TrackSequence staleTS : completedTracks) { 650 651 sequenceGraph.remove(staleTS); 652 653 staleTS.onStopped((staleTS.currentFrame > staleTS.endFrame) 654 ? staleTS.endFrame : staleTS.currentFrame); 655 656 //removeTrackFromGraph(staleTS, staleTS.endFrame); 657 } 658 659 trackCount = tracksToPlay.size(); 660 isMoreQueued = sequenceGraph.size() > trackCount; 661 662 // If there is nothing queued and there are no tracks to play, 663 // then playback is finished. 664 if (!isMoreQueued && trackCount == 0) 665 return; 666 667 } // release lock 668 669 totalFramesMixed = 0; // this will be set to the maximum amount of frames that were mixed accross all tracks 670 671 // Clear audio buffer 672 for (i = 0; i < bufferFrameLength; i++) { 673 // TODO: Efficient way of clearing buffer? 674 mixedFrameBufferL[i] = 0; 675 mixedFrameBufferR[i] = 0; 676 } 677 678 // Perform Mixing : 679 // Convert the sample size to 16-bit always for best precision while 680 // processing audio in the mix pipeline.... 681 for (TrackSequence ts : tracksToPlay) { 682 683 // Notify model that initiated 684 if (!ts.isPlaying()) ts.onInitiated(timelineFrame); 685 686 // Skip muted / unsoloed tracks - they add nothing to the sample mix 687 if (ts.isMuted || (isSoloEnable && !ts.isSolo)) { 688 689 // Make sure start where initiated, if not already initiated 690 if (ts.initiationFrame >= timelineFrame && ts.initiationFrame < (timelineFrame + bufferFrameLength)) { 691 692 // Get index in frame buffer where to initiate 693 frameIndex = (int)(ts.initiationFrame - timelineFrame); 694 695 // Calcuate the length of frames to buffer - adjust silent tracks position 696 ts.currentFrame += (bufferFrameLength - frameIndex); 697 698 } else { // skip full buffer of bytes ... silenced 699 700 ts.currentFrame += bufferFrameLength; // currentFrame can go outside endframe boundry of the track 701 702 } 703 704 totalFramesMixed = bufferFrameLength; 705 706 } else { // Get samples and add to mix 707 708 // If the track is yet to initiate - part way through the buffer, then start adding bytes 709 // at initiation point 710 if (ts.initiationFrame >= timelineFrame && ts.initiationFrame < (timelineFrame + bufferFrameLength)) { 711 712 frameIndex = (int)(ts.initiationFrame - timelineFrame); 713 714 } else { 715 716 frameIndex = 0; 717 718 } 719 720 // For each frame 721 for (;frameIndex < bufferFrameLength && ts.currentFrame <= ts.endFrame; frameIndex++) { 722 723 // Get sample according to byte order 724 int base_posL = ts.currentFrame * (2*2); // 2=stereo, 2=16-bit 725 int base_posR = base_posL+2; 726 727 if (ts.isBigEndian) { 728 729 // First byte is MSB (high order) 730 msbL = (int)ts.playbackAudioBytes[base_posL]; 731 732 // Second byte is LSB (low order) 733 lsbL = (int)ts.playbackAudioBytes[base_posL + 1]; 734 735 // And again for the right channel 736 msbR= (int)ts.playbackAudioBytes[base_posR]; 737 lsbR = (int)ts.playbackAudioBytes[base_posR + 1]; 738 739 } else { 740 741 // First byte is LSB (low order) 742 lsbL = (int)ts.playbackAudioBytes[base_posL]; 743 744 // Second byte is MSB (high order) 745 msbL = (int)ts.playbackAudioBytes[base_posL+1]; 746 747 // And again for the right channel 748 lsbR = (int)ts.playbackAudioBytes[base_posR]; 749 msbR = (int)ts.playbackAudioBytes[base_posR+1]; 750 } 751 752 sampleL = (msbL << 0x8) | (0xFF & lsbL); 753 sampleR = (msbR << 0x8) | (0xFF & lsbR); 754 755 // Apply track volume 756 sampleL = (int)(sampleL * ts.volume); 757 sampleR = (int)(sampleR * ts.volume); 758 759 // Add to current mix 760 mixedFrameBufferL[frameIndex] += sampleL; 761 mixedFrameBufferR[frameIndex] += sampleR; 762 763 // Get next sample 764 ts.currentFrame++; 765 } 766 767 768 // Keep track of total frames mixed in buffer 769 if (frameIndex > totalFramesMixed) 770 totalFramesMixed = frameIndex; 771 } 772 773 } // Mix in next track 774 775 // totalFramesMixed is the amount of frames to play. 776 // If it is zero then it means that there are tracks yet to be initiated, and nothing currently playing 777 assert (totalFramesMixed <= bufferFrameLength); 778 assert (totalFramesMixed > 0 || 779 (totalFramesMixed == 0 && trackCount == 0 && isMoreQueued)); 780 781 // Post mix with master settings 782 if (isMasterMuteOn) { // Silence sample buffer if master mute is on 783 784 for (i = 0; i < sampleBuffer.length; i++) { 785 sampleBuffer[i] = 0; 786 } 787 788 // Let the muted bytes play 789 totalFramesMixed = bufferFrameLength; 790 791 } else { // otherwise apply master volume 792 793 for (i = 0; i < totalFramesMixed; i++) { 794 795 // Average tracks 796 //mixedFrameBuffer[i] /= trackCount; // depreciated 797 798 // Apply master volume 799 mixedFrameBufferL[i] = (int)(mixedFrameBufferL[i] * masterVolume); 800 mixedFrameBufferR[i] = (int)(mixedFrameBufferR[i] * masterVolume); 801 802 // Clip 803 if (mixedFrameBufferL[i] > Short.MAX_VALUE) mixedFrameBufferL[i] = Short.MAX_VALUE; 804 else if (mixedFrameBufferL[i] < Short.MIN_VALUE) mixedFrameBufferL[i] = Short.MIN_VALUE; 805 806 if (mixedFrameBufferR[i] > Short.MAX_VALUE) mixedFrameBufferR[i] = Short.MAX_VALUE; 807 else if (mixedFrameBufferR[i] < Short.MIN_VALUE) mixedFrameBufferR[i] = Short.MIN_VALUE; 808 809 // Convert to output format 810 lsbL = (mixedFrameBufferL[i] & 0xFF); 811 msbL = ((mixedFrameBufferL[i] >> 8) & 0xFF); 812 lsbR = (mixedFrameBufferR[i] & 0xFF); 813 msbR = ((mixedFrameBufferR[i] >> 8) & 0xFF); 814 815 int base_posL = i * (2 * 2); // 2=stereo, 2=16-bits 816 int base_posR = base_posL + 2; 817 if (isOutputBigEndian) { 818 sampleBuffer[base_posL] = (byte)msbL; 819 sampleBuffer[base_posL+1] = (byte)lsbL; 820 sampleBuffer[base_posR] = (byte)msbR; 821 sampleBuffer[base_posR+1] = (byte)lsbR; 822 } else { 823 sampleBuffer[base_posL] = (byte)lsbL; 824 sampleBuffer[base_posL+1] = (byte)msbL; 825 sampleBuffer[base_posR] = (byte)lsbR; 826 sampleBuffer[base_posR+1] = (byte)msbR; 827 } 828 829 } 830 831 } 832 833 // Generate silence only if there are more tracks to be played. 834 // Note that this could be false, but a track might have been queued after 835 // setting the isMoreQueued flag. In such cases... silence is not wanted anyway! 836 if (isMoreQueued) { 837 for (i = totalFramesMixed; i < bufferFrameLength; i++) { // will skip if no need to generate silence 838 int base_posL = i * (2 * 2); // 2=stereo, 2=16-bits 839 int base_posR = base_posL + 2; 840 841 sampleBuffer[base_posL] = 0; 842 sampleBuffer[base_posL+1] = 0; 843 sampleBuffer[base_posR] = 0; 844 sampleBuffer[base_posR+1] = 0; 845 } 846 // Ensure that full buffer is played ... including the silence 847 totalFramesMixed = bufferFrameLength; 848 } 849 850 // Write processed bytes to line out stream and update the timeline frame 851 srcDataLine.write( 852 sampleBuffer, 853 0, 854 totalFramesMixed * (2 * 2)); // 2=stereo, 2=16-bits 855 856 // Update timeline counter for sequencing management 857 timelineFrame += totalFramesMixed; 858 859 // The timelineFrame should always be larger or equal to the live frame position 860 assert(timelineFrame >= srcDataLine.getLongFramePosition()); 861 862 } // Next pass 863 864 } finally { 865 866 isStopping = true; 867 868 // Ensure line freed 869 if (srcDataLine.isOpen()) { 870 srcDataLine.drain(); // avoids chopping off last buffered chunk 871 srcDataLine.close(); 872 } 873 874 // Clear sequence graph. 875 synchronized(sequenceGraph) { 876 877 for (TrackSequence track : sequenceGraph) { 878 879 track.onStopped((track.currentFrame > track.endFrame) 880 ? track.endFrame : track.currentFrame); 881 } 882 883 sequenceGraph.clear(); 884 885 } 886 887 // Notify observers that playback has finished. 888 ApolloPlaybackMixer.this.fireSubjectChangedLaterOnSwingThread( 889 new SubjectChangedEvent(ApolloSubjectChangedEvent.PLAYBACK_STOPPED)); 890 891 } 892 893 } 894 } 895 896 private class MonoPlaybackThread extends PlaybackThread { 897 898 899 900 /** 901 * Initantly prepares for audio playback: Opens the (stereo) source data line for output 902 * 903 * @throws LineUnavailableException 904 */ 905 MonoPlaybackThread() throws LineUnavailableException { 906 super("Apollo Mono Playback Mixer Thread"); 907 908 } 909 546 910 /** 547 911 * The audio mixing pipeline … … 637 1001 638 1002 // Clear audio buffer 639 for (i = 0; i < bufferFrameLength; i++) // TODO: Eff ecient way of clearing buffer?1003 for (i = 0; i < bufferFrameLength; i++) // TODO: Efficient way of clearing buffer? 640 1004 mixedFrameBuffer[i] = 0; 641 1005 … … 746 1110 //mixedFrameBuffer[i] /= trackCount; // depreciated 747 1111 748 // Apply mast ar volume1112 // Apply master volume 749 1113 mixedFrameBuffer[i] = (int)(mixedFrameBuffer[i] * masterVolume); 750 1114 … … 781 1145 } 782 1146 783 // Write proc cessed bytes to line out stream and update the timeline frame1147 // Write processed bytes to line out stream and update the timeline frame 784 1148 srcDataLine.write( 785 1149 sampleBuffer, … … 826 1190 } 827 1191 828 829 830 } 1192 } 831 1193 832 1194
Note:
See TracChangeset
for help on using the changeset viewer.