5 #include "library/hex.hpp"
6 #include "library/minmax.hpp"
7 #include "library/ogg.hpp"
8 #include "library/opus-ogg.hpp"
9 #include "library/string.hpp"
10 #include "core/messages.hpp"
22 const uint64_t past_end
= 0xFFFFFFFFFFFFFFFFULL
;
24 //These values are taken from libopusfile.
25 const downmix_pair downmix
[] = {
28 {9598,0},{6786,6786},{0,9598},
29 {6924,0},{0,6924}, {5996,3464}, {3464,5996},
30 {10666,0},{7537,7537},{0,10666},{9234,5331},{5331,9234},
31 {8668,0},{6129,6129},{0,8668},{7507,4335},{4335,7507},{6129,6129},
32 {7459,0},{5275,5275},{0,7459},{6460,3731},{3731,6460},{4568,4568},{5275,5275},
33 {6368,0},{4502,4502},{0,6368},{5515,3183},{3183,5515},{5515,3183},{3183,5515},{4502,4502}
36 const size_t downmix_idx
[9] = {0, 0, 1, 3, 6, 10, 15, 21, 28};
38 uint32_t pick_subsong(random
& rng
, const std::set
<uint32_t>& candidates
)
40 if(candidates
.empty())
42 if(candidates
.size() == 1)
43 return *candidates
.begin();
44 uint32_t r
= rng
.pull();
45 r
%= candidates
.size();
46 for(auto i
: candidates
)
52 void fill_msc_downmix_family0(struct multistream_characteristics
& c
, unsigned chan
)
54 if(chan
< 1 || chan
> 2)
55 (stringfmt() << "Illegal channel count " << chan
<< " for map family 0").throwex();
56 for(unsigned i
= 0; i
< chan
; i
++) {
57 c
.downmix_l
[i
] = downmix
[i
+ downmix_idx
[chan
]].l
;
58 c
.downmix_r
[i
] = downmix
[i
+ downmix_idx
[chan
]].r
;
62 void fill_msc_downmix_family1(struct multistream_characteristics
& c
, unsigned chan
)
64 if(chan
< 1 || chan
> 8)
65 (stringfmt() << "Illegal channel count " << chan
<< " for map family 1").throwex();
66 for(unsigned i
= 0; i
< chan
; i
++) {
67 c
.downmix_l
[i
] = downmix
[i
+ downmix_idx
[chan
]].l
;
68 c
.downmix_r
[i
] = downmix
[i
+ downmix_idx
[chan
]].r
;
72 void fill_msc_from_header(struct multistream_characteristics
& c
, const opus::ogg_header
& h
)
74 c
.channels
= h
.channels
;
76 c
.streams
= h
.streams
;
77 c
.coupled
= h
.coupled
;
78 memcpy(c
.mapping
, h
.chanmap
, 255);
80 fill_msc_downmix_family0(c
, c
.channels
);
81 else if(h
.map_family
== 1)
82 fill_msc_downmix_family1(c
, c
.channels
);
84 (stringfmt() << "Unsupported map family " << (int)h
.map_family
).throwex();
87 multistream_characteristics::multistream_characteristics()
94 fill_msc_downmix_family0(*this, 1);
97 subsong_transition::subsong_transition()
100 xfade_pts
= past_end
;
104 void subsong_transition::fixup(uint64_t pregap
, uint64_t datalen
, const std::string
& ssname
)
106 if(start_pts
== past_end
)
108 if(end_pts
== past_end
)
110 if(xfade_pts
== past_end
)
112 if(end_pts
< start_pts
|| end_pts
> datalen
) {
113 messages
<< "Warning: [" << ssname
<< "] Ending PTS out of range, clipped to the end."
117 if(xfade_pts
< start_pts
|| xfade_pts
> end_pts
) {
118 messages
<< "Warning: [" << ssname
<< "] Fade PTS out of range, clipped to the end."
122 if(start_pts
>= end_pts
) {
123 messages
<< "Warning: [" << ssname
<< "] Start PTS out of range, clipped to the start."
129 uint32_t song_buffer::register_lsid(const std::string
& ssid
)
131 if(ssid_to_lsid
.count(ssid
))
132 return ssid_to_lsid
[ssid
];
133 auto dummy
= stransitions
[next_lsid
];
134 ssid_to_lsid
[ssid
] = next_lsid
++;
135 return ssid_to_lsid
[ssid
];
138 uint32_t song_buffer::register_lsid(const std::string
& ssid
, uint32_t psid
)
140 if(ssid_to_lsid
.count(ssid
)) {
141 lsid_to_psid
[ssid_to_lsid
[ssid
]] = psid
;
142 return ssid_to_lsid
[ssid
];
144 auto dummy
= stransitions
[next_lsid
];
145 lsid_to_psid
[next_lsid
] = psid
;
146 ssid_to_lsid
[ssid
] = next_lsid
++;
147 return ssid_to_lsid
[ssid
];
150 void song_buffer::delete_undefined_substreams()
152 bool deleted
= false;
155 for(auto& i
: ssid_to_lsid
) {
156 uint32_t lsid
= i
.second
;
157 if(!lsid_to_psid
.count(lsid
)) {
158 //This needs to be deleted.
159 stransitions
.erase(lsid
);
160 ssid_to_lsid
.erase(i
.first
);
168 void song_buffer::delete_stream(uint32_t psid
)
170 bool deleted
= false;
173 for(auto& i
: ssid_to_lsid
) {
174 uint32_t lsid
= i
.second
;
175 if(lsid_to_psid
.count(lsid
) && lsid_to_psid
[lsid
] == psid
) {
176 //This needs to be deleted.
177 stransitions
.erase(lsid
);
178 lsid_to_psid
.erase(lsid
);
179 ssid_to_lsid
.erase(i
.first
);
185 mscharacteristics
.erase(psid
);
186 auto key1
= std::make_pair(psid
, 0ULL);
187 auto key2
= std::make_pair(psid
+ 1, 0ULL);
188 std::map
<std::pair
<uint32_t, uint64_t>, std::vector
<uint8_t>>::iterator itr1
;
189 while((itr1
= packetdata
.lower_bound(key1
)) != packetdata
.lower_bound(key2
))
190 packetdata
.erase(itr1
);
193 std::string
song_buffer::reverse_lsid(uint32_t lsid
)
195 for(auto& i
: ssid_to_lsid
)
201 const std::vector
<uint8_t>& song_buffer::get_packet(uint32_t subsong
, uint64_t pts
)
203 if(!lsid_to_psid
.count(subsong
))
205 std::pair
<uint32_t, uint64_t> ptsx
= std::make_pair(lsid_to_psid
[subsong
], pts
);
206 if(!packetdata
.count(ptsx
))
208 return packetdata
[ptsx
];
211 uint64_t song_buffer::next_timecode(uint32_t subsong
, uint64_t pts
)
213 if(!lsid_to_psid
.count(subsong
))
215 std::pair
<uint32_t, uint64_t> ptsx
= std::make_pair(lsid_to_psid
[subsong
], pts
);
216 if(ptsx
.second
== past_end
|| packetdata
.empty())
218 auto i
= packetdata
.lower_bound(ptsx
);
219 if(i
== packetdata
.end())
222 uint32_t psid
= i
->first
.first
;
223 if(psid
== lsid_to_psid
[subsong
])
224 return i
->first
.second
;
230 uint64_t song_buffer::prev_timecode(uint32_t subsong
, uint64_t pts
)
232 if(!lsid_to_psid
.count(subsong
))
234 std::pair
<uint32_t, uint64_t> ptsx
= std::make_pair(lsid_to_psid
[subsong
], pts
);
235 if(ptsx
.second
== past_end
|| packetdata
.empty())
237 auto i
= packetdata
.upper_bound(ptsx
);
238 if(i
== packetdata
.end()) {
239 uint32_t psid
= packetdata
.rbegin()->first
.first
;
240 if(psid
== lsid_to_psid
[subsong
])
241 return packetdata
.rbegin()->first
.second
;
246 uint32_t psid
= i
->first
.first
;
247 if(psid
== lsid_to_psid
[subsong
])
248 return i
->first
.second
;
254 song_buffer::song_buffer(std::istream
& stream
)
257 ogg::stream_reader_iostreams
r(stream
);
258 r
.set_errors_to(messages
);
260 std::map
<uint32_t, subsong_context
> psids
;
261 uint32_t next_psid
= 0;
263 if(page_starts_new_stream(p
)) {
264 uint32_t psid
= next_psid
++;
265 psids
[psid
].psid
= psid
;
266 psids
[psid
].oggid
= p
.get_stream();
267 parse_ogg_page(p
, psids
[psid
]);
270 if(parse_ogg_page(p
, i
.second
) && p
.get_eos()) {
271 if(i
.second
.pts
<= i
.second
.pregap
) {
272 //Invalid or blank stream.
273 messages
<< "Warning: " << p
.stream_debug_id() << " has "
274 << "length <= 0. Ignoring stream." << std::endl
;
275 psids
.erase(i
.first
);
276 //Also erase all associated lsids.
277 delete_stream(i
.first
);
280 i
.second
.eos_seen
= true;
283 if(!i
.second
.eos_seen
)
284 messages
<< "Warning: No EOS on stream " << hex::to(i
.second
.oggid
, true)
286 delete_undefined_substreams();
287 if(ssid_to_lsid
.empty())
288 throw std::runtime_error("No valid Oggopus streams found");
289 for(auto& i
: ssid_to_lsid
) {
290 uint32_t psid
= lsid_to_psid
[i
.second
];
291 uint32_t next_psid
= 0;
292 uint32_t default_lsid
;
293 //Look up known psids.
294 if(mscharacteristics
.upper_bound(psid
) != mscharacteristics
.end())
295 next_psid
= mscharacteristics
.upper_bound(psid
)->first
;
297 next_psid
= mscharacteristics
.begin()->first
;
298 default_lsid
= ssid_to_lsid
[(stringfmt() << "PSID" << next_psid
).str()];
299 bool deleted
= false;
302 for(auto& j
: stransitions
[i
.second
].next_subsongs
)
303 if(!lsid_to_psid
.count(j
)) {
304 messages
<< "Warning: Undefined reference from '" << i
.first
305 << "' to '" << reverse_lsid(j
) << "'" << std::endl
;
306 stransitions
[i
.second
].next_subsongs
.erase(j
);
311 if(stransitions
[i
.second
].next_subsongs
.empty())
312 stransitions
[i
.second
].next_subsongs
.insert(default_lsid
);
313 stransitions
[i
.second
].fixup(psids
[psid
].pregap
, psids
[psid
].pts
, i
.first
);
316 for(auto& i
: mscharacteristics
) {
317 entry
.insert(ssid_to_lsid
[(stringfmt() << "PSID" << i
.first
).str()]);
321 for(auto& i : ssid_to_lsid) {
322 std::cerr << "SSID: " << i.first << " (#" << i.second << ") Start: "
323 << stransitions[i.second].start_pts / 48 << "ms Fade:"
324 << stransitions[i.second].xfade_pts / 48 << "ms End:"
325 << stransitions[i.second].end_pts / 48 << "ms"
326 << (entry.count(i.second) ? " ENTRYPOINT" : "") << std::endl;
327 std::cerr << "\tNext up\t";
329 for(auto& j : stransitions[i.second].next_subsongs) {
330 std::cerr << (f ? "" : ",") << reverse_lsid(j);
333 uint32_t psid = lsid_to_psid[i.second];
334 std::cerr << std::endl << "\tPhysical stream (PSID#" << psid << ", Ogg stream " <<
335 hex::to(psids[psid].oggid) << "):" << std::endl;
336 auto c = mscharacteristics[psid];
337 std::cerr << "\t\t" << (int)c.channels << " channels (" << (int)c.streams << " streams, "
338 << (int)c.coupled << " coupled) @" << (c.gain / 256.0) << "dB" << std::endl;
339 std::cerr << "\t\tPregap " << psids[psid].pregap / 48 << "ms, length " << psids[psid].pts / 48
340 << "ms." << std::endl;
345 song_buffer::subsong_context::subsong_context()
356 void song_buffer::fill_ms_characteristics(uint32_t subsong
, struct multistream_characteristics
& c
)
358 if(!lsid_to_psid
.count(subsong
)) {
359 c
= multistream_characteristics();
362 subsong
= lsid_to_psid
[subsong
];
363 if(mscharacteristics
.count(subsong
))
364 c
= mscharacteristics
[subsong
];
366 c
= multistream_characteristics();
369 const struct subsong_transition
& song_buffer::transitions(uint32_t subsong
)
371 if(stransitions
.count(subsong
))
372 return stransitions
[subsong
];
374 return dummy_transition
;
377 bool song_buffer::page_starts_new_stream(ogg::page
& p
)
379 if(!p
.get_bos() || p
.get_packet_count() != 1)
381 auto pkt
= p
.get_packet(0);
382 if(pkt
.second
< 8 || memcmp(pkt
.first
, "OpusHead", 8))
387 bool song_buffer::parse_ogg_page(ogg::page
& page
, subsong_context
& ctx
)
390 if(!ctx
.demux
.page_in(page
))
392 while(ctx
.demux
.wants_packet_out()) {
393 ctx
.demux
.packet_out(p
);
395 parse_ogg_header(p
, ctx
);
397 } else if(ctx
.pages
== 1) {
398 parse_ogg_tags(p
, ctx
, page
);
401 parse_ogg_data(p
, ctx
, page
);
408 void song_buffer::parse_ogg_header(ogg::packet
& p
, subsong_context
& ctx
)
410 struct opus::ogg_header h
;
412 fill_msc_from_header(mscharacteristics
[ctx
.psid
], h
);
413 ctx
.pregap
= h
.preskip
;
417 void song_buffer::parse_ogg_tags(ogg::packet
& p
, subsong_context
& ctx
, const ogg::page
& debug
)
419 struct opus::ogg_tags t
;
421 for(auto& i
: t
.comments
) {
423 regex_results r
= regex("SKY-([^-]+)-([^=]+)=(.*)", i
);
426 uint32_t lsid
= register_lsid(r
[2], ctx
.psid
);
428 stransitions
[lsid
].start_pts
= parse_value
<uint64_t>(r
[3]) + ctx
.pregap
;
430 stransitions
[lsid
].xfade_pts
= parse_value
<uint64_t>(r
[3]) + ctx
.pregap
;
432 stransitions
[lsid
].end_pts
= parse_value
<uint64_t>(r
[3]) + ctx
.pregap
;
434 std::string next
= r
[3];
435 for(auto& token
: token_iterator
<char>::foreach(next
, {","})) {
436 uint32_t lsid2
= register_lsid(token
);
437 stransitions
[lsid
].next_subsongs
.insert(lsid2
);
440 if(r
[1] == "ENTRY") {
443 } catch(std::exception
& e
) {
444 messages
<< "Warning: " << debug
.stream_debug_id() << " tag '" << i
<< "': "
445 << e
.what() << std::endl
;
448 //Make sure substream 0 exits.
449 register_lsid((stringfmt() << "PSID" << ctx
.psid
).str(), ctx
.psid
);
452 void song_buffer::parse_ogg_data(ogg::packet
& p
, subsong_context
& ctx
, const ogg::page
& debug
)
454 std::pair
<uint32_t, uint64_t> ptsx
= std::make_pair(ctx
.psid
, ctx
.pts
);
455 packetdata
[ptsx
] = p
.get_vector();
456 uint8_t t
= opus::packet_tick_count(&packetdata
[ptsx
][0], packetdata
[ptsx
].size());
458 if(p
.get_last_page()) {
459 uint64_t samples
= p
.get_granulepos() - ctx
.last_granule
;
460 if(samples
> ctx
.pts
- ctx
.last_pts
) {
461 //If there is only one data page, it is assumed to have zero base granulepos.
462 //But for multiple pages, the first granulepos is arbitrary.
463 if(ctx
.pages
> 2 || p
.get_on_eos_page())
464 messages
<< "Warning: " << debug
.page_debug_id() << " Granulepos says there "
465 << "are " << samples
<< " samples, found " << ctx
.pts
- ctx
.last_pts
467 } else if(p
.get_on_eos_page())
469 ctx
.pts
= ctx
.last_pts
+ samples
;
470 else if(samples
< ctx
.pts
- ctx
.last_pts
)
471 messages
<< "Warning: " << debug
.page_debug_id() << " Granulepos says there are "
472 << samples
<< " samples, found " << ctx
.pts
- ctx
.last_pts
474 ctx
.last_pts
= ctx
.pts
;
475 ctx
.last_granule
= p
.get_granulepos();
479 packet_decoder::packet_decoder()
485 void packet_decoder::set_multistream(const struct multistream_characteristics
& c
)
488 size_t msstate_size
= opus::multistream_decoder::size(c
.streams
, c
.coupled
);
489 msstate_size
+= sizeof(opus::multistream_decoder
) + alignof(opus::multistream_decoder
);
490 size_t dmix_offset
= msstate_size
;
491 msstate_size
+= 5760 * c
.channels
* sizeof(float);
492 if(memory
.size() < msstate_size
)
493 memory
.resize(msstate_size
);
494 uint8_t* a
= &memory
[0];
495 if(reinterpret_cast<uint64_t>(a
) % alignof(opus::multistream_decoder
))
497 uint8_t* b
= a
+ sizeof(opus::multistream_decoder
);
498 d
= new(a
) opus::multistream_decoder(opus::samplerate::r48k
, c
.channels
, c
.streams
, c
.coupled
,
499 c
.mapping
, reinterpret_cast<char*>(b
));
500 dmem
= reinterpret_cast<float*>(a
+ dmix_offset
);
501 channels
= c
.channels
;
502 float gain_factor
= 2 * pow(10, c
.gain
/ 5120.0);
503 for(unsigned i
= 0; i
< channels
; i
++) {
504 downmix_l
[i
] = gain_factor
* c
.downmix_l
[i
];
505 downmix_r
[i
] = gain_factor
* c
.downmix_r
[i
];
507 } catch(opus::not_loaded l
) {
509 channels
= c
.channels
;
513 void packet_decoder::decode_packet(const std::vector
<uint8_t>& data
)
518 s
= d
->decode(&data
[0], data
.size(), dmem
, 5760);
521 uint8_t ticks
= opus::packet_tick_count(&data
[0], data
.size());
523 ticks
= 1; //Try to recover.
524 memset(pcmbuf
, 0, 240 * ticks
* sizeof(int16_t));
526 pcmlen
= 120 * ticks
;
529 } catch(std::exception
& e
) {
530 //Try to insert silence.
531 messages
<< "Failed to decode opus packet: " << e
.what() << std::endl
;
532 uint8_t ticks
= opus::packet_tick_count(&data
[0], data
.size());
534 ticks
= 1; //Try to recover.
535 memset(pcmbuf
, 0, 240 * ticks
* sizeof(int16_t));
537 pcmlen
= 120 * ticks
;
540 for(unsigned i
= 0; i
< s
; i
++) {
543 for(unsigned j
= 0; j
< channels
; j
++) {
544 a
+= downmix_l
[j
] * dmem
[i
* channels
+ j
];
545 b
+= downmix_r
[j
] * dmem
[i
* channels
+ j
];
547 pcmbuf
[2 * i
+ 0] = min(max((int32_t)a
, -32768), 32767);
548 pcmbuf
[2 * i
+ 1] = min(max((int32_t)b
, -32768), 32767);
554 void packet_decoder::reset()
559 } catch(opus::not_loaded e
) {
563 music_player::music_player(struct music_player_memory
& m
, random
& _rng
)
569 void music_player::seek_channel(packet_decoder
& i
, uint64_t& spts
, uint32_t subsong
, uint64_t pts
)
571 if(pts
== past_end
) {
572 i
.pcmpos
= i
.pcmlen
= 0;
577 uint64_t ptsr
= song
->prev_timecode(subsong
, (pts
>= 3840) ? (pts
- 3840) : 0);
579 ptsr
= song
->next_timecode(subsong
, ptsr
);
582 i
.decode_packet(song
->get_packet(subsong
, ptsr
));
583 if(ptsr
+ i
.pcmlen
> pts
) {
584 i
.pcmpos
= pts
- ptsr
;
592 void music_player::song_to_beginning()
594 mem
.pcmpos2
= past_end
;
598 mem
.pcmpos1
= past_end
;
601 const std::set
<uint32_t>& songs
= song
->entrypoints();
602 mem
.subsong1
= pick_subsong(rng
, songs
);
603 mem
.pcmpos1
= song
->transitions(mem
.subsong1
).start_pts
;
606 void music_player::do_preroll()
608 multistream_characteristics c1
, c2
;
610 song
->fill_ms_characteristics(mem
.subsong1
, c1
);
611 song
->fill_ms_characteristics(mem
.subsong2
, c2
);
613 i1
.set_multistream(c1
);
614 i2
.set_multistream(c2
);
619 seek_channel(i1
, pts1p
, mem
.subsong1
, mem
.pcmpos1
);
620 seek_channel(i2
, pts2p
, mem
.subsong2
, mem
.pcmpos2
);
623 void music_player::decode(std::pair
<int16_t, int16_t>* output
, size_t samples
)
626 memset(output
, 0, samples
* sizeof(std::pair
<int16_t, int16_t>));
629 const subsong_transition
* strans1
= &song
->transitions(mem
.subsong1
);
630 const subsong_transition
* strans2
= &song
->transitions(mem
.subsong2
);
631 for(; samples
> 0; output
++, samples
--) {
632 if(strans1
->xfade_pts
== mem
.pcmpos1
) {
633 mem
.subsong2
= pick_subsong(rng
, strans1
->next_subsongs
);
634 const subsong_transition
& st
= song
->transitions(mem
.subsong2
);
635 seek_channel(i2
, mem
.pcmpos2
, mem
.subsong2
, st
.start_pts
);
636 strans2
= &song
->transitions(mem
.subsong2
);
638 if(strans2
->xfade_pts
== mem
.pcmpos2
) {
639 mem
.subsong1
= pick_subsong(rng
, strans2
->next_subsongs
);
640 const subsong_transition
& st
= song
->transitions(mem
.subsong1
);
641 seek_channel(i1
, mem
.pcmpos1
, mem
.subsong1
, st
.start_pts
);
642 strans1
= &song
->transitions(mem
.subsong1
);
644 if(strans1
->end_pts
== mem
.pcmpos1
)
645 seek_channel(i1
, mem
.pcmpos1
, mem
.subsong1
, past_end
);
646 if(strans2
->end_pts
== mem
.pcmpos2
)
647 seek_channel(i2
, mem
.pcmpos2
, mem
.subsong2
, past_end
);
648 if(i1
.pcmpos
== i1
.pcmlen
&& mem
.pcmpos1
!= past_end
) {
649 uint64_t pts
= song
->next_timecode(mem
.subsong1
, mem
.pcmpos1
);
651 i1
.decode_packet(song
->get_packet(mem
.subsong1
, mem
.pcmpos1
));
653 if(i2
.pcmpos
== i2
.pcmlen
&& mem
.pcmpos2
!= past_end
) {
654 uint64_t pts
= song
->next_timecode(mem
.subsong2
, mem
.pcmpos2
);
656 i2
.decode_packet(song
->get_packet(mem
.subsong2
, mem
.pcmpos2
));
658 int32_t cf
= 0, icf
= 0;
659 if(mem
.pcmpos1
>= strans1
->end_pts
)
661 else if(mem
.pcmpos2
>= strans2
->end_pts
)
663 else if(strans1
->xfade_pts
< mem
.pcmpos1
) {
664 uint64_t cfstart
= strans1
->xfade_pts
;
665 uint64_t cflen
= strans1
->end_pts
- strans1
->xfade_pts
;
666 cf
= 256 - 256 * (mem
.pcmpos1
- cfstart
) / cflen
;
668 } else if(strans2
->xfade_pts
< mem
.pcmpos2
) {
669 uint64_t cfstart
= strans2
->xfade_pts
;
670 uint64_t cflen
= strans2
->end_pts
- strans2
->xfade_pts
;
671 icf
= 256 - 256 * (mem
.pcmpos2
- cfstart
) / cflen
;
674 int32_t l
= (cf
* i1
.pcmbuf
[2 * i1
.pcmpos
+ 0] + icf
* i2
.pcmbuf
[2 * i2
.pcmpos
+ 0]) >> 8;
675 int32_t r
= (cf
* i1
.pcmbuf
[2 * i1
.pcmpos
+ 1] + icf
* i2
.pcmbuf
[2 * i2
.pcmpos
+ 1]) >> 8;
676 output
->first
= max(min(l
, 32767), -32768);
677 output
->second
= max(min(r
, 32767), -32768);
678 if(i1
.pcmpos
< i1
.pcmlen
)
680 if(i2
.pcmpos
< i2
.pcmlen
)
682 if(mem
.pcmpos1
!= past_end
)
684 if(mem
.pcmpos2
!= past_end
)